/* * 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.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; 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 ArraySortedMapTest { 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 = ArraySortedMap.fromMap(data, StringComparator); assertEquals(10, map.size()); } @Test public void emptyMap() { Map<String, Integer> data = new HashMap<>(); ImmutableSortedMap<String, Integer> map = ArraySortedMap.fromMap(data, StringComparator); assertEquals(0, map.size()); assertEquals(true, map.isEmpty()); } @Test public void almostEmptyMap() { Map<String, Integer> data = new HashMap<>(); data.put("a", 1); data.put("b", null); ImmutableSortedMap<String, Integer> map = ArraySortedMap.fromMap(data, StringComparator); assertEquals(2, map.size()); assertEquals(false, map.isEmpty()); } @Test public void searchForASpecificKey() { ImmutableSortedMap<Integer, Integer> map = new ArraySortedMap<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 removeKeyValuePair() { ImmutableSortedMap<Integer, Integer> map = new ArraySortedMap<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 ArraySortedMap<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)); } @Test public void canReplaceExistingItem() { ImmutableSortedMap<Integer, Integer> map = new ArraySortedMap<>(IntComparator); map = map.insert(1, 1).insert(1, 2); assertEquals(2, (int) map.get(1)); } @Test public void replacesExistingKey() { ImmutableSortedMap<String, Integer> map = new ArraySortedMap<>(StringComparator); String keyOne = new String("1"); String keyTwo = new String("2"); map = map.insert(keyOne, 1).insert(keyTwo, 2); assertNotSame(keyOne, map.getMaxKey()); assertSame(keyTwo, map.getMaxKey()); } @Test public void replaceExactKeyYieldsSameMap() { ImmutableSortedMap<String, Integer> map = new ArraySortedMap<>(StringComparator); String key = "1"; Integer value = 1; map = map.insert(key, value); Assert.assertSame(map, map.insert(key, value)); } @Test public void removingNonExistentKeyYieldsSameMap() { ImmutableSortedMap<String, Integer> map = new ArraySortedMap<>(StringComparator); map = map.insert("key", 1); Assert.assertSame(map, map.remove("no-key")); } @Test public void predecessorKeyThrowsExceptionIfKeyIsNotPresent() { ImmutableSortedMap<String, Integer> map = new ArraySortedMap<>(StringComparator); map = map.insert("key", 1); Assert.assertEquals(null, map.getPredecessorKey("key")); try { map.getPredecessorKey("no-key"); fail("Didn't throw exception"); } catch (IllegalArgumentException e) { // } } // QuickCheck Tests @Test public void sizeIsCorrect() { for (Map<Integer, Integer> any : someMaps(integers(), integers())) { Assert.assertEquals(any.size(), ArraySortedMap.fromMap(any, IntComparator).size()); } } @Test public void addWorks() { for (Map<Integer, Integer> any : someMaps(integers(), integers())) { ImmutableSortedMap<Integer, Integer> map = new ArraySortedMap<>(IntComparator); for (Map.Entry<Integer, Integer> entry : any.entrySet()) { map = map.insert(entry.getKey(), entry.getValue()); } for (Map.Entry<Integer, Integer> entry : any.entrySet()) { Assert.assertEquals(entry.getValue(), map.get(entry.getKey())); } } } @Test public void removeWorks() { for (Map<Integer, Integer> any : someMaps(integers(), integers())) { ImmutableSortedMap<Integer, Integer> map = ArraySortedMap.fromMap(any, IntComparator); for (Map.Entry<Integer, Integer> entry : any.entrySet()) { map = map.remove(entry.getKey()); } Assert.assertEquals(0, map.size()); } } @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 = ArraySortedMap.fromMap(any, IntComparator); List<Integer> actualKeys = new ArrayList<>(); for (Map.Entry<Integer, Integer> entry : map) { actualKeys.add(entry.getKey()); } Assert.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 = ArraySortedMap.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()); } Assert.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 = ArraySortedMap.fromMap(any, IntComparator); List<Integer> actualKeys = new ArrayList<>(); Iterator<Map.Entry<Integer, Integer>> iterator = map.reverseIterator(); while (iterator.hasNext()) { actualKeys.add(iterator.next().getKey()); } Assert.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 = ArraySortedMap.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()); } Assert.assertEquals(expectedKeys, actualKeys); } } @Test public void predecessorKeyIsCorrect() { for (Map<Integer, Integer> any : someMaps(integers(), integers())) { ImmutableSortedMap<Integer, Integer> map = ArraySortedMap.fromMap(any, IntComparator); Integer predecessorKey = null; for (Map.Entry<Integer, Integer> entry : map) { Assert.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 = ArraySortedMap.fromMap(any, IntComparator); Integer lastKey = null; for (Map.Entry<Integer, Integer> entry : map) { if (lastKey != null) { Assert.assertEquals(entry.getKey(), map.getSuccessorKey(lastKey)); } lastKey = entry.getKey(); } if (lastKey != null) { Assert.assertEquals(null, map.getSuccessorKey(lastKey)); } } } @Test public void addAboveLimitYieldsRBTree() { Map<Integer, Integer> any = someMaps(integers(), integers(), fixedValues(100)).iterator().next(); ImmutableSortedMap<Integer, Integer> map = new ArraySortedMap<>(IntComparator); for (Map.Entry<Integer, Integer> entry : any.entrySet()) { map = map.insert(entry.getKey(), entry.getValue()); } Assert.assertEquals(RBTreeSortedMap.class, map.getClass()); for (Map.Entry<Integer, Integer> entry : any.entrySet()) { Assert.assertEquals(entry.getValue(), map.get(entry.getKey())); } } @Test public void equalsIsCorrect() { ImmutableSortedMap<Integer, Integer> map; ImmutableSortedMap<Integer, Integer> copy; ImmutableSortedMap<Integer, Integer> rbcopy; ImmutableSortedMap<Integer, Integer> copyWithDifferentComparator; map = new ArraySortedMap<>(IntComparator); copy = new ArraySortedMap<>(IntComparator); rbcopy = new RBTreeSortedMap<>(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); rbcopy = rbcopy.insert(key, value); copyWithDifferentComparator = copyWithDifferentComparator.insert(key, value); } Assert.assertTrue(map.equals(copy)); Assert.assertTrue(map.equals(rbcopy)); Assert.assertTrue(rbcopy.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(rbcopy.remove(rbcopy.getMaxKey()))); } //@Test public void perf() { ImmutableSortedMap<Integer, Integer> map = new ArraySortedMap<>(IntComparator); for (int j = 0; j < 5; j++) { final long startTime = System.currentTimeMillis(); for (int i = 0; i < 50000; i++) { map = map.insert(i, i); } for (int i = 0; i < 50000; i++) { map = map.remove(i); } System.out.println("Elapsed: " + (System.currentTimeMillis() - startTime)); } } }