// Copyright 2012 Google Inc. All Rights Reserved. // // 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.collide.client.util.collections; import com.google.collide.json.client.JsoArray; import com.google.collide.json.client.JsoStringSet; import com.google.collide.json.shared.JsonArray; import com.google.gwt.junit.client.GWTTestCase; import java.util.Comparator; import java.util.Iterator; import java.util.Random; /** * Test cases for {@link SkipListStringSet}. * */ public class SkipListStringSetTest extends GWTTestCase { @Override public String getModuleName() { return "com.google.collide.client.util.UtilTestModule"; } public void testEmpty() { SkipListStringSet set = SkipListStringSet.create(); Iterator iterator = set.search("").iterator(); assertFalse("emptiness", iterator.hasNext()); set.remove("foo"); } public void testAdd() { SkipListStringSet set = SkipListStringSet.create(); set.add("foo"); Iterator iterator = set.search("foo").iterator(); assertEquals("found added element", "foo", iterator.next()); assertFalse("no more elements", iterator.hasNext()); } public void testSearch() { SkipListStringSet set = SkipListStringSet.create(); set.add("3"); set.add("1"); set.add("2"); Iterator iterator = set.search("2").iterator(); assertEquals("found target", "2", iterator.next()); assertEquals("found next", "3", iterator.next()); assertFalse("no more elements", iterator.hasNext()); } public void testSearchAbsent() { SkipListStringSet set = SkipListStringSet.create(); set.add("4"); set.add("1"); set.add("3"); Iterator iterator = set.search("2").iterator(); assertEquals("found least greater than", "3", iterator.next()); assertEquals("found next", "4", iterator.next()); assertFalse("no more elements", iterator.hasNext()); } public void testRemove() { SkipListStringSet set = SkipListStringSet.create(); set.add("4"); set.add("2"); set.add("1"); set.add("3"); set.remove("3"); Iterator iterator = set.search("2").iterator(); assertEquals("found target", "2", iterator.next()); assertEquals("found next", "4", iterator.next()); assertFalse("no more elements", iterator.hasNext()); } public void testCorrectnessOnRandomData() { Random rnd = new Random(42); for (int i = 128; i <= 4096; i = i * 2) { checkCorrectnessOnRandomData(rnd, i, 100); } checkCorrectnessOnRandomData(rnd, 16384, 10000); } /** * This method is used to check correctness of implementation on random data. * * <p>During test random keys are generated. With some probability they are * either added or removed form set. * * <p>To avoid useless operations, in situation when we were going to remove key * that is not in set, already, we add it. Vice versa, do not add keys that are * already in set. * * <p>All operations are applied to instance of target implementation and to * instance of class that allows similar functionality (set). So resulting key * set should be similar. This is explicitly checked. * * @param rnd randomness source * @param limit number of operations to perform * @param range how many different keys can appear in test */ private void checkCorrectnessOnRandomData(final Random rnd, int limit, int range) { // Origin instance. JsoStringSet mirror = JsoStringSet.create(); // Array to hold recently generated keys. JsoArray<String> values = JsoArray.create(); // Target instance, with fully deterministic behavior.(driven by our // oscillator). SkipListStringSet target = new SkipListStringSet(8, new SkipListStringSet.LevelGenerator() { @Override public int generate() { int result = 0; while (rnd.nextInt(4) == 0 && result < 7) { result++; } return result; } }); for (int i = 0; i < limit; i++) { boolean remove = false; if (values.size() > 0) { // Actually we simulate that set is growing // i.e. more key added than removed). remove = rnd.nextDouble() > 0.6; } // Value is either generated or selected from recent values list. String value; if (remove) { // When we are going to remove, recent values is a good choice // to choose from. value = values.get(rnd.nextInt(values.size())); // Eventually, if can't remove - add. if (!mirror.contains(value)) { remove = false; } } else { value = String.valueOf(rnd.nextInt(range)); // If key already in set - remove it. if (mirror.contains(value)) { remove = true; } else { values.add(value); } } // Perform operation on both instances. if (remove) { target.remove(value); mirror.remove(value); } else { target.add(value); mirror.add(value); } } // Get sorted list of set of keys. JsonArray<String> keys = mirror.getKeys(); keys.sort(new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } }); // Check that keys in search result has the same order and values. Iterator iterator = target.search(keys.get(0)).iterator(); for (int i = 0, l = keys.size(); i < l; i++) { assertEquals("values", keys.get(i), iterator.next()); } assertFalse("no more values", iterator.hasNext()); // Check that all keys are properly selectable. for (int i = 0, l = keys.size(); i < l; i++) { String value = keys.get(i); assertEquals("search", value, target.search(value).iterator().next()); } } }