/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* 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.badlogic.gdx.tests;
import java.util.Iterator;
import java.util.Random;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.tests.utils.GdxTest;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ArrayMap;
import com.badlogic.gdx.utils.BooleanArray;
import com.badlogic.gdx.utils.ByteArray;
import com.badlogic.gdx.utils.CharArray;
import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.IdentityMap;
import com.badlogic.gdx.utils.IntArray;
import com.badlogic.gdx.utils.IntFloatMap;
import com.badlogic.gdx.utils.IntIntMap;
import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.IntSet;
import com.badlogic.gdx.utils.LongArray;
import com.badlogic.gdx.utils.LongMap;
import com.badlogic.gdx.utils.ObjectFloatMap;
import com.badlogic.gdx.utils.ObjectIntMap;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.ObjectSet;
import com.badlogic.gdx.utils.OrderedMap;
import com.badlogic.gdx.utils.OrderedSet;
import com.badlogic.gdx.utils.ShortArray;
import com.badlogic.gdx.utils.SnapshotArray;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import com.badlogic.gdx.utils.reflect.Constructor;
import com.badlogic.gdx.utils.reflect.Method;
/** Tests for the collection classes. Currently, only equals() and hashCode() methods are tested. */
public class CollectionsTest extends GdxTest {
// Objects to use for test keys/values; no duplicates may exist. All arrays are 10 elements.
private Object[] values = {"just", "some", "random", "values", true, false, 50, "nope", "yeah", 53};
private Object[] valuesWithNulls = {"just", "some", null, "values", true, false, 50, "nope", "yeah", 53};
private Integer[] intValues = {42, 13, 0, -44, 56, 561, 61, -532, -1, 32};
private Float[] floatValues = {4f, 3.14f, 0f, 5f, 2f, -5f, 43f, 643f, 3525f, 32f};
private Long[] longValues = {5L, 3L, 41432L, 0L, -4312L, -532L, 1L, 4L, 1362L};
private Byte[] byteValues = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
private Short[] shortValues = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
private Character[] charValues = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};
/** Checks that the two values are equal, and that their hashcodes are equal. */
private void assertEquals (Object a, Object b) {
if (!a.equals(b)) throw new GdxRuntimeException("equals() failed: " + a + " != " + b);
if (!b.equals(a)) throw new GdxRuntimeException("equals() failed (not symmetric): " + b + " != " + a);
if (a.hashCode() != b.hashCode()) throw new GdxRuntimeException("hashCode() failed: " + a + " != " + b);
}
/** Checks that the two values are not equal, and emits a warning if their hashcodes are equal. */
private void assertNotEquals (Object a, Object b) {
if (a.equals(b)) throw new GdxRuntimeException("!equals() failed: " + a + " == " + b);
if (b.equals(a)) throw new GdxRuntimeException("!equals() failed (not symmetric): " + b + " == " + a);
if (a.hashCode() == b.hashCode()) System.out.println("Warning: hashCode() may be incorrect: " + a + " == " + b);
}
/** Uses reflection to create a new instance of the given type. */
private Object newInstance (Class<?> clazz) {
try {
return ClassReflection.newInstance(clazz);
} catch (Throwable ex) {
throw new GdxRuntimeException(ex);
}
}
private void invoke (String methodName, Object object, Object... args) {
try {
Method theMethod = null;
for (Method method : ClassReflection.getMethods(object.getClass())) {
if (methodName.equals(method.getName()) && method.getParameterTypes().length == args.length) {
theMethod = method;
break;
}
}
theMethod.invoke(object, args);
} catch (Throwable ex) {
throw new GdxRuntimeException(ex);
}
}
private void set (String fieldName, Object object, Object value) {
try {
ClassReflection.getField(object.getClass(), fieldName).set(object, value);
} catch (Throwable ex) {
throw new GdxRuntimeException(ex);
}
}
private Object copy(Object object) {
try {
Constructor theConstructor = null;
for (Constructor constructor : ClassReflection.getConstructors(object.getClass())) {
if (constructor.getParameterTypes().length == 1
&& ClassReflection.isAssignableFrom(constructor.getParameterTypes()[0], object.getClass())) {
theConstructor = constructor;
break;
}
}
return theConstructor.newInstance(object);
} catch (Throwable ex) {
throw new GdxRuntimeException(ex);
}
}
private void testMap (Class<?> mapClass, Object[] keys, Object[] values) {
System.out.println(mapClass);
Object map = newInstance(mapClass);
Object otherMap = newInstance(mapClass);
assertEquals(map, map);
for (int i = 0, n = keys.length; i < n; ++i) {
Object anotherMap = copy(map);
assertEquals(map, anotherMap);
invoke("put", map, keys[i], values[i]);
invoke("put", otherMap, keys[i], values[i]);
assertEquals(map, otherMap);
assertNotEquals(map, anotherMap);
invoke("put", anotherMap, keys[(i + 1) % keys.length], values[i]);
assertNotEquals(map, anotherMap);
}
// perform an iteration test
Object anotherMap = copy(map);
Iterator it = ((Iterable) anotherMap).iterator();
int iterationCount = 0;
while (it.hasNext()) {
Object entry = it.next();
iterationCount++;
}
assertEquals(iterationCount, keys.length);
// perform an iteration and remove test for every index
for (int i = 0, n = keys.length; i < n; ++i) {
anotherMap = copy(map);
it = ((Iterable) anotherMap).iterator();
iterationCount = 0;
while (it.hasNext()) {
Object entry = it.next();
if (iterationCount == i) {
it.remove();
}
iterationCount++;
}
assertEquals(iterationCount, keys.length);
}
}
private void testArray (Class<?> arrayClass, Object[] values) {
System.out.println(arrayClass);
Object array = newInstance(arrayClass);
for (int i = 0; i < values.length; i++)
invoke("add", array, values[i]);
Object otherArray = newInstance(arrayClass);
for (int i = 0; i < values.length; i++)
invoke("add", otherArray, values[i]);
assertEquals(array, otherArray);
Object unorderedArray = newInstance(arrayClass);
set("ordered", unorderedArray, false);
Object otherUnorderedArray = newInstance(arrayClass);
set("ordered", otherUnorderedArray, false);
assertEquals(unorderedArray, unorderedArray);
assertNotEquals(unorderedArray, otherUnorderedArray);
}
private void testSet (Class<?> setClass, Object[] values) {
System.out.println(setClass);
Object set = newInstance(setClass);
for (int i = 0, n = values.length; i < n; ++i)
invoke("add", set, values[i]);
Object otherSet = newInstance(setClass);
for (int i = 0, n = values.length; i < n; ++i)
invoke("add", otherSet, values[i]);
Object thirdSet = newInstance(setClass);
for (int i = 0, n = values.length; i < n; i++)
invoke("add", thirdSet, values[n - i - 1]);
assertEquals(set, set);
assertEquals(set, otherSet);
assertEquals(set, thirdSet);
assertEquals(otherSet, set);
assertEquals(otherSet, otherSet);
assertEquals(otherSet, thirdSet);
assertEquals(thirdSet, set);
assertEquals(thirdSet, otherSet);
assertEquals(thirdSet, thirdSet);
}
public void create () {
testMap(ArrayMap.class, values, valuesWithNulls);
testMap(IdentityMap.class, values, valuesWithNulls);
testMap(IntFloatMap.class, intValues, floatValues);
testMap(IntIntMap.class, intValues, intValues);
testMap(IntMap.class, intValues, valuesWithNulls);
testMap(LongMap.class, longValues, valuesWithNulls);
testMap(ObjectFloatMap.class, values, floatValues);
testMap(ObjectIntMap.class, values, intValues);
testMap(ObjectMap.class, values, valuesWithNulls);
testMap(OrderedMap.class, values, valuesWithNulls);
testArray(Array.class, valuesWithNulls);
testArray(BooleanArray.class, new Boolean[] {true, false});
testArray(ByteArray.class, byteValues);
testArray(CharArray.class, charValues);
testArray(FloatArray.class, floatValues);
testArray(IntArray.class, intValues);
testArray(LongArray.class, longValues);
testArray(ShortArray.class, shortValues);
testArray(SnapshotArray.class, values);
testSet(IntSet.class, intValues);
testSet(ObjectSet.class, values);
testSet(OrderedSet.class, values);
System.out.println("Success!");
}
}