package org.dcache.services.info.base; import org.junit.Test; import java.util.Arrays; import java.util.Set; import java.util.TreeSet; import static org.junit.Assert.*; public class StatePathTest extends InfoBaseTestHelper { // The number of elements in the array must be prime for stride to work private static final String PATH_ELEMENTS[] = {"foo", "bar", "baz"}; /** * Test that the String constructor works (or, at least, is * not obviously broken). * <p> * Assumes that hashCode() works. */ @Test public void testSimpleRepeatedHashCode() { Set<Integer> seenHashCodes = new TreeSet<>(); for( String element : PATH_ELEMENTS) { StatePath testPath = new StatePath( element); assertFalse( "hashCode for " + element + " repeated", hashAlreadySeen( seenHashCodes, testPath.hashCode())); // Make sure that non-intern Strings hash to same value. testPath = new StatePath( new String( element)); assertTrue( "hashCode for " + element + " not found again", hashAlreadySeen( seenHashCodes, testPath.hashCode())); } } /** * Test that all ordering of elements produces distinct hashes. */ @Test public void testOrderedElementHashCode() { Set<Integer> seenHashCodes = new TreeSet<>(); final int MAX_LEN = 5; for( int len = 2; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++ ) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { StatePath path = buildStatePath( len, offset, stride); assertFalse( "ordered elements test failed due to hashCode repeating for len="+len+", offset="+offset+", stride="+stride+" (" + path.toString()+")", hashAlreadySeen( seenHashCodes, path.hashCode())); } } } } /** * Test some potentially awkward corner cases */ @Test public void testAwkwardHashCode() { StatePath nullPath = new StatePath( (String)null); StatePath emptyPath = new StatePath( ""); StatePath spacePath = new StatePath( " "); assertNotSame( "null and empty paths hash to same value", nullPath.hashCode(), emptyPath.hashCode()); assertNotSame( "empty and single-space paths hash to same value", spacePath.hashCode(), emptyPath.hashCode()); assertNotSame( "null and single-space paths hash to same value", spacePath.hashCode(), nullPath.hashCode()); } /** * Check that parsing results in a StatePath that gives the same result * as building the StatePath manually. * * This assumes hashCode() and newChild() work. */ @Test public void testParsePath() { final int MAX_LEN = 5; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { String pathElement[] = buildPathArray( len, offset, stride); StatePath manuallyBuiltPath = buildStatePath( len, offset, stride); StringBuilder sb = new StringBuilder(); for( String element : pathElement) { if( sb.length() > 0) { sb.append("."); } sb.append( element); } // parse the string StatePath parsedPath = StatePath.parsePath( sb.toString()); assertTrue( "parsed path ("+parsedPath.toString()+") hashes differently to manually built path ("+ manuallyBuiltPath.toString() +")", parsedPath.hashCode() == manuallyBuiltPath.hashCode()); } } } } /** * Test some obvious, awkward cases. Really, we just check that * these don't crash. Correct functionality is checked elsewhere. */ @Test public void testStatePathString() { StatePath nullPath = new StatePath( (String)null); StatePath emptyPath = new StatePath( ""); StatePath spacePath = new StatePath( " "); assertNotNull( "StatePath(null) returned null", nullPath); assertNotNull( "StatePath(\"\") returned null", emptyPath); assertNotNull( "StatePath(\" \") returned null", spacePath); } /** * Fairly exhaustive test of the equals() method. */ @Test public void testEqualsObject() { final int MAX_LEN = 4; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { StatePath path1 = buildStatePath( len, offset, stride); assertFalse( "path1 equals null", path1.equals( null)); assertEquals( "path1 equals path1", path1, path1); // Iterate over all other StatePaths, checking equality. for( int lenOther = 1; lenOther < MAX_LEN; lenOther++) { for( int offsetOther = 0; offsetOther < PATH_ELEMENTS.length; offsetOther++) { for( int strideOther = 0; strideOther < PATH_ELEMENTS.length; strideOther++) { StatePath path2 = buildStatePath( lenOther, offsetOther, strideOther); /** * What we would expect for equality. The slightly odd stride-ness is because, * when len == 1, stride is irrelevant. */ boolean expectedEquality = len == lenOther && offset == offsetOther && (len == 1 || stride == strideOther); if( expectedEquality) { assertEquals( "path1 not equal to path2", path1, path2); assertEquals( "path2 not equal to path1", path2, path1); } else { assertFalse( "path1 equal to path2", path1.equals( path2)); assertFalse( "path2 equal to path1", path2.equals( path1)); } } } } } } } } /** * Check that equals or is child returns the correct answer. */ @Test public void testEqualsOrHasChild() { final int MAX_LEN = 4; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { StatePath parentPath = buildStatePath( len, offset, stride); for( int lenOther = 1; lenOther < MAX_LEN; lenOther++) { for( int offsetOther = 0; offsetOther < PATH_ELEMENTS.length; offsetOther++) { for( int strideOther = 0; strideOther < PATH_ELEMENTS.length; strideOther++) { StatePath childPath = buildStatePath( lenOther, offsetOther, strideOther); boolean expected = len <= lenOther && offset == offsetOther && (len == 1 || stride == strideOther); if( expected) { assertTrue( "false: " + parentPath.toString() + " equalsOrHasChild:" + childPath.toString(), parentPath.equalsOrHasChild( childPath)); } else { assertFalse( "true: " + parentPath.toString() + " equalsOrHasChild:" + childPath.toString(), parentPath.equalsOrHasChild( childPath)); } } } } } } } } @Test public void testEqualsOrHasChildNotEqualAsNull() { StatePath path = StatePath.parsePath("aa.bb"); assertFalse( "null is unexpectedly equal-or-child-of a path", path.equalsOrHasChild( null)); } @Test public void testNullIsParentOf() { StatePath path = StatePath.parsePath("aa.bb"); assertFalse( "StatePath claiming to be parent of root", path.isParentOf(null)); } /** * Check that isParentOf() works as expected. */ @Test public void testIsParentOf() { final int MAX_LEN = 4; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { StatePath parentPath = buildStatePath( len, offset, stride); for( int lenOther = 1; lenOther < MAX_LEN; lenOther++) { for( int offsetOther = 0; offsetOther < PATH_ELEMENTS.length; offsetOther++) { for( int strideOther = 0; strideOther < PATH_ELEMENTS.length; strideOther++) { StatePath childPath = buildStatePath( lenOther, offsetOther, strideOther); assertNotNull( "childPath", childPath); boolean expected = (len+1 == lenOther) && offset == offsetOther && (len == 1 || stride == strideOther); if( expected) { assertTrue( "false: " + parentPath.toString() + " isParentOf:" + childPath.toString(), parentPath.isParentOf( childPath)); } else { assertFalse( "true: " + parentPath.toString() + " isParentOf:" + childPath.toString(), parentPath.isParentOf( childPath)); } } } } } } } } @Test public void testToString() { final int MAX_LEN = 5; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { String elements[] = buildPathArray( len, offset, stride); StatePath path = buildStatePath( len, offset, stride); StringBuilder sb = new StringBuilder(); for( String element : elements) { if( sb.length() > 0) { sb.append("."); } sb.append( element); } assertEquals( "path name mismatch: " + path.toString()+ " != " + sb.toString(), path.toString(), sb.toString()); } } } } @Test public void testToStringPrefix() { StatePath testPath = StatePath.parsePath("aa.bb.cc"); assertEquals( "toString with prefix", "aa.bb.cc", testPath.toString( null)); assertEquals( "toString with prefix", "bb.cc", testPath.toString( StatePath.parsePath( "foo"))); assertEquals( "toString with prefix", "cc", testPath.toString( StatePath.parsePath( "foo.bar"))); assertEquals( "toString with prefix", "", testPath.toString( StatePath.parsePath( "foo.bar.baz"))); } @Test public void testToStringStringInt() { String seperators[] = { ".", "\"", "[]", "", "t"}; final int MAX_LEN = 5; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { String elements[] = buildPathArray( len, offset, stride); StatePath path = buildStatePath( len, offset, stride); for( String sep : seperators) { for( int skip = 0; skip < len+1; skip++) { StringBuilder sb = new StringBuilder(); int count=0; for( String element : elements) { if( count++ < skip) { continue; } if( sb.length() > 0) { sb.append(sep); } sb.append( element); } String pathStr = path.toString( sep, skip); assertEquals( "path "+path.toString( sep, skip)+" (skip="+skip+") not equal to expected value " + sb.toString(), pathStr, sb.toString()); } } } } } } /** * TODO: implement this test @Test public void testToStringStatePath() { fail("Not yet implemented"); } */ @Test public void testGetFirstElement() { final int MAX_LEN = 5; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { StatePath path = buildStatePath( len, offset, stride); String firstElement = path.getFirstElement(); assertNotNull( "firstElement", firstElement); assertEquals( "mismatch between first element ("+firstElement+") and expected value (" + PATH_ELEMENTS [offset] + ")", firstElement, PATH_ELEMENTS [offset]); } } } } @Test public void testGetLastElement() { final int MAX_LEN = 5; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { StatePath path = buildStatePath( len, offset, stride); String pathElements[] = buildPathArray( len, offset, stride); String lastElement = path.getLastElement(); String expectedLastElement = pathElements [pathElements.length -1]; assertEquals( "mismatch between last element ("+lastElement+") and expected value (" + expectedLastElement + ")", lastElement, expectedLastElement); } } } } @Test public void testNewChildString() { final int MAX_LEN = 5; final String childElement = "foo"; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { StatePath parent = buildStatePath( len, offset, stride); StatePath child = parent.newChild( childElement); assertTrue( "false: parent isParentOf child", parent.isParentOf( child)); assertFalse( "true: child isParentOf parent", child.isParentOf( parent)); assertEquals( "last element mismatch", child.getLastElement(), childElement); } } } } @Test public void testNewChildStatePath() { final int MAX_LEN = 3; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { StatePath parent = buildStatePath( len, offset, stride); for( int lenOther = 1; lenOther < MAX_LEN; lenOther++) { for( int offsetOther = 0; offsetOther < PATH_ELEMENTS.length; offsetOther++) { for( int strideOther = 0; strideOther < PATH_ELEMENTS.length; strideOther++) { StatePath subPath = buildStatePath( lenOther, offsetOther, strideOther); StatePath child = parent.newChild( subPath); if( lenOther == 1) { assertTrue("false: parent (" + parent .toString() + ") isParentOf child (" + child .toString() + ")", parent .isParentOf(child)); } else { assertFalse("true: parent (" + parent .toString() + ") isParentOf child (" + child .toString() + ")", parent .isParentOf(child)); } assertFalse( "true: child isParentOf parent", child.isParentOf( parent)); String expectedChildPath = parent.toString() + "." + subPath.toString(); assertEquals( "mismatch between expected ("+expectedChildPath+") and actual path ("+child.toString()+")", expectedChildPath, child.toString()); } } } } } } } @Test public void testChildPath() { final int MAX_LEN = 5; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { StatePath path = buildStatePath( len, offset, stride); StatePath childsPath = path.childPath(); if( len == 1) { assertSame( "child of single element wrong", childsPath, null); } else { // Try to rebuild the original path StatePath first = new StatePath( PATH_ELEMENTS [offset]); StatePath reconstructedPath = first.newChild( childsPath); assertEquals( "unable to reconstruct original path", reconstructedPath, path); } } } } } @Test public void testParentPath() { final int MAX_LEN = 5; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { StatePath path = buildStatePath( len, offset, stride); String array[] = buildPathArray( len, offset, stride); StatePath parentPath = path.parentPath(); if( len == 1) { assertSame( "parentPath returned non-null entry for path with a single element", parentPath, null); } else { // Attempt to reconstruct the original path StatePath reconstructedPath = parentPath.newChild( array [array.length-1]); assertEquals( "reconstructedPath and path not the same", reconstructedPath, path); } } } } } @Test public void testIsSimplePath() { final int MAX_LEN = 5; for( int len = 1; len < MAX_LEN; len++) { for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { StatePath path = buildStatePath( len, offset, stride); if( len == 1) { assertTrue("expected true", path.isSimplePath()); } else { assertFalse("expected false", path.isSimplePath()); } } } } } @Test public void testParseAndListFromStringCopyEqual() { String elementName = "element-1"; StatePath pathFromParser = StatePath.parsePath( elementName); String copyOfElementName = new String(elementName); assertNotSame( "asserting String not intern()ed", elementName, copyOfElementName); StatePath pathFromList = StatePath.buildFromList( Arrays.asList( copyOfElementName)); assertEquals( "", pathFromParser, pathFromList); } @Test public void testListAndParseFromStringCopyEqual() { String elementName = "element-1"; StatePath pathFromList = StatePath.buildFromList( Arrays.asList( elementName)); String copyOfElementName = new String(elementName); assertNotSame( "asserting String not intern()ed", elementName, copyOfElementName); StatePath pathFromParser = new StatePath(elementName); assertEquals( "", pathFromParser, pathFromList); } /** * S A N I T Y---C H E C K T E S T S */ /** * Test the vital buildPathArray() method. */ @Test public void testPathArray() { // Test that offset works and that, when length is 1, the stride is irrelevant. for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { String elements[] = buildPathArray( 1, offset, stride); assertEquals( "single-element buildStatePath broken", elements[0], PATH_ELEMENTS [offset]); } } // Test that stride works, at least for paths of length 2 for( int offset = 0; offset < PATH_ELEMENTS.length; offset++) { for( int stride = 0; stride < PATH_ELEMENTS.length; stride++) { String elements[] = buildPathArray( 2, offset, stride); if( stride == 0) { assertEquals("mismatch between first and second elements", elements[0], elements[1]); } else { assertFalse("first and second elements are the same when they shouldn't be", elements[0] .equals(elements[1])); } } } } /** * Check that that hashAlreadySeen works as expected. * (or, at least, not obviously broken). */ @Test public void testHashAlreadySeen() { Set<Integer> seenHashCodes = new TreeSet<>(); for( int hash = 0; hash < 100; hash++) { assertFalse(hashAlreadySeen(seenHashCodes, hash)); } for( int hash = 0; hash < 100; hash++) { assertTrue(hashAlreadySeen(seenHashCodes, hash)); } } /** * P R I V A T E F U N C T I O N S */ /** * Provide a path of length len by cycling through the available path elements. * Which elements are selected is controlled by the offset and stride parameters. * <p> * This assumes that String constructor and newChild(String) method is working. * @param len number of elements in the path * @param offset the index of the first element to be selected * @param stride the number to increment the index after each iteration * @return a StatePath with the number of elements present. */ private StatePath buildStatePath( int len, int offset, int stride) { StatePath path = null; String elements[] = buildPathArray( len, offset, stride); for( String element : elements) { path = path == null ? new StatePath(element) : path .newChild(element); } if( len > 0) { assertNotNull("buildStatePath", path); } else { assertNull("buildStatePath", path); } return path; } /** * Provide a String array based on the PATH_ELEMENTS by cycling through the available * path elements. The selected elements are controlled by the offset and stride * parameters. * <p> * @param len number of elements in the array, * @param offset the index of the first element to be selected * @param stride the number to increment the index after each iteration * @return a String array with the number of elements. */ private String[] buildPathArray( int len, int offset, int stride) { String[] array = new String[len]; for( int arrayIdx = 0; arrayIdx < len; arrayIdx++) { array[arrayIdx] = PATH_ELEMENTS[(arrayIdx * stride + offset) % PATH_ELEMENTS.length]; } return array; } }