/** * Copyright 2016 LinkedIn Corp. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ package com.github.ambry.store; import com.github.ambry.utils.TestUtils; import com.github.ambry.utils.Utils; import com.github.ambry.utils.UtilsTest; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.Arrays; import java.util.Comparator; import java.util.HashSet; import java.util.Set; import org.junit.Test; import static org.junit.Assert.*; /** * Tests the helper functions of {@link LogSegmentNameHelper} */ public class LogSegmentNameHelperTest { /** * Tests the comparator in {@link LogSegmentNameHelper} for correctness. */ @Test public void comparatorTest() { Comparator<String> comparator = LogSegmentNameHelper.COMPARATOR; // compare empty name with empty name assertEquals("Empty names should be equal", 0, comparator.compare("", "")); // create sample names String[] names = {LogSegmentNameHelper.getName(0, 0), LogSegmentNameHelper.getName(0, 1), LogSegmentNameHelper.getName(1, 0), LogSegmentNameHelper.getName(1, 1)}; for (int i = 0; i < names.length; i++) { for (int j = 0; j < names.length; j++) { int expectCompare = i == j ? 0 : i > j ? 1 : -1; assertEquals("Unexpected value on compare", expectCompare, comparator.compare(names[i], names[j])); assertEquals("Unexpected value on compare", -1 * expectCompare, comparator.compare(names[j], names[i])); } } // empty name cannot be compared with anything else String validName = LogSegmentNameHelper.getName(0, 0); try { comparator.compare(validName, ""); fail("Should not have been able to compare empty name with anything else"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } try { comparator.compare("", validName); fail("Should not have been able to compare empty name with anything else"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } } /** * Checks the file name filter in {@link LogSegmentNameHelper} for correctness by creating valid and invalid files * and checking that the invalid ones are filtered out and the valid ones correctly picked up. * @throws IOException */ @Test public void filenameFilterTest() throws IOException { int validFileCount = 10; int invalidFileCount = 5; Set<File> validFiles = new HashSet<>(validFileCount); File tempDir = Files.createTempDirectory("nameHelper-" + UtilsTest.getRandomString(10)).toFile(); tempDir.deleteOnExit(); try { String filename = LogSegmentNameHelper.nameToFilename(""); File file = createFile(tempDir, filename); validFiles.add(file); for (int i = 1; i < validFileCount; i++) { long pos = Utils.getRandomLong(TestUtils.RANDOM, 1000); long gen = Utils.getRandomLong(TestUtils.RANDOM, 1000); filename = LogSegmentNameHelper.nameToFilename(LogSegmentNameHelper.getName(pos, gen)); file = createFile(tempDir, filename); validFiles.add(file); } for (int i = 0; i < invalidFileCount; i++) { filename = UtilsTest.getRandomString(10); switch (i) { case 0: filename = filename + "_index"; break; case 1: filename = filename + LogSegmentNameHelper.SUFFIX + "_temp"; break; default: break; } createFile(tempDir, filename); } Set<File> filteredFiles = new HashSet<>(Arrays.asList(tempDir.listFiles(LogSegmentNameHelper.LOG_FILE_FILTER))); assertEquals("Filtered files do not have the valid files", validFiles, filteredFiles); } finally { File[] files = tempDir.listFiles(); if (files != null) { for (File file : files) { assertTrue("The file [" + file.getAbsolutePath() + "] could not be deleted", file.delete()); } } assertTrue("The directory [" + tempDir.getAbsolutePath() + "] could not be deleted", tempDir.delete()); } } /** * Tests correctness of {@link LogSegmentNameHelper#hashcode(String)} */ @Test public void hashCodeTest() { String name = UtilsTest.getRandomString(10); assertEquals("Hashcode is not as expected", name.hashCode(), LogSegmentNameHelper.hashcode(name)); } /** * Tests correctness of {@link LogSegmentNameHelper#getPosition(String)} and * {@link LogSegmentNameHelper#getGeneration(String)}. */ @Test public void getPositionAndGenerationTest() { for (int i = 0; i < 10; i++) { long pos = Utils.getRandomLong(TestUtils.RANDOM, 1000); long gen = Utils.getRandomLong(TestUtils.RANDOM, 1000); checkPosAndGeneration(LogSegmentNameHelper.getName(pos, gen), pos, gen); } try { LogSegmentNameHelper.getPosition(""); fail("Should have failed to get position for empty log segment name"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } try { LogSegmentNameHelper.getGeneration(""); fail("Should have failed to get generation for empty log segment name"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } } /** * Tests correctness of {@link LogSegmentNameHelper#getName(long, long)}. */ @Test public void getNameTest() { for (int i = 0; i < 10; i++) { long pos = Utils.getRandomLong(TestUtils.RANDOM, 1000); long gen = Utils.getRandomLong(TestUtils.RANDOM, 1000); assertEquals("Did not get expected name", pos + BlobStore.SEPARATOR + gen, LogSegmentNameHelper.getName(pos, gen)); } } /** * Tests correctness of {@link LogSegmentNameHelper#getNextPositionName(String)} and * {@link LogSegmentNameHelper#getNextGenerationName(String)}. */ @Test public void getNextPositionAndGenerationTest() { for (int i = 0; i < 10; i++) { long pos = Utils.getRandomLong(TestUtils.RANDOM, 1000); long gen = Utils.getRandomLong(TestUtils.RANDOM, 1000); String name = LogSegmentNameHelper.getName(pos, gen); checkPosAndGeneration(LogSegmentNameHelper.getNextPositionName(name), pos + 1, 0); checkPosAndGeneration(LogSegmentNameHelper.getNextGenerationName(name), pos, gen + 1); } try { LogSegmentNameHelper.getNextPositionName(""); fail("Should have failed to get next position for empty log segment name"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } try { LogSegmentNameHelper.getNextGenerationName(""); fail("Should have failed to get next generation for empty log segment name"); } catch (IllegalArgumentException e) { // expected. Nothing to do. } } /** * Tests correctness of {@link LogSegmentNameHelper#generateFirstSegmentName(boolean)} for different numbers of log * segments (including invalid ones). */ @Test public void generateFirstSegmentNameTest() { assertEquals("Did not get expected name", "", LogSegmentNameHelper.generateFirstSegmentName(false)); String firstSegmentName = LogSegmentNameHelper.getName(0, 0); assertEquals("Did not get expected name", firstSegmentName, LogSegmentNameHelper.generateFirstSegmentName(true)); } /** * Tests correctness of {@link LogSegmentNameHelper#nameFromFilename(String)}. */ @Test public void nameFromFilenameTest() { assertEquals("Did not get expected name", "", LogSegmentNameHelper.nameFromFilename("log_current")); String name = LogSegmentNameHelper.getName(0, 0); String filename = LogSegmentNameHelper.nameToFilename(name); assertEquals("Did not get expected name", name, LogSegmentNameHelper.nameFromFilename(filename)); // bad file names String badNameBase = UtilsTest.getRandomString(10); String[] badNames = {badNameBase, badNameBase + LogSegmentNameHelper.SUFFIX, name + BlobStore.SEPARATOR + "123" + LogSegmentNameHelper.SUFFIX}; for (String badName : badNames) { try { LogSegmentNameHelper.nameFromFilename(badName); fail("Should have failed to get name for filename [" + badName + "]"); } catch (IllegalArgumentException | StringIndexOutOfBoundsException e) { // expected. Nothing to do. } } } /** * Tests correctness of {@link LogSegmentNameHelper#nameToFilename(String)}. */ @Test public void nameToFilenameTest() { assertEquals("Did not get expected file name", "log_current", LogSegmentNameHelper.nameToFilename("")); String name = UtilsTest.getRandomString(10); assertEquals("Did not get expected file name", name + LogSegmentNameHelper.SUFFIX, LogSegmentNameHelper.nameToFilename(name)); } // helpers // general /** * Checks the position and generation obtained from {@code name} match {@code expectedPos} and {@code expectedGen} * respectively. * @param name the name whose position and generation needs to be checked. * @param expectedPos the expected return value from {@link LogSegmentNameHelper#getPosition(String)}. * @param expectedGen the expected return value from {@link LogSegmentNameHelper#getGeneration(String)}. */ private void checkPosAndGeneration(String name, long expectedPos, long expectedGen) { assertEquals("Did not get expected position", expectedPos, LogSegmentNameHelper.getPosition(name)); assertEquals("Did not get expected generation number", expectedGen, LogSegmentNameHelper.getGeneration(name)); } // filenameFilterTest() helpers /** * Creates a file in {@code parentDir} with name {@code filename} and configures it to be deleted on exit. * @param parentDir the directory where a file with {@code filename} needs to be created. * @param filename the name of the file to be created. * @return a reference to the created {@link File}. * @throws IOException */ private File createFile(File parentDir, String filename) throws IOException { File file = new File(parentDir, filename); if (!file.exists()) { assertTrue("File could not be created at path " + file.getAbsolutePath(), file.createNewFile()); } file.deleteOnExit(); return file; } }