/* * Copyright 2017 Google Inc. * * 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. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.firebase.database.collection; import static net.java.quickcheck.generator.CombinedGeneratorsIterables.someMaps; import static net.java.quickcheck.generator.PrimitiveGenerators.booleans; import static net.java.quickcheck.generator.PrimitiveGenerators.fixedValues; import static net.java.quickcheck.generator.PrimitiveGenerators.integers; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import com.google.firebase.database.utilities.Utilities; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.junit.Assert; import org.junit.Test; public class RBTreeSortedMapTest { private static Comparator<Integer> IntComparator = StandardComparator.getComparator(Integer.class); private static Comparator<String> StringComparator = StandardComparator.getComparator(String.class); @Test public void basicImmutableSortedMapBuilding() { Map<String, Integer> data = new HashMap<>(); data.put("a", 1); data.put("b", 2); data.put("c", 3); data.put("d", 4); data.put("e", 5); data.put("f", 6); data.put("g", 7); data.put("h", 8); data.put("i", 9); data.put("j", 10); ImmutableSortedMap<String, Integer> map = RBTreeSortedMap.fromMap(data, StringComparator); assertEquals(10, map.size()); } @Test public void emptyMap() { Map<String, Integer> data = new HashMap<>(); ImmutableSortedMap<String, Integer> map = RBTreeSortedMap.fromMap(data, StringComparator); assertEquals(0, map.size()); } @Test public void almostEmptyMap() { Map<String, Integer> data = new HashMap<>(); data.put("a", 1); data.put("b", null); ImmutableSortedMap<String, Integer> map = RBTreeSortedMap.fromMap(data, StringComparator); assertEquals(2, map.size()); } @Test public void createNode() { ImmutableSortedMap<String, Integer> map = new RBTreeSortedMap<>(StringComparator); map = map.insert("a", 1); RBTreeSortedMap<String, Integer> rbMap = (RBTreeSortedMap<String, Integer>) map; assertTrue(rbMap.getRoot().getLeft().isEmpty()); assertTrue(rbMap.getRoot().getRight().isEmpty()); } @Test public void searchForASpecificKey() { ImmutableSortedMap<Integer, Integer> map = new RBTreeSortedMap<Integer, Integer>(IntComparator).insert(1, 1).insert(2, 2); assertEquals(1, (int) map.get(1)); assertEquals(2, (int) map.get(2)); assertNull(map.get(3)); } @Test public void canInsertNewPairs() { ImmutableSortedMap<Integer, Integer> map = new RBTreeSortedMap<Integer, Integer>(IntComparator).insert(1, 1).insert(2, 2); RBTreeSortedMap<Integer, Integer> rbMap = (RBTreeSortedMap<Integer, Integer>) map; assertEquals(2, (int) rbMap.getRoot().getKey()); assertEquals(1, (int) rbMap.getRoot().getLeft().getKey()); } @Test public void removeKeyValuePair() { ImmutableSortedMap<Integer, Integer> map = new RBTreeSortedMap<Integer, Integer>(IntComparator).insert(1, 1).insert(2, 2); map = map.remove(1); assertEquals(2, (int) map.get(2)); assertNull(map.get(1)); } @Test public void moreRemovals() { ImmutableSortedMap<Integer, Integer> map = new RBTreeSortedMap<Integer, Integer>(IntComparator) .insert(1, 1) .insert(50, 50) .insert(3, 3) .insert(4, 4) .insert(7, 7) .insert(9, 9) .insert(20, 20) .insert(18, 18) .insert(2, 2) .insert(71, 71) .insert(42, 42) .insert(88, 88); map = map.remove(7).remove(3).remove(1); assertNull(map.get(7)); assertNull(map.get(3)); assertNull(map.get(1)); assertEquals(50, (int) map.get(50)); } // QuickCheck tests @Test public void iterationIsInOrder() { for (Map<Integer, Integer> any : someMaps(integers(), integers())) { List<Integer> expectedKeys = new ArrayList<>(any.keySet()); Collections.sort(expectedKeys); ImmutableSortedMap<Integer, Integer> map = RBTreeSortedMap.fromMap(any, IntComparator); List<Integer> actualKeys = new ArrayList<>(); for (Map.Entry<Integer, Integer> entry : map) { actualKeys.add(entry.getKey()); } assertEquals(expectedKeys, actualKeys); } } @Test public void iterationFromKeyIsInOrder() { for (Map<Integer, Integer> any : someMaps(integers(), integers())) { List<Integer> expectedKeys = new ArrayList<>(any.keySet()); Integer fromKey = (expectedKeys.isEmpty() || booleans().next()) ? integers().next() : expectedKeys.get(0); Collections.sort(expectedKeys); Iterator<Integer> iterator = expectedKeys.iterator(); while (iterator.hasNext()) { Integer next = iterator.next(); if (next.compareTo(fromKey) < 0) { iterator.remove(); } } ImmutableSortedMap<Integer, Integer> map = RBTreeSortedMap.fromMap(any, IntComparator); List<Integer> actualKeys = new ArrayList<>(); Iterator<Map.Entry<Integer, Integer>> mapIterator = map.iteratorFrom(fromKey); while (mapIterator.hasNext()) { actualKeys.add(mapIterator.next().getKey()); } assertEquals(expectedKeys, actualKeys); } } @Test public void reverseIterationIsInOrder() { for (Map<Integer, Integer> any : someMaps(integers(), integers())) { List<Integer> expectedKeys = new ArrayList<>(any.keySet()); Collections.sort(expectedKeys); Collections.reverse(expectedKeys); ImmutableSortedMap<Integer, Integer> map = RBTreeSortedMap.fromMap(any, IntComparator); List<Integer> actualKeys = new ArrayList<>(); Iterator<Map.Entry<Integer, Integer>> iterator = map.reverseIterator(); while (iterator.hasNext()) { actualKeys.add(iterator.next().getKey()); } assertEquals(expectedKeys, actualKeys); } } @Test public void reverseIterationFromKeyIsInOrder() { for (Map<Integer, Integer> any : someMaps(integers(), integers())) { List<Integer> expectedKeys = new ArrayList<>(any.keySet()); Integer fromKey = (expectedKeys.isEmpty() || booleans().next()) ? integers().next() : expectedKeys.get(0); Collections.sort(expectedKeys); Collections.reverse(expectedKeys); Iterator<Integer> iterator = expectedKeys.iterator(); while (iterator.hasNext()) { Integer next = iterator.next(); if (next.compareTo(fromKey) > 0) { iterator.remove(); } } ImmutableSortedMap<Integer, Integer> map = RBTreeSortedMap.fromMap(any, IntComparator); List<Integer> actualKeys = new ArrayList<>(); Iterator<Map.Entry<Integer, Integer>> mapIterator = map.reverseIteratorFrom(fromKey); while (mapIterator.hasNext()) { actualKeys.add(mapIterator.next().getKey()); } assertEquals(expectedKeys, actualKeys); } } @Test public void predecessorKeyIsCorrect() { for (Map<Integer, Integer> any : someMaps(integers(), integers())) { ImmutableSortedMap<Integer, Integer> map = RBTreeSortedMap.fromMap(any, IntComparator); Integer predecessorKey = null; for (Map.Entry<Integer, Integer> entry : map) { assertEquals(predecessorKey, map.getPredecessorKey(entry.getKey())); predecessorKey = entry.getKey(); } } } @Test public void successorKeyIsCorrect() { for (Map<Integer, Integer> any : someMaps(integers(), integers())) { ImmutableSortedMap<Integer, Integer> map = RBTreeSortedMap.fromMap(any, IntComparator); Integer lastKey = null; for (Map.Entry<Integer, Integer> entry : map) { if (lastKey != null) { assertEquals(entry.getKey(), map.getSuccessorKey(lastKey)); } lastKey = entry.getKey(); } if (lastKey != null) { assertEquals(null, map.getSuccessorKey(lastKey)); } } } @Test public void equalsIsCorrect() { ImmutableSortedMap<Integer, Integer> map; ImmutableSortedMap<Integer, Integer> copy; ImmutableSortedMap<Integer, Integer> arraycopy; ImmutableSortedMap<Integer, Integer> copyWithDifferentComparator; map = new RBTreeSortedMap<>(IntComparator); copy = new RBTreeSortedMap<>(IntComparator); arraycopy = new ArraySortedMap<>(IntComparator); copyWithDifferentComparator = new ArraySortedMap<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Utilities.compareInts(o1, o2); } }); int size = ArraySortedMap.Builder.ARRAY_TO_RB_TREE_SIZE_THRESHOLD - 1; Map<Integer, Integer> any = someMaps(integers(), integers(), fixedValues(size)).iterator().next(); for (Map.Entry<Integer, Integer> entry : any.entrySet()) { Integer key = entry.getKey(); Integer value = entry.getValue(); map = map.insert(key, value); copy = copy.insert(key, value); arraycopy = arraycopy.insert(key, value); copyWithDifferentComparator = copyWithDifferentComparator.insert(key, value); } Assert.assertTrue(map.equals(copy)); Assert.assertTrue(map.equals(arraycopy)); Assert.assertTrue(arraycopy.equals(map)); Assert.assertFalse(map.equals(copyWithDifferentComparator)); Assert.assertFalse(map.equals(copy.remove(copy.getMaxKey()))); Assert.assertFalse(map.equals(copy.insert(copy.getMaxKey() + 1, 1))); Assert.assertFalse(map.equals(arraycopy.remove(arraycopy.getMaxKey()))); } }