package org.codefx.libfx.collection.transform;
import static org.junit.Assert.assertEquals;
import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.BiPredicate;
import java.util.function.ToIntFunction;
import junit.framework.JUnit4TestAdapter;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.junit.Before;
import com.google.common.collect.testing.MapTestSuiteBuilder;
import com.google.common.collect.testing.SampleElements;
import com.google.common.collect.testing.TestMapGenerator;
import com.google.common.collect.testing.features.CollectionFeature;
import com.google.common.collect.testing.features.CollectionSize;
import com.google.common.collect.testing.features.Feature;
import com.google.common.collect.testing.features.MapFeature;
/**
* Tests {@link EqualityTransformingMap}.
*/
public class EqualityTransformingMapTest {
/**
* JUnit-3-style method to create the tests run for this class.
*
* @return the tests to run
*/
public static Test suite() {
TestSuite suite = new TestSuite("org.codefx.libfx.collection.transform.EqualityTransformingMap");
suite.addTest(originalEquality());
suite.addTest(lengthBasedEquality());
return suite;
}
private static Feature<?>[] features() {
return new Feature<?>[] {
// since 'EqualityTransformingMap' passes all calls along,
// the features are determined by the backing data structure (which is a 'HashMap')
CollectionSize.ANY,
MapFeature.ALLOWS_ANY_NULL_QUERIES,
MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
MapFeature.SUPPORTS_PUT,
MapFeature.SUPPORTS_REMOVE,
CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
};
}
/**
* Creates a test which uses hashCode and equals of the original keys.
*
* @return the test case
*/
private static Test originalEquality() {
return MapTestSuiteBuilder
.using(new TransformingMapGenerator(String::equals, String::hashCode))
.named("original equality and hashCode")
.withFeatures(features())
.createTestSuite();
}
/**
* Creates a test which uses hashCode and equals based on the string's lengths.
*
* @return the test case
*/
private static Test lengthBasedEquality() {
BiPredicate<String, String> equals = (s1, s2) -> s1.length() == s2.length();
ToIntFunction<String> hash = s -> s.length();
Test generalTests = MapTestSuiteBuilder
.using(new TransformingMapGenerator(equals, hash))
.named("length-based equality and hashCode - general tests")
.withFeatures(features())
.createTestSuite();
TestSuite specificTests = new TestSuite("length-based equality and hashCode - specific tests");
specificTests.addTest(new JUnit4TestAdapter(LengthBasedEqualityAndHashCodeTests.class));
TestSuite tests = new TestSuite("length-based equality and hashCode");
tests.addTest(generalTests);
tests.addTest(specificTests);
return tests;
}
/**
* Tests {@link EqualityTransformingMap} with a specific set of tests geared towards its special functionality, i.e.
* transforming equals and hashCode.
*/
public static class LengthBasedEqualityAndHashCodeTests {
private Map<String, Integer> testedMap;
private final BiPredicate<String, String> equals = (s1, s2) -> s1.length() == s2.length();
private final ToIntFunction<String> hash = s -> s.length();
@Before
@SuppressWarnings("javadoc")
public void createMap() {
testedMap = EqualityTransformingCollectionBuilder
.forType(String.class)
.withEquals(equals)
.withHash(hash)
.buildMap();
}
@org.junit.Test
@SuppressWarnings("javadoc")
public void put_getWithSameLengthKey_exists() {
Integer associatedValue = 1000;
testedMap.put("aaa", associatedValue);
assertEquals(associatedValue, testedMap.get("bbb"));
}
}
private static class TransformingMapGenerator implements TestMapGenerator<String, Integer> {
private final BiPredicate<String, String> equals;
private final ToIntFunction<String> hash;
public TransformingMapGenerator(BiPredicate<String, String> equals, ToIntFunction<String> hash) {
this.equals = equals;
this.hash = hash;
}
@Override
public SampleElements<Entry<String, Integer>> samples() {
return new SampleElements<Entry<String, Integer>>(
new SimpleEntry<>("A", 1),
new SimpleEntry<>("AA", 2),
new SimpleEntry<>("AAA", 3),
new SimpleEntry<>("AAAA", 4),
new SimpleEntry<>("AAAAA", 5));
}
@Override
@SuppressWarnings("unchecked")
public Entry<String, Integer>[] createArray(int length) {
return new Entry[length];
}
@Override
public String[] createKeyArray(int length) {
return new String[length];
}
@Override
public Integer[] createValueArray(int length) {
return new Integer[length];
}
@Override
public Iterable<Entry<String, Integer>> order(List<Entry<String, Integer>> insertionOrder) {
return insertionOrder;
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Integer> create(Object... entries) {
Map<String, Integer> transformingMap = EqualityTransformingCollectionBuilder
.forType(String.class)
.withEquals(equals)
.withHash(hash)
.buildMap();
Arrays.stream(entries)
.map(entry -> (Entry<String, Integer>) entry)
.forEach(entry -> transformingMap.put(entry.getKey(), entry.getValue()));
return transformingMap;
}
}
}