package org.codefx.libfx.collection.transform;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.codefx.libfx.collection.transform.ElementTypes.Cat;
import org.codefx.libfx.collection.transform.ElementTypes.Feline;
import org.codefx.libfx.collection.transform.ElementTypes.Mammal;
import com.google.common.collect.testing.SampleElements;
import com.google.common.collect.testing.SetTestSuiteBuilder;
import com.google.common.collect.testing.TestSetGenerator;
import com.google.common.collect.testing.features.CollectionFeature;
import com.google.common.collect.testing.features.CollectionSize;
import com.google.common.collect.testing.features.Feature;
/**
* Tests {@link TransformingSet}.
*/
public class TransformingSetTest {
/**
* 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.TransformingSet");
suite.addTest(backingSetHasSupertype());
suite.addTest(backingSetHasSubtype());
return suite;
}
private static Feature<?>[] features() {
return new Feature<?>[] {
// since 'TransformedSet' passes all calls along,
// the features are determined by the backing data structure (which is a 'HashSet')
CollectionSize.ANY,
CollectionFeature.ALLOWS_NULL_VALUES, // includes ALLOWS_NULL_QUERIES
CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
CollectionFeature.SUPPORTS_ADD,
CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
CollectionFeature.SUPPORTS_REMOVE,
};
}
/**
* Creates a test for a feline set which us backed by a mammal set (i.e. a supertype).
*
* @return the test case
*/
private static Test backingSetHasSupertype() {
return SetTestSuiteBuilder
.using(new TransformingSetGenerator(Mammal.class))
.named("backed by supertype")
.withFeatures(features())
.createTestSuite();
}
/**
* Creates a test for a feline set which us backed by a cat set (i.e. a subtype).
*
* @return the test case
*/
private static Test backingSetHasSubtype() {
return SetTestSuiteBuilder
.using(new TransformingSetGenerator(Cat.class))
.named("backed by subtype")
.withFeatures(features())
.createTestSuite();
}
private static class TransformingSetGenerator implements TestSetGenerator<Feline> {
private final Class<?> backingSetGenericType;
public TransformingSetGenerator(Class<?> backingSetGenericType) {
this.backingSetGenericType = backingSetGenericType;
}
@Override
public SampleElements<Feline> samples() {
return new SampleElements<Feline>(
new Feline("A"), new Feline("B"), new Feline("C"), new Feline("D"), new Feline("E"));
}
@Override
public Feline[] createArray(int length) {
return new Feline[length];
}
@Override
public Iterable<Feline> order(List<Feline> insertionOrder) {
return insertionOrder;
}
@Override
public Set<Feline> create(Object... elements) {
if (backingSetGenericType.equals(Mammal.class))
return createBackedByMammalSet(elements);
if (backingSetGenericType.equals(Cat.class))
return createBackedByCatSet(elements);
throw new UnsupportedOperationException();
}
private static Set<Feline> createBackedByMammalSet(Object[] felines) {
Set<Mammal> mammals = new HashSet<>();
for (Object feline : felines)
if (feline == null)
mammals.add(null);
else {
String name = ((Feline) feline).getName();
mammals.add(new Mammal(name));
}
return new TransformingSet<>(
mammals,
/*
* Because 'Feline' does not uphold the Liskov Substitution Principle (by having its own 'toString'
* method) felines can not masquerade as mammals. Hence create a new mammal for each feline.
*/
Mammal.class, Feline.class,
mammal -> new Feline(mammal.getName()), feline -> new Mammal(feline.getName()));
}
private static Set<Feline> createBackedByCatSet(Object[] felines) {
Set<Cat> cats = new HashSet<>();
for (Object feline : felines)
if (feline == null)
cats.add(null);
else {
String name = ((Feline) feline).getName();
cats.add(new Cat(name));
}
return new TransformingSet<>(
cats,
/*
* Because 'Cat' does not uphold the Liskov Substitution Principle (by having its own 'toString'
* method) cats can not masquerade as felines. Hence create a new feline for each cat.
*/
Cat.class, Feline.class,
cat -> new Feline(cat.getName()), feline -> new Cat(feline.getName()));
}
}
}