Skip to content

Commit

Permalink
implemented withFactory option in collection contract
Browse files Browse the repository at this point in the history
  • Loading branch information
maciejmikosik committed Apr 6, 2015
2 parents af92071 + 276dfd8 commit 407cafd
Show file tree
Hide file tree
Showing 17 changed files with 564 additions and 114 deletions.
1 change: 1 addition & 0 deletions .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
<classpathentry kind="lib" path="main/jar/junit-4.12.jar" sourcepath="main/jar/junit-4.12-sources.jar"/>

<classpathentry kind="src" path="test/java"/>
<classpathentry kind="lib" path="test/jar/guava-18.0.jar" sourcepath="test/jar/guava-18.0-sources.jar"/>
</classpath>
33 changes: 0 additions & 33 deletions main/java/org/quackery/contract/collection/Assumptions.java

This file was deleted.

101 changes: 66 additions & 35 deletions main/java/org/quackery/contract/collection/CollectionContract.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,30 @@
import static org.quackery.contract.collection.Flags.clean;
import static org.quackery.contract.collection.Flags.onlyIf;
import static org.quackery.contract.collection.suite.CollectionMutableSuite.clearRemovesElement;
import static org.quackery.contract.collection.suite.CollectionSuite.copyConstructorIsDeclared;
import static org.quackery.contract.collection.suite.CollectionSuite.copyConstructorIsPublic;
import static org.quackery.contract.collection.suite.CollectionSuite.creatorCanCreateCollectionWithOneElement;
import static org.quackery.contract.collection.suite.CollectionSuite.creatorDoesNotModifyArgument;
import static org.quackery.contract.collection.suite.CollectionSuite.creatorFailsForNullArgument;
import static org.quackery.contract.collection.suite.CollectionSuite.creatorMakesDefensiveCopy;
import static org.quackery.contract.collection.suite.CollectionSuite.defaultConstructorCreatesEmptyCollection;
import static org.quackery.contract.collection.suite.CollectionSuite.defaultConstructorIsDeclared;
import static org.quackery.contract.collection.suite.CollectionSuite.defaultConstructorIsPublic;
import static org.quackery.contract.collection.suite.CollectionSuite.instantiatorCanCreateCollectionWithOneElement;
import static org.quackery.contract.collection.suite.CollectionSuite.instantiatorDoesNotModifyArgument;
import static org.quackery.contract.collection.suite.CollectionSuite.instantiatorFailsForNullArgument;
import static org.quackery.contract.collection.suite.CollectionSuite.instantiatorIsDeclared;
import static org.quackery.contract.collection.suite.CollectionSuite.instantiatorIsPublic;
import static org.quackery.contract.collection.suite.CollectionSuite.instantiatorMakesDefensiveCopy;
import static org.quackery.contract.collection.suite.CollectionSuite.instantiatorReturnsCollection;
import static org.quackery.contract.collection.suite.CollectionSuite.factoryIsDeclared;
import static org.quackery.contract.collection.suite.CollectionSuite.factoryIsPublic;
import static org.quackery.contract.collection.suite.CollectionSuite.factoryIsStatic;
import static org.quackery.contract.collection.suite.CollectionSuite.factoryReturnsCollection;
import static org.quackery.contract.collection.suite.CollectionSuite.implementsCollectionInterface;
import static org.quackery.contract.collection.suite.CollectionSuite.isEmptyReturnsFalseIfCollectionHasOneElement;
import static org.quackery.contract.collection.suite.CollectionSuite.isEmptyReturnsTrueIfCollectionIsEmpty;
import static org.quackery.contract.collection.suite.CollectionSuite.sizeReturnsOneIfCollectionHasOneElement;
import static org.quackery.contract.collection.suite.CollectionSuite.sizeReturnsZeroIfCollectionIsEmpty;
import static org.quackery.contract.collection.suite.ListMutableSuite.addAddsElementAtTheEnd;
import static org.quackery.contract.collection.suite.ListMutableSuite.addReturnsTrue;
import static org.quackery.contract.collection.suite.ListSuite.cretorStoresAllElementsInOrder;
import static org.quackery.contract.collection.suite.ListSuite.getFailsForIndexAboveBound;
import static org.quackery.contract.collection.suite.ListSuite.getFailsForIndexBelowBound;
import static org.quackery.contract.collection.suite.ListSuite.getReturnsEachElement;
import static org.quackery.contract.collection.suite.ListSuite.instantiatorStoresAllElementsInOrder;

import java.util.Collection;
import java.util.List;
Expand All @@ -34,57 +38,74 @@
public final class CollectionContract implements Contract<Class<?>> {
private final Class<?> supertype;
private final boolean mutable;
private final String factory;

private CollectionContract(Class<?> supertype, boolean mutable) {
private CollectionContract(Class<?> supertype) {
this.supertype = supertype;
mutable = false;
factory = null;
}

private CollectionContract(Class<?> supertype, boolean mutable, String factory) {
this.supertype = supertype;
this.mutable = mutable;
this.factory = factory;
}

public static CollectionContract collectionContract(Class<Collection> supertype,
Collection<?>... erasure) {
return new CollectionContract(supertype, false);
return new CollectionContract(supertype);
}

public static CollectionContract collectionContract(Class<List> supertype, List<?>... erasure) {
return new CollectionContract(supertype, false);
return new CollectionContract(supertype);
}

public Test test(Class<?> type) {
boolean isList = supertype == List.class;
boolean hasConstructor = factory == null;
boolean hasFactory = factory != null;
Creator creator = hasConstructor
? new ConstructorCreator(type)
: new FactoryCreator(type, factory);
return clean(suite(name(type))
.test(suite("quacks like Collection")
.test(suite("provides default constructor")
.test(onlyIf(hasConstructor, implementsCollectionInterface(type)))
.test(onlyIf(hasConstructor, suite("provides default constructor")
.test(defaultConstructorIsDeclared(type))
.test(defaultConstructorIsPublic(type))
.test(defaultConstructorCreatesEmptyCollection(type)))
.test(suite("provides copy constructor")
.test(instantiatorIsDeclared(type))
.test(instantiatorIsPublic(type))
.test(instantiatorReturnsCollection(type))
.test(instantiatorCanCreateCollectionWithOneElement(type))
.test(instantiatorFailsForNullArgument(type))
.test(instantiatorMakesDefensiveCopy(type))
.test(instantiatorDoesNotModifyArgument(type)))
.test(defaultConstructorCreatesEmptyCollection(type))))
.test(suite("provides " + name(creator))
.test(onlyIf(hasConstructor, copyConstructorIsDeclared(type)))
.test(onlyIf(hasConstructor, copyConstructorIsPublic(type)))
.test(onlyIf(hasFactory, factoryIsDeclared(type, factory)))
.test(onlyIf(hasFactory, factoryIsPublic(type, factory)))
.test(onlyIf(hasFactory, factoryIsStatic(type, factory)))
.test(onlyIf(hasFactory, factoryReturnsCollection(type, factory)))
.test(creatorCanCreateCollectionWithOneElement(creator))
.test(creatorFailsForNullArgument(creator))
.test(creatorMakesDefensiveCopy(creator))
.test(creatorDoesNotModifyArgument(creator)))
.test(suite("overrides size")
.test(sizeReturnsZeroIfCollectionIsEmpty(type))
.test(sizeReturnsOneIfCollectionHasOneElement(type)))
.test(sizeReturnsZeroIfCollectionIsEmpty(creator))
.test(sizeReturnsOneIfCollectionHasOneElement(creator)))
.test(suite("overrides isEmpty")
.test(isEmptyReturnsFalseIfCollectionHasOneElement(type))
.test(isEmptyReturnsTrueIfCollectionIsEmpty(type))))
.test(isEmptyReturnsFalseIfCollectionHasOneElement(creator))
.test(isEmptyReturnsTrueIfCollectionIsEmpty(creator))))
.test(onlyIf(mutable, suite("quacks like mutable collection")
.test(suite("overrides clear")
.test(clearRemovesElement(type)))))
.test(clearRemovesElement(creator)))))
.test(onlyIf(isList, suite("quacks like list")
.test(suite("provides copy constructor")
.test(instantiatorStoresAllElementsInOrder(type)))
.test(suite("provides " + name(creator))
.test(cretorStoresAllElementsInOrder(creator)))
.test(suite("overrides get")
.test(getReturnsEachElement(type))
.test(getFailsForIndexAboveBound(type))
.test(getFailsForIndexBelowBound(type)))))
.test(getReturnsEachElement(creator))
.test(getFailsForIndexAboveBound(creator))
.test(getFailsForIndexBelowBound(creator)))))
.test(onlyIf(isList && mutable, suite("quacks like mutable list")
.test(suite("overrides add")
.test(addAddsElementAtTheEnd(type))
.test(addReturnsTrue(type))))));
.test(addAddsElementAtTheEnd(creator))
.test(addReturnsTrue(creator))))));
}

private String name(Class<?> type) {
Expand All @@ -97,7 +118,17 @@ private String name(Class<?> type) {
return builder.toString();
}

public Contract<Class<?>> mutable() {
return new CollectionContract(supertype, true);
private static String name(Creator creator) {
return creator instanceof ConstructorCreator
? "copy constructor"
: "factory method";
}

public CollectionContract mutable() {
return new CollectionContract(supertype, true, factory);
}

public CollectionContract withFactory(String factoryMethodName) {
return new CollectionContract(supertype, mutable, factoryMethodName);
}
}
26 changes: 26 additions & 0 deletions main/java/org/quackery/contract/collection/ConstructorCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.quackery.contract.collection;

import static org.quackery.AssumptionException.assume;

import java.lang.reflect.Constructor;
import java.util.Collection;

import org.quackery.AssumptionException;

public class ConstructorCreator implements Creator {
private final Class<?> type;

public ConstructorCreator(Class<?> type) {
this.type = type;
}

public <T> T create(Class<T> cast, Object original) throws ReflectiveOperationException {
assume(cast.isAssignableFrom(type));
try {
Constructor<?> constructor = type.getConstructor(Collection.class);
return cast.cast(constructor.newInstance(original));
} catch (NoSuchMethodException e) {
throw new AssumptionException();
}
}
}
5 changes: 5 additions & 0 deletions main/java/org/quackery/contract/collection/Creator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.quackery.contract.collection;

public interface Creator {
<T> T create(Class<T> cast, Object original) throws ReflectiveOperationException;
}
30 changes: 30 additions & 0 deletions main/java/org/quackery/contract/collection/FactoryCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.quackery.contract.collection;

import static org.quackery.AssumptionException.assume;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;

import org.quackery.AssumptionException;

public class FactoryCreator implements Creator {
private final Class<?> type;
private final String methodName;

public FactoryCreator(Class<?> type, String methodName) {
this.type = type;
this.methodName = methodName;
}

public <T> T create(Class<T> cast, Object original) throws ReflectiveOperationException {
try {
Method method = type.getMethod(methodName, Collection.class);
assume(cast.isAssignableFrom(method.getReturnType()));
assume(Modifier.isStatic(method.getModifiers()));
return cast.cast(method.invoke(null, original));
} catch (NoSuchMethodException e) {
throw new AssumptionException();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package org.quackery.contract.collection.suite;

import static org.quackery.AssertionException.assertEquals;
import static org.quackery.contract.collection.Assumptions.assumeCreateCollection;
import static org.quackery.contract.collection.Collections.newArrayList;
import static org.quackery.contract.collection.Element.a;

import java.util.Collection;

import org.quackery.Case;
import org.quackery.Test;
import org.quackery.contract.collection.Creator;

public class CollectionMutableSuite {
public static Test clearRemovesElement(final Class<?> type) {
public static Test clearRemovesElement(final Creator creator) {
return new Case("empties collection if it has 1 element") {
public void run() throws Throwable {
Collection<?> collection = assumeCreateCollection(type, newArrayList(a));
Collection<?> collection = creator.create(Collection.class, newArrayList(a));
collection.clear();
assertEquals(collection.toArray(), new Object[] {});
}
Expand Down
Loading

0 comments on commit 407cafd

Please sign in to comment.