Skip to content

Commit

Permalink
reorganized test hierarchy in collection contract
Browse files Browse the repository at this point in the history
  • Loading branch information
maciejmikosik committed Apr 4, 2015
2 parents f72faea + 534ea2c commit 1399ded
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 107 deletions.
75 changes: 58 additions & 17 deletions main/java/org/quackery/contract/collection/CollectionContract.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
package org.quackery.contract.collection;

import static org.quackery.Suite.suite;
import static org.quackery.contract.collection.suite.CollectionMutableSuite.collectionMutableSuite;
import static org.quackery.contract.collection.suite.CollectionSuite.collectionSuite;
import static org.quackery.contract.collection.suite.ListMutableSuite.listMutableSuite;
import static org.quackery.contract.collection.suite.ListSuite.listSuite;
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.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.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.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.ArrayList;
import java.util.Collection;
import java.util.List;

Expand All @@ -32,18 +50,41 @@ public static CollectionContract collectionContract(Class<List> supertype, List<
}

public Test test(Class<?> type) {
List<Test> suites = new ArrayList<>();
suites.add(collectionSuite(type));
if (mutable) {
suites.add(collectionMutableSuite(type));
}
if (supertype == List.class) {
suites.add(listSuite(type));
}
if (supertype == List.class && mutable) {
suites.add(listMutableSuite(type));
}
return suite(name(type)).testAll(suites);
boolean isList = supertype == List.class;
return clean(suite(name(type))
.test(suite("quacks like Collection")
.test(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(suite("overrides size")
.test(sizeReturnsZeroIfCollectionIsEmpty(type))
.test(sizeReturnsOneIfCollectionHasOneElement(type)))
.test(suite("overrides isEmpty")
.test(isEmptyReturnsFalseIfCollectionHasOneElement(type))
.test(isEmptyReturnsTrueIfCollectionIsEmpty(type))))
.test(onlyIf(mutable, suite("quacks like mutable collection")
.test(suite("overrides clear")
.test(clearRemovesElement(type)))))
.test(onlyIf(isList, suite("quacks like list")
.test(suite("provides copy constructor")
.test(instantiatorStoresAllElementsInOrder(type)))
.test(suite("overrides get")
.test(getReturnsEachElement(type))
.test(getFailsForIndexAboveBound(type))
.test(getFailsForIndexBelowBound(type)))))
.test(onlyIf(isList && mutable, suite("quacks like mutable list")
.test(suite("overrides add")
.test(addAddsElementAtTheEnd(type))
.test(addReturnsTrue(type))))));
}

private String name(Class<?> type) {
Expand Down
39 changes: 39 additions & 0 deletions main/java/org/quackery/contract/collection/Flags.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.quackery.contract.collection;

import static org.quackery.Suite.suite;

import java.util.ArrayList;
import java.util.List;

import org.quackery.Case;
import org.quackery.Suite;
import org.quackery.Test;

public class Flags {
public static Test onlyIf(boolean condition, Test test) {
return condition
? test
: suite("");
}

public static Test clean(Test test) {
return test instanceof Case
? test
: clean((Suite) test);
}

private static Test clean(Suite suite) {
List<Test> cleanedChildren = new ArrayList<>();
for (Test child : suite.tests) {
Test cleanChild = clean(child);
if (!isEmptySuite(cleanChild)) {
cleanedChildren.add(cleanChild);
}
}
return suite(suite.name).testAll(cleanedChildren);
}

private static boolean isEmptySuite(Test test) {
return test instanceof Suite && ((Suite) test).tests.isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.quackery.contract.collection.suite;

import static org.quackery.AssertionException.assertEquals;
import static org.quackery.Suite.suite;
import static org.quackery.contract.collection.Assumptions.assumeCreateCollection;
import static org.quackery.contract.collection.Collections.newArrayList;
import static org.quackery.contract.collection.Element.a;
Expand All @@ -12,14 +11,8 @@
import org.quackery.Test;

public class CollectionMutableSuite {
public static Test collectionMutableSuite(Class<?> type) {
return suite("quacks like mutable collection")
.test(suite("overrides clear")
.test(clearRemovesElement(type)));
}

private static Test clearRemovesElement(final Class<?> type) {
return new Case("clear empties collection if it has 1 element") {
public static Test clearRemovesElement(final Class<?> type) {
return new Case("empties collection if it has 1 element") {
public void run() throws Throwable {
Collection<?> collection = assumeCreateCollection(type, newArrayList(a));
collection.clear();
Expand Down
107 changes: 57 additions & 50 deletions main/java/org/quackery/contract/collection/suite/CollectionSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,63 +4,49 @@
import static org.quackery.AssertionException.assertThat;
import static org.quackery.AssertionException.fail;
import static org.quackery.AssumptionException.assume;
import static org.quackery.Suite.suite;
import static org.quackery.contract.collection.Assumptions.assumeConstructor;
import static org.quackery.contract.collection.Assumptions.assumeCreateCollection;
import static org.quackery.contract.collection.Collections.copy;
import static org.quackery.contract.collection.Collections.newArrayList;
import static org.quackery.contract.collection.Element.a;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;

import org.quackery.Case;
import org.quackery.Test;

public class CollectionSuite {
public static Test collectionSuite(Class<?> type) {
return suite("quacks like Collection")
.test(implementsCollectionInterface(type))
.test(suite("provides default constructor")
.test(hasDefaultConstructor(type))
.test(defaultConstructorCreatesEmptyCollection(type)))
.test(suite("provides copy constructor")
.test(hasCopyConstructor(type))
.test(copyConstructorCanCreateCollectionWithOneElement(type))
.test(copyConstructorFailsForNullArgument(type))
.test(copyConstructorMakesDefensiveCopy(type))
.test(copyConstructorDoesNotModifyArgument(type)))
.test(suite("overrides size")
.test(sizeOfEmptyCollectionIsZero(type))
.test(sizeOfCollectionWithOneElementIsOne(type)))
.test(suite("overrides isEmpty")
.test(isEmptyReturnsFalseIfCollectionHasOneElement(type))
.test(isEmptyReturnsTrueIfCollectionIsEmpty(type)));
}

private static Test implementsCollectionInterface(final Class<?> type) {
return new Case("implements Collection interface") {
public void run() throws Throwable {
assertThat(Collection.class.isAssignableFrom(type));
public static Test defaultConstructorIsDeclared(final Class<?> type) {
return new Case("is declared") {
public void run() {
try {
type.getDeclaredConstructor();
} catch (NoSuchMethodException e) {
fail();
}
}
};
}

private static Test hasDefaultConstructor(final Class<?> type) {
return new Case("has default constructor") {
public static Test defaultConstructorIsPublic(final Class<?> type) {
return new Case("is public") {
public void run() {
try {
type.getConstructor();
Constructor<?> constructor = type.getDeclaredConstructor();
assertThat(Modifier.isPublic(constructor.getModifiers()));
} catch (NoSuchMethodException e) {
fail();
assume(false);
}
}
};
}

private static Case defaultConstructorCreatesEmptyCollection(final Class<?> type) {
return new Case("default constructor creates empty collection") {
public static Test defaultConstructorCreatesEmptyCollection(final Class<?> type) {
return new Case("creates empty collection") {
public void run() throws Throwable {
assume(Collection.class.isAssignableFrom(type));
Collection<?> collection = (Collection<?>) assumeConstructor(type).newInstance();
Expand All @@ -69,20 +55,41 @@ public void run() throws Throwable {
};
}

private static Test hasCopyConstructor(final Class<?> type) {
return new Case("has copy constructor") {
public static Test instantiatorIsDeclared(final Class<?> type) {
return new Case("is declared") {
public void run() {
try {
type.getConstructor(Collection.class);
type.getDeclaredConstructor(Collection.class);
} catch (NoSuchMethodException e) {
fail();
}
}
};
}

private static Test copyConstructorCanCreateCollectionWithOneElement(final Class<?> type) {
return new Case("copy constructor can create collection with 1 element") {
public static Test instantiatorIsPublic(final Class<?> type) {
return new Case("is public") {
public void run() {
try {
Constructor<?> constructor = type.getDeclaredConstructor(Collection.class);
assertThat(Modifier.isPublic(constructor.getModifiers()));
} catch (NoSuchMethodException e) {
assume(false);
}
}
};
}

public static Test instantiatorReturnsCollection(final Class<?> type) {
return new Case("implements Collection interface") {
public void run() throws Throwable {
assertThat(Collection.class.isAssignableFrom(type));
}
};
}

public static Test instantiatorCanCreateCollectionWithOneElement(final Class<?> type) {
return new Case("can create collection with 1 element") {
public void run() throws Throwable {
ArrayList<Object> original = newArrayList(a);
Collection<?> collection = assumeCreateCollection(type, copy(original));
Expand All @@ -91,8 +98,8 @@ public void run() throws Throwable {
};
}

private static Test copyConstructorFailsForNullArgument(final Class<?> type) {
return new Case("copy constructor fails for null argument") {
public static Test instantiatorFailsForNullArgument(final Class<?> type) {
return new Case("fails for null argument") {
public void run() throws Throwable {
try {
assumeCreateCollection(type, null);
Expand All @@ -106,8 +113,8 @@ public void run() throws Throwable {
};
}

private static Test copyConstructorMakesDefensiveCopy(final Class<?> type) {
return new Case("copy constructor makes defensive copy") {
public static Test instantiatorMakesDefensiveCopy(final Class<?> type) {
return new Case("makes defensive copy") {
public void run() throws Throwable {
ArrayList<Object> original = newArrayList(a);
ArrayList<Object> trojan = copy(original);
Expand All @@ -119,8 +126,8 @@ public void run() throws Throwable {
};
}

private static Test copyConstructorDoesNotModifyArgument(final Class<?> type) {
return new Case("copy constructor does not modify argument") {
public static Test instantiatorDoesNotModifyArgument(final Class<?> type) {
return new Case("does not modify argument") {
public void run() throws Throwable {
ArrayList<Object> original = newArrayList(a);
ArrayList<Object> argument = copy(original);
Expand All @@ -130,35 +137,35 @@ public void run() throws Throwable {
};
}

private static Test sizeOfCollectionWithOneElementIsOne(final Class<?> type) {
return new Case("size of collection with 1 element is 1") {
public static Test sizeReturnsOneIfCollectionHasOneElement(final Class<?> type) {
return new Case("returns 1 if collection has 1 element") {
public void run() throws Throwable {
Collection<?> collection = assumeCreateCollection(type, newArrayList(a));
assertThat(collection.size() == 1);
}
};
}

private static Test sizeOfEmptyCollectionIsZero(final Class<?> type) {
return new Case("size of empty collection is 0") {
public static Test sizeReturnsZeroIfCollectionIsEmpty(final Class<?> type) {
return new Case("returns 0 if collection is empty") {
public void run() throws Throwable {
Collection<?> collection = assumeCreateCollection(type, newArrayList());
assertThat(collection.size() == 0);
}
};
}

private static Test isEmptyReturnsFalseIfCollectionHasOneElement(final Class<?> type) {
return new Case("isEmpty returns false if collection has 1 element") {
public static Test isEmptyReturnsFalseIfCollectionHasOneElement(final Class<?> type) {
return new Case("returns false if collection has 1 element") {
public void run() throws Throwable {
Collection<?> collection = assumeCreateCollection(type, newArrayList(a));
assertThat(!collection.isEmpty());
}
};
}

private static Test isEmptyReturnsTrueIfCollectionIsEmpty(final Class<?> type) {
return new Case("isEmpty returns true if collection is empty") {
public static Test isEmptyReturnsTrueIfCollectionIsEmpty(final Class<?> type) {
return new Case("returns true if collection is empty") {
public void run() throws Throwable {
Collection<?> collection = assumeCreateCollection(type, newArrayList());
assertThat(collection.isEmpty());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static org.quackery.AssertionException.assertEquals;
import static org.quackery.AssertionException.assertThat;
import static org.quackery.Suite.suite;
import static org.quackery.contract.collection.Assumptions.assumeCreateList;
import static org.quackery.contract.collection.Collections.copy;
import static org.quackery.contract.collection.Collections.newArrayList;
Expand All @@ -18,15 +17,8 @@
import org.quackery.Test;

public class ListMutableSuite {
public static Test listMutableSuite(Class<?> type) {
return suite("quacks like mutable list")
.test(suite("overrides add")
.test(addAddsElementAtTheEnd(type))
.test(addReturnsTrue(type)));
}

private static Test addAddsElementAtTheEnd(final Class<?> type) {
return new Case("add adds element at the end") {
public static Test addAddsElementAtTheEnd(final Class<?> type) {
return new Case("adds element at the end") {
public void run() throws Throwable {
ArrayList<Object> original = newArrayList(a, b, c);
List<Object> list = assumeCreateList(type, copy(original));
Expand All @@ -37,8 +29,8 @@ public void run() throws Throwable {
};
}

private static Test addReturnsTrue(final Class<?> type) {
return new Case("add returns true") {
public static Test addReturnsTrue(final Class<?> type) {
return new Case("returns true") {
public void run() throws Throwable {
List<Object> list = assumeCreateList(type, newArrayList(a, b, c));
assertThat(list.add(d));
Expand Down
Loading

0 comments on commit 1399ded

Please sign in to comment.