Expanded Unit tests to IntLists/Sets

Maps are sadly not possible. It will require writing templates for
permutations effectively copying guavas Test library.
So this will take a lot longer
This commit is contained in:
Speiger 2021-12-13 13:45:35 +01:00
parent caf2f22e9f
commit 18f6704ed7
11 changed files with 332 additions and 46 deletions

View File

@ -3,6 +3,8 @@
### Version 0.5.1
- Fixed: Reworked the NavigableSet/Map implementations of RBTree/AVLTree/Array Sets/Maps so they are now deemed stable.
- Added: Another 150k Unit tests.
- Added: List and Set Unit tests for Integer (or Primitives in this case) to ensure basic stability there. (Now covering all sets and lists)
- Fixed: Bugs with null values for primitive collections.
### Version 0.5.0
- Added: 2 Helper functions to find out how many bits are required to store a Number.

View File

@ -232,6 +232,7 @@ public class ARRAY_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE
* @param index the index where to append the elements to
* @param c the elements to append to the list
* @throws IndexOutOfBoundsException if index is outside of the lists range
* @throws NullPointerException if collection contains a null element
*/
@Override
@Primitive
@ -239,6 +240,9 @@ public class ARRAY_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE
if(c instanceof COLLECTION) return addAll(index, (COLLECTION KEY_GENERIC_TYPE)c);
int add = c.size();
if(add <= 0) return false;
#if !TYPE_OBJECT
if(c.contains(null)) throw new NullPointerException();
#endif
grow(size + add);
if(index != size) System.arraycopy(data, index, data, index+add, size - index);
size+=add;

View File

@ -210,6 +210,16 @@ public class AVL_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE
@Override
public KEY_TYPE getDefaultMinValue() { return defaultMinNotFound; }
#else
/** only used for primitives
* @return null
*/
public CLASS_TYPE getDefaultMaxValue() { return null; }
/** only used for primitives
* @return null
*/
public CLASS_TYPE getDefaultMinValue() { return null; }
#endif
@Override
public boolean add(KEY_TYPE o) {
@ -263,43 +273,53 @@ public class AVL_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE
@Override
public KEY_TYPE lower(KEY_TYPE e) {
Entry KEY_GENERIC_TYPE node = findLowerNode(e);
#if TYPE_OBJECT
return node != null ? node.key : null;
#else
return node != null ? node.key : defaultMinNotFound;
#endif
return node != null ? node.key : getDefaultMinValue();
}
@Override
public KEY_TYPE floor(KEY_TYPE e) {
Entry KEY_GENERIC_TYPE node = findFloorNode(e);
#if TYPE_OBJECT
return node != null ? node.key : null;
#else
return node != null ? node.key : defaultMinNotFound;
#endif
return node != null ? node.key : getDefaultMinValue();
}
@Override
public KEY_TYPE higher(KEY_TYPE e) {
Entry KEY_GENERIC_TYPE node = findHigherNode(e);
#if TYPE_OBJECT
return node != null ? node.key : null;
#else
return node != null ? node.key : defaultMaxNotFound;
#endif
return node != null ? node.key : getDefaultMaxValue();
}
@Override
public KEY_TYPE ceiling(KEY_TYPE e) {
Entry KEY_GENERIC_TYPE node = findCeilingNode(e);
#if TYPE_OBJECT
return node != null ? node.key : null;
#else
return node != null ? node.key : defaultMaxNotFound;
#endif
return node != null ? node.key : getDefaultMaxValue();
}
#if !TYPE_OBJECT
@Override
public CLASS_TYPE lower(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE node = findLowerNode(e);
return node != null ? node.key : null;
}
@Override
public CLASS_TYPE floor(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE node = findFloorNode(e);
return node != null ? node.key : null;
}
@Override
public CLASS_TYPE higher(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE node = findHigherNode(e);
return node != null ? node.key : null;
}
@Override
public CLASS_TYPE ceiling(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE node = findCeilingNode(e);
return node != null ? node.key : null;
}
#endif
@Override
public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) {
Objects.requireNonNull(action);
@ -1078,6 +1098,36 @@ public class AVL_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE
return highKeyOrNull(subHigher(e));
}
#if !TYPE_OBJECT
@Override
@Deprecated
public CLASS_TYPE lower(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE entry = subLower(e);
return entry == null ? null : KEY_TO_OBJ(entry.key);
}
@Override
@Deprecated
public CLASS_TYPE floor(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE entry = subFloor(e);
return entry == null ? null : KEY_TO_OBJ(entry.key);
}
@Override
@Deprecated
public CLASS_TYPE ceiling(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE entry = subCeiling(e);
return entry == null ? null : KEY_TO_OBJ(entry.key);
}
@Override
@Deprecated
public CLASS_TYPE higher(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE entry = subHigher(e);
return entry == null ? null : KEY_TO_OBJ(entry.key);
}
#endif
@Override
public KEY_TYPE POLL_FIRST_KEY() {
Entry KEY_GENERIC_TYPE entry = subLowest();

View File

@ -257,7 +257,12 @@ public class IMMUTABLE_HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERI
@Override
public boolean contains(Object o) {
#if TYPE_OBJECT
if(o == null) return containsNull;
#else
if(o == null) return false;
if(o instanceof CLASS_TYPE && KEY_EQUALS(CLASS_TO_KEY(o), EMPTY_KEY_VALUE)) return containsNull;
#endif
int pos = HashUtil.mix(o.hashCode()) & mask;
KEY_TYPE current = keys[pos];
if(KEY_EQUALS_NULL(current)) return false;

View File

@ -171,10 +171,10 @@ public interface NAVIGABLE_SET KEY_GENERIC_TYPE extends NavigableSet<CLASS_TYPE>
default CLASS_TYPE last() { return SORTED_SET.super.last(); }
@Override
@Deprecated
public default CLASS_TYPE pollFirst() { return KEY_TO_OBJ(POLL_FIRST_KEY()); }
public default CLASS_TYPE pollFirst() { return isEmpty() ? null : KEY_TO_OBJ(POLL_FIRST_KEY()); }
@Override
@Deprecated
public default CLASS_TYPE pollLast() { return KEY_TO_OBJ(POLL_LAST_KEY()); }
public default CLASS_TYPE pollLast() { return isEmpty() ? null : KEY_TO_OBJ(POLL_LAST_KEY()); }
@Override
@Deprecated

View File

@ -252,7 +252,12 @@ public class HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE imp
@Override
public boolean contains(Object o) {
#if TYPE_OBJECT
if(o == null) return containsNull;
#else
if(o == null) return false;
if(o instanceof CLASS_TYPE && KEY_EQUALS(CLASS_TO_KEY(o), EMPTY_KEY_VALUE)) return containsNull;
#endif
int pos = HashUtil.mix(o.hashCode()) & mask;
KEY_TYPE current = keys[pos];
if(KEY_EQUALS_NULL(current)) return false;
@ -265,7 +270,12 @@ public class HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE imp
@Override
public boolean remove(Object o) {
#if TYPE_OBJECT
if(o == null) return (containsNull ? removeNullIndex() : false);
#else
if(o == null) return false;
if(o instanceof CLASS_TYPE && KEY_EQUALS(CLASS_TO_KEY(o), EMPTY_KEY_VALUE)) return (containsNull ? removeNullIndex() : false);
#endif
int pos = HashUtil.mix(o.hashCode()) & mask;
KEY_TYPE current = keys[pos];
if(KEY_EQUALS_NULL(current)) return false;

View File

@ -210,6 +210,16 @@ public class RB_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE
@Override
public KEY_TYPE getDefaultMinValue() { return defaultMinNotFound; }
#else
/** only used for primitives
* @return null
*/
public CLASS_TYPE getDefaultMaxValue() { return null; }
/** only used for primitives
* @return null
*/
public CLASS_TYPE getDefaultMinValue() { return null; }
#endif
@Override
public boolean add(KEY_TYPE o) {
@ -263,43 +273,53 @@ public class RB_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE
@Override
public KEY_TYPE lower(KEY_TYPE e) {
Entry KEY_GENERIC_TYPE node = findLowerNode(e);
#if TYPE_OBJECT
return node != null ? node.key : null;
#else
return node != null ? node.key : defaultMinNotFound;
#endif
return node != null ? node.key : getDefaultMinValue();
}
@Override
public KEY_TYPE floor(KEY_TYPE e) {
Entry KEY_GENERIC_TYPE node = findFloorNode(e);
#if TYPE_OBJECT
return node != null ? node.key : null;
#else
return node != null ? node.key : defaultMinNotFound;
#endif
return node != null ? node.key : getDefaultMinValue();
}
@Override
public KEY_TYPE higher(KEY_TYPE e) {
Entry KEY_GENERIC_TYPE node = findHigherNode(e);
#if TYPE_OBJECT
return node != null ? node.key : null;
#else
return node != null ? node.key : defaultMaxNotFound;
#endif
return node != null ? node.key : getDefaultMaxValue();
}
@Override
public KEY_TYPE ceiling(KEY_TYPE e) {
Entry KEY_GENERIC_TYPE node = findCeilingNode(e);
#if TYPE_OBJECT
return node != null ? node.key : null;
#else
return node != null ? node.key : defaultMaxNotFound;
#endif
return node != null ? node.key : getDefaultMaxValue();
}
#if !TYPE_OBJECT
@Override
public CLASS_TYPE lower(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE node = findLowerNode(e);
return node != null ? node.key : null;
}
@Override
public CLASS_TYPE floor(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE node = findFloorNode(e);
return node != null ? node.key : null;
}
@Override
public CLASS_TYPE higher(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE node = findHigherNode(e);
return node != null ? node.key : null;
}
@Override
public CLASS_TYPE ceiling(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE node = findCeilingNode(e);
return node != null ? node.key : null;
}
#endif
@Override
public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) {
Objects.requireNonNull(action);
@ -1139,6 +1159,36 @@ public class RB_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE
return highKeyOrNull(subHigher(e));
}
#if !TYPE_OBJECT
@Override
@Deprecated
public CLASS_TYPE lower(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE entry = subLower(e);
return entry == null ? null : KEY_TO_OBJ(entry.key);
}
@Override
@Deprecated
public CLASS_TYPE floor(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE entry = subFloor(e);
return entry == null ? null : KEY_TO_OBJ(entry.key);
}
@Override
@Deprecated
public CLASS_TYPE ceiling(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE entry = subCeiling(e);
return entry == null ? null : KEY_TO_OBJ(entry.key);
}
@Override
@Deprecated
public CLASS_TYPE higher(CLASS_TYPE e) {
Entry KEY_GENERIC_TYPE entry = subHigher(e);
return entry == null ? null : KEY_TO_OBJ(entry.key);
}
#endif
@Override
public KEY_TYPE POLL_FIRST_KEY() {
Entry KEY_GENERIC_TYPE entry = subLowest();

View File

@ -12,6 +12,10 @@ import com.google.common.collect.testing.features.ListFeature;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import speiger.src.collections.ints.lists.ImmutableIntList;
import speiger.src.collections.ints.lists.IntArrayList;
import speiger.src.collections.ints.lists.IntLinkedList;
import speiger.src.collections.ints.lists.IntList;
import speiger.src.collections.objects.lists.ImmutableObjectList;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectLinkedList;
@ -25,19 +29,28 @@ public class ObjectListTests extends TestCase
suite.addTest(suite("ArrayList", T -> new ObjectArrayList<>(T)));
suite.addTest(suite("LinkedList", T -> new ObjectLinkedList<>(T)));
suite.addTest(immutableSuite("ImmutableList", T -> new ImmutableObjectList<>(T)));
suite.addTest(intSuite("IntArrayList", IntArrayList::new));
suite.addTest(intSuite("IntLinkedList", IntLinkedList::new));
suite.addTest(intImmutableSuite("IntImmutableList", ImmutableIntList::new));
return suite;
}
public static Test suite(String name, Function<String[], List<String>> factory)
{
public static Test intSuite(String name, Function<int[], IntList> factory) {
return ListTestSuiteBuilder.using(new TestIntListGenerator(factory)).named(name).withFeatures(ListFeature.GENERAL_PURPOSE, CollectionSize.ANY).createTestSuite();
}
public static Test intImmutableSuite(String name, Function<int[], IntList> factory) {
return ListTestSuiteBuilder.using(new TestIntListGenerator(factory)).named(name).withFeatures(CollectionSize.ANY).createTestSuite();
}
public static Test suite(String name, Function<String[], List<String>> factory) {
return ListTestSuiteBuilder.using(new TestStringListGenerator() {
@Override
protected List<String> create(String[] elements) { return factory.apply(elements); }
}).named(name).withFeatures(ListFeature.GENERAL_PURPOSE, CollectionFeature.ALLOWS_NULL_VALUES, CollectionSize.ANY).createTestSuite();
}
public static Test immutableSuite(String name, Function<String[], List<String>> factory)
{
public static Test immutableSuite(String name, Function<String[], List<String>> factory) {
return ListTestSuiteBuilder.using(new TestStringListGenerator() {
@Override
protected List<String> create(String[] elements) { return factory.apply(elements); }

View File

@ -0,0 +1,50 @@
package speiger.src.collections.objects.list;
import java.util.List;
import java.util.function.Function;
import com.google.common.collect.testing.SampleElements;
import com.google.common.collect.testing.SampleElements.Ints;
import com.google.common.collect.testing.TestListGenerator;
import speiger.src.collections.ints.lists.IntList;
@SuppressWarnings("javadoc")
public class TestIntListGenerator implements TestListGenerator<Integer>
{
Function<int[], IntList> function;
public TestIntListGenerator(Function<int[], IntList> function)
{
this.function = function;
}
@Override
public SampleElements<Integer> samples()
{
return new Ints();
}
@Override
public List<Integer> create(Object... elements)
{
int[] array = new int[elements.length];
int i = 0;
for(Object e : elements)
{
array[i++] = ((Integer)e).intValue();
}
return function.apply(array);
}
@Override
public Integer[] createArray(int length)
{
return new Integer[length];
}
@Override
public List<Integer> order(List<Integer> insertionOrder)
{
return insertionOrder;
}
}

View File

@ -1,13 +1,17 @@
package speiger.src.collections.objects.set;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import com.google.common.collect.testing.NavigableSetTestSuiteBuilder;
import com.google.common.collect.testing.SetTestSuiteBuilder;
import com.google.common.collect.testing.TestIntegerSetGenerator;
import com.google.common.collect.testing.TestStringSetGenerator;
import com.google.common.collect.testing.TestStringSortedSetGenerator;
import com.google.common.collect.testing.features.CollectionFeature;
@ -17,6 +21,17 @@ import com.google.common.collect.testing.features.SetFeature;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import speiger.src.collections.ints.sets.ImmutableIntOpenHashSet;
import speiger.src.collections.ints.sets.IntAVLTreeSet;
import speiger.src.collections.ints.sets.IntArraySet;
import speiger.src.collections.ints.sets.IntLinkedOpenCustomHashSet;
import speiger.src.collections.ints.sets.IntLinkedOpenHashSet;
import speiger.src.collections.ints.sets.IntNavigableSet;
import speiger.src.collections.ints.sets.IntOpenCustomHashSet;
import speiger.src.collections.ints.sets.IntOpenHashSet;
import speiger.src.collections.ints.sets.IntRBTreeSet;
import speiger.src.collections.ints.sets.IntSet;
import speiger.src.collections.ints.utils.IntStrategy;
import speiger.src.collections.objects.sets.ImmutableObjectOpenHashSet;
import speiger.src.collections.objects.sets.ObjectAVLTreeSet;
import speiger.src.collections.objects.sets.ObjectArraySet;
@ -30,9 +45,18 @@ import speiger.src.collections.objects.utils.ObjectStrategy;
@SuppressWarnings("javadoc")
public class ObjectSetTests extends TestCase
{
@SuppressWarnings("deprecation")
public static Test suite()
{
TestSuite suite = new TestSuite("Sets");
suite.addTest(intSuite("IntHashSet", IntOpenHashSet::new));
suite.addTest(intSuite("IntCustomHashSet", () -> new IntOpenCustomHashSet(IntegerStrategy.INSTANCE)));
suite.addTest(intSuite("IntLinkedHashSet", IntLinkedOpenHashSet::new));
suite.addTest(intSuite("IntLinkedCustomHashSet", () -> new IntLinkedOpenCustomHashSet(IntegerStrategy.INSTANCE)));
suite.addTest(intSuite("IntArraySet", IntArraySet::new));
suite.addTest(intImmutableSuite("ImmutableHashSet", ImmutableIntOpenHashSet::new));
suite.addTest(intNavigableSuite("IntRBTreeSet", IntRBTreeSet::new));
suite.addTest(intNavigableSuite("IntAVLTreeSet", IntAVLTreeSet::new));
suite.addTest(suite("HashSet", ObjectOpenHashSet::new, true));
suite.addTest(suite("LinkedHashSet", ObjectLinkedOpenHashSet::new, true));
suite.addTest(suite("CustomHashSet", T -> new ObjectOpenCustomHashSet<>(T, Strategy.INSTANCE), true));
@ -46,6 +70,37 @@ public class ObjectSetTests extends TestCase
return suite;
}
public static Test intSuite(String name, Supplier<IntSet> factory) {
SetTestSuiteBuilder<Integer> generator = SetTestSuiteBuilder.using(new TestIntegerSetGenerator() {
@Override
protected Set<Integer> create(Integer[] elements) {
IntSet set = factory.get();
set.addAll(Arrays.asList(elements));
return set;
}
}).named(name).withFeatures(CollectionSize.ANY, SetFeature.GENERAL_PURPOSE);
return generator.createTestSuite();
}
public static Test intNavigableSuite(String name, Supplier<IntNavigableSet> factory) {
SetTestSuiteBuilder<Integer> generator = NavigableSetTestSuiteBuilder.using(new TestIntegerNavigableSetGenerator() {
@Override
protected NavigableSet<Integer> create(Integer[] elements) {
IntNavigableSet set = factory.get();
set.addAll(Arrays.asList(elements));
return set;
}
}).named(name).withFeatures(CollectionSize.ANY, SetFeature.GENERAL_PURPOSE, CollectionFeature.SUBSET_VIEW);
return generator.createTestSuite();
}
public static Test intImmutableSuite(String name, Function<List<Integer>, IntSet> factory) {
return SetTestSuiteBuilder.using(new TestIntegerSetGenerator() {
@Override
protected Set<Integer> create(Integer[] elements) { return factory.apply(Arrays.asList(elements)); }
}).named(name).withFeatures(CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_QUERIES).createTestSuite();
}
public static Test suite(String name, Function<String[], Set<String>> factory, boolean allowNull) {
SetTestSuiteBuilder<String> generator = SetTestSuiteBuilder.using(new TestStringSetGenerator() {
@Override
@ -84,4 +139,19 @@ public class ObjectSetTests extends TestCase
return Objects.equals(key, value);
}
}
private static class IntegerStrategy implements IntStrategy {
static final IntegerStrategy INSTANCE = new IntegerStrategy();
@Override
public int hashCode(int o) {
return Integer.hashCode(o);
}
@Override
public boolean equals(int key, int value) {
return key == value;
}
}
}

View File

@ -0,0 +1,32 @@
package speiger.src.collections.objects.set;
import java.util.SortedSet;
import com.google.common.collect.testing.TestIntegerSortedSetGenerator;
import com.google.common.collect.testing.TestSortedSetGenerator;
@SuppressWarnings("javadoc")
public abstract class TestIntegerNavigableSetGenerator extends TestIntegerSortedSetGenerator implements TestSortedSetGenerator<Integer>
{
@Override
protected abstract SortedSet<Integer> create(Integer[] elements);
@Override
public SortedSet<Integer> create(Object... elements) {
Integer[] array = new Integer[elements.length];
int i = 0;
for (Object e : elements) {
array[i++] = (Integer) e;
}
return create(array);
}
@Override
public Integer belowSamplesLesser() { return -2; }
@Override
public Integer belowSamplesGreater() { return -1; }
@Override
public Integer aboveSamplesLesser() { return 5; }
@Override
public Integer aboveSamplesGreater() { return 6; }
}