// Copyright © 2011-2013, Esko Luontola <www.orfjackal.net> // This software is released under the Apache License 2.0. // The license text is at http://www.apache.org/licenses/LICENSE-2.0 package fi.jumi.api.drivers; import org.hamcrest.*; import org.junit.*; import org.junit.rules.ExpectedException; import java.util.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; public class TestIdTest { @Rule public final ExpectedException thrown = ExpectedException.none(); // low-level operations @Test public void to_string() { assertThat(TestId.of().toString(), is("TestId()")); assertThat(TestId.of(0).toString(), is("TestId(0)")); assertThat(TestId.of(1).toString(), is("TestId(1)")); assertThat(TestId.of(1, 2).toString(), is("TestId(1, 2)")); assertThat(TestId.of(1, 2, 3).toString(), is("TestId(1, 2, 3)")); } @Test public void is_a_value_object() { assertTrue("same value", TestId.of(1).equals(TestId.of(1))); assertFalse("different value", TestId.of(1).equals(TestId.of(2))); assertFalse("many path elements", TestId.of(1, 3, 1).equals(TestId.of(1, 2, 1))); assertFalse("longer & shorter", TestId.of(1, 2).equals(TestId.of(1))); assertFalse("shorter & longer", TestId.of(1).equals(TestId.of(1, 2))); assertFalse("root & child", TestId.ROOT.equals(TestId.of(1))); assertFalse("child & root", TestId.of(1).equals(TestId.ROOT)); assertFalse("null", TestId.of(1).equals(null)); assertEquals("hashCode for same values", TestId.of(1, 2, 3).hashCode(), TestId.of(1, 2, 3).hashCode()); } @Test public void hashCode_has_good_dispersion() { List<TestId> values = new ArrayList<>(); values.add(TestId.of()); values.add(TestId.of(0)); values.add(TestId.of(1)); values.add(TestId.of(2)); values.add(TestId.of(3)); values.add(TestId.of(0, 0)); values.add(TestId.of(1, 0)); values.add(TestId.of(0, 1)); values.add(TestId.of(2, 0)); values.add(TestId.of(0, 2)); values.add(TestId.of(1, 1)); values.add(TestId.of(1, 2)); values.add(TestId.of(2, 1)); Set<Integer> uniqueHashCodes = new HashSet<>(); for (TestId value : values) { uniqueHashCodes.add(value.hashCode()); } assertThat("unique hash codes", uniqueHashCodes.size(), is(values.size())); } @Test public void ordering() { List<TestId> expectedOrder = Arrays.asList( TestId.of(), TestId.of(0), TestId.of(0, 0), TestId.of(0, 1), TestId.of(0, 2), TestId.of(1), TestId.of(1, 0), TestId.of(1, 1), TestId.of(1, 2), TestId.of(2) ); List<TestId> actualOrder = new ArrayList<>(expectedOrder); Collections.shuffle(actualOrder); Collections.sort(actualOrder); assertThat("natural order", actualOrder, is(expectedOrder)); for (TestId testId : expectedOrder) { assertThat("comparing with self: " + testId, testId.compareTo(testId), is(0)); } } @Test public void get_index() { assertThat(TestId.of(0).getIndex(), is(0)); assertThat(TestId.of(1).getIndex(), is(1)); assertThat(TestId.of(1, 2).getIndex(), is(2)); } @Test public void get_path() { assertThat(TestId.ROOT.getPath(), intArray()); assertThat(TestId.of(0).getPath(), intArray(0)); assertThat(TestId.of(1).getPath(), intArray(1)); assertThat(TestId.of(5, 6, 7).getPath(), intArray(5, 6, 7)); } @Test public void root_has_no_index() { thrown.expect(UnsupportedOperationException.class); thrown.expectMessage("root has no index"); TestId.ROOT.getIndex(); } @Test public void negative_indices_are_not_allowed() { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("illegal index: -1"); TestId.of(-1); } @Test public void overflows_are_prevented() { TestId lastSibling = TestId.of(Integer.MAX_VALUE); thrown.expect(IllegalArgumentException.class); thrown.expectMessage("illegal index: -2147483648"); lastSibling.getNextSibling(); } // inquiring @Test public void is_root() { assertThat(TestId.ROOT.isRoot(), is(true)); assertThat(TestId.of().isRoot(), is(true)); assertThat(TestId.of(0).isRoot(), is(false)); assertThat(TestId.of(1, 2).isRoot(), is(false)); } @Test public void there_is_only_one_root_instance() { assertThat(TestId.of(), is(sameInstance(TestId.ROOT))); } @Test public void is_first_child() { assertThat(TestId.of(0).isFirstChild(), is(true)); assertThat(TestId.of(1).isFirstChild(), is(false)); assertThat(TestId.of(1, 2, 0).isFirstChild(), is(true)); assertThat(TestId.of(1, 2, 3).isFirstChild(), is(false)); } @Test public void root_is_not_a_child() { thrown.expect(UnsupportedOperationException.class); thrown.expectMessage("root is not a child"); TestId.ROOT.isFirstChild(); } @Test public void is_ancestor_of() { TestId x = TestId.of(1, 2, 3); assertThat("grand child of x", TestId.of(1, 2, 3, 4, 5).isAncestorOf(x), is(false)); assertThat("child of x", TestId.of(1, 2, 3, 4).isAncestorOf(x), is(false)); assertThat("itself", TestId.of(1, 2, 3).isAncestorOf(x), is(false)); assertThat("parent of x", TestId.of(1, 2).isAncestorOf(x), is(true)); assertThat("grand parent of x", TestId.of(1).isAncestorOf(x), is(true)); assertThat("root", TestId.of().isAncestorOf(x), is(true)); assertThat("root reverse", x.isAncestorOf(TestId.of()), is(false)); assertThat("sibling of x", TestId.of(1, 2, 10).isAncestorOf(x), is(false)); assertThat("cousin of x", TestId.of(1, 10, 3).isAncestorOf(x), is(false)); assertThat("second cousin of x", TestId.of(10, 2, 3).isAncestorOf(x), is(false)); } @Test public void is_descendant_of() { TestId x = TestId.of(1, 2, 3); assertThat("grand child of x", TestId.of(1, 2, 3, 4, 5).isDescendantOf(x), is(true)); assertThat("child of x", TestId.of(1, 2, 3, 4).isDescendantOf(x), is(true)); assertThat("itself", TestId.of(1, 2, 3).isDescendantOf(x), is(false)); assertThat("parent of x", TestId.of(1, 2).isDescendantOf(x), is(false)); assertThat("grand parent of x", TestId.of(1).isDescendantOf(x), is(false)); assertThat("root", TestId.of().isDescendantOf(x), is(false)); assertThat("root reverse", x.isDescendantOf(TestId.of()), is(true)); assertThat("sibling of x", TestId.of(1, 2, 10).isDescendantOf(x), is(false)); assertThat("cousin of x", TestId.of(1, 10, 3).isDescendantOf(x), is(false)); assertThat("second cousin of x", TestId.of(10, 2, 3).isDescendantOf(x), is(false)); } // accessing relatives @Test public void get_parent() { assertThat(TestId.of(0).getParent(), is(TestId.ROOT)); assertThat(TestId.of(1).getParent(), is(TestId.ROOT)); assertThat(TestId.of(1, 2).getParent(), is(TestId.of(1))); } @Test public void root_has_no_parent() { thrown.expect(UnsupportedOperationException.class); thrown.expectMessage("root has no parent"); TestId.ROOT.getParent(); } @Test public void get_first_child() { assertThat(TestId.ROOT.getFirstChild(), is(TestId.of(0))); assertThat(TestId.ROOT.getFirstChild().getFirstChild(), is(TestId.of(0, 0))); assertThat(TestId.of(1, 2, 3).getFirstChild(), is(TestId.of(1, 2, 3, 0))); } @Test public void get_next_sibling() { assertThat(TestId.of(0).getNextSibling(), is(TestId.of(1))); assertThat(TestId.of(1).getNextSibling(), is(TestId.of(2))); assertThat(TestId.of(1, 2, 3).getNextSibling(), is(TestId.of(1, 2, 4))); } @Test public void root_has_no_siblings() { thrown.expect(UnsupportedOperationException.class); thrown.expectMessage("root has no siblings"); TestId.ROOT.getNextSibling(); } // helpers private static Matcher<int[]> intArray(int... expected) { return new TypeSafeMatcher<int[]>() { @Override protected boolean matchesSafely(int[] actual) { return Arrays.equals(actual, expected); } @Override public void describeTo(Description description) { description.appendText("int array ").appendText(Arrays.toString(expected)); } @Override protected void describeMismatchSafely(int[] actual, Description mismatchDescription) { mismatchDescription.appendText("was ").appendText(Arrays.toString(actual)); } }; } }