/*
* 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())));
}
}