Refactoring in the unit test utility framework:
* Moved unit test utilities to the onlab-junit package under utils/junit
- ImmutableClassChecker
- TestUtils and TestUtilsTest
* Added/ported unit test utilities from the older code
- UtilityClassChecker and UtilityClassCheckerTest
- ImmutableClassCheckerTest
* Updated/fixed some of the pom.xml files in the context of the
onlab-junit package:
- Added <scope>test</scope>
- Replaced hard-coded "1.0.0-SNAPSHOT" with "${project.version}"
Change-Id: Ie5f51ba401ca1748340f38848ab6bfc251964adc
diff --git a/utils/junit/pom.xml b/utils/junit/pom.xml
index d994a07..6ddadac 100644
--- a/utils/junit/pom.xml
+++ b/utils/junit/pom.xml
@@ -27,6 +27,16 @@
<artifactId>guava-testlib</artifactId>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-library</artifactId>
+ <scope>compile</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/utils/junit/src/main/java/org/onlab/junit/ImmutableClassChecker.java b/utils/junit/src/main/java/org/onlab/junit/ImmutableClassChecker.java
new file mode 100644
index 0000000..30d895e
--- /dev/null
+++ b/utils/junit/src/main/java/org/onlab/junit/ImmutableClassChecker.java
@@ -0,0 +1,124 @@
+package org.onlab.junit;
+
+import org.hamcrest.Description;
+import org.hamcrest.StringDescription;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Hamcrest style class for verifying that a class follows the
+ * accepted rules for immutable classes.
+ *
+ * The rules that are enforced for immutable classes:
+ * - the class must be declared final
+ * - all data members of the class must be declared private and final
+ * - the class must not define any setter methods
+ */
+
+public class ImmutableClassChecker {
+
+ private String failureReason = "";
+
+ /**
+ * Method to determine if a given class is a properly specified
+ * immutable class.
+ *
+ * @param clazz the class to check
+ * @return true if the given class is a properly specified immutable class.
+ */
+ private boolean isImmutableClass(Class<?> clazz) {
+ // class must be declared final
+ if (!Modifier.isFinal(clazz.getModifiers())) {
+ failureReason = "a class that is not final";
+ return false;
+ }
+
+ // class must have only final and private data members
+ for (final Field field : clazz.getDeclaredFields()) {
+ if (field.getName().startsWith("__cobertura")) {
+ // cobertura sticks these fields into classes - ignore them
+ continue;
+ }
+ if (!Modifier.isFinal(field.getModifiers())) {
+ failureReason = "a field named '" + field.getName() +
+ "' that is not final";
+ return false;
+ }
+ if (!Modifier.isPrivate(field.getModifiers())) {
+ //
+ // NOTE: We relax the recommended rules for defining immutable
+ // objects and allow "static final" fields that are not
+ // private. The "final" check was already done above so we
+ // don't repeat it here.
+ //
+ if (!Modifier.isStatic(field.getModifiers())) {
+ failureReason = "a field named '" + field.getName() +
+ "' that is not private and is not static";
+ return false;
+ }
+ }
+ }
+
+ // class must not define any setters
+ for (final Method method : clazz.getMethods()) {
+ if (method.getDeclaringClass().equals(clazz)) {
+ if (method.getName().startsWith("set")) {
+ failureReason = "a class with a setter named '" + method.getName() + "'";
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Describe why an error was reported. Uses Hamcrest style Description
+ * interfaces.
+ *
+ * @param description the Description object to use for reporting the
+ * mismatch
+ */
+ public void describeMismatch(Description description) {
+ description.appendText(failureReason);
+ }
+
+ /**
+ * Describe the source object that caused an error, using a Hamcrest
+ * Matcher style interface. In this case, it always returns
+ * that we are looking for a properly defined utility class.
+ *
+ * @param description the Description object to use to report the "to"
+ * object
+ */
+ public void describeTo(Description description) {
+ description.appendText("a properly defined immutable class");
+ }
+
+ /**
+ * Assert that the given class adheres to the utility class rules.
+ *
+ * @param clazz the class to check
+ *
+ * @throws java.lang.AssertionError if the class is not a valid
+ * utility class
+ */
+ public static void assertThatClassIsImmutable(Class<?> clazz) {
+ final ImmutableClassChecker checker = new ImmutableClassChecker();
+ if (!checker.isImmutableClass(clazz)) {
+ final Description toDescription = new StringDescription();
+ final Description mismatchDescription = new StringDescription();
+
+ checker.describeTo(toDescription);
+ checker.describeMismatch(mismatchDescription);
+ final String reason =
+ "\n" +
+ "Expected: is \"" + toDescription.toString() + "\"\n" +
+ " but : was \"" + mismatchDescription.toString() + "\"";
+
+ throw new AssertionError(reason);
+ }
+ }
+}
diff --git a/utils/junit/src/main/java/org/onlab/junit/TestUtils.java b/utils/junit/src/main/java/org/onlab/junit/TestUtils.java
new file mode 100644
index 0000000..64b7ae300
--- /dev/null
+++ b/utils/junit/src/main/java/org/onlab/junit/TestUtils.java
@@ -0,0 +1,169 @@
+package org.onlab.junit;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+
+/**
+ * Utilities for testing.
+ */
+public final class TestUtils {
+
+ /**
+ * Sets the field, bypassing scope restriction.
+ *
+ * @param subject Object where the field belongs
+ * @param fieldName name of the field to set
+ * @param value value to set to the field.
+ * @param <T> subject type
+ * @param <U> value type
+ * @throws TestUtilsException if there are reflection errors while setting
+ * the field
+ */
+ public static <T, U> void setField(T subject, String fieldName, U value)
+ throws TestUtilsException {
+ @SuppressWarnings("unchecked")
+ Class<T> clazz = (Class<T>) subject.getClass();
+ try {
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(subject, value);
+ } catch (NoSuchFieldException | SecurityException |
+ IllegalArgumentException | IllegalAccessException e) {
+ throw new TestUtilsException("setField failed", e);
+ }
+ }
+
+ /**
+ * Gets the field, bypassing scope restriction.
+ *
+ * @param subject Object where the field belongs
+ * @param fieldName name of the field to get
+ * @return value of the field.
+ * @param <T> subject type
+ * @param <U> field value type
+ * @throws TestUtilsException if there are reflection errors while getting
+ * the field
+ */
+ public static <T, U> U getField(T subject, String fieldName)
+ throws TestUtilsException {
+ try {
+ @SuppressWarnings("unchecked")
+ Class<T> clazz = (Class<T>) subject.getClass();
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+
+ @SuppressWarnings("unchecked")
+ U result = (U) field.get(subject);
+ return result;
+ } catch (NoSuchFieldException | SecurityException |
+ IllegalArgumentException | IllegalAccessException e) {
+ throw new TestUtilsException("getField failed", e);
+ }
+ }
+
+ /**
+ * Calls the method, bypassing scope restriction.
+ *
+ * @param subject Object where the method belongs
+ * @param methodName name of the method to call
+ * @param paramTypes formal parameter type array
+ * @param args arguments
+ * @return return value or null if void
+ * @param <T> subject type
+ * @param <U> return value type
+ * @throws TestUtilsException if there are reflection errors while calling
+ * the method
+ */
+ public static <T, U> U callMethod(T subject, String methodName,
+ Class<?>[] paramTypes, Object...args) throws TestUtilsException {
+
+ try {
+ @SuppressWarnings("unchecked")
+ Class<T> clazz = (Class<T>) subject.getClass();
+ final Method method;
+ if (paramTypes == null || paramTypes.length == 0) {
+ method = clazz.getDeclaredMethod(methodName);
+ } else {
+ method = clazz.getDeclaredMethod(methodName, paramTypes);
+ }
+ method.setAccessible(true);
+
+ @SuppressWarnings("unchecked")
+ U result = (U) method.invoke(subject, args);
+ return result;
+ } catch (NoSuchMethodException | SecurityException |
+ IllegalAccessException | IllegalArgumentException |
+ InvocationTargetException e) {
+ throw new TestUtilsException("callMethod failed", e);
+ }
+ }
+
+ /**
+ * Calls the method, bypassing scope restriction.
+ *
+ * @param subject Object where the method belongs
+ * @param methodName name of the method to call
+ * @param paramType formal parameter type
+ * @param arg argument
+ * @return return value or null if void
+ * @param <T> subject type
+ * @param <U> return value type
+ * @throws TestUtilsException if there are reflection errors while calling
+ * the method
+ */
+ public static <T, U> U callMethod(T subject, String methodName,
+ Class<?> paramType, Object arg) throws TestUtilsException {
+ return callMethod(subject, methodName, new Class<?>[]{paramType}, arg);
+ }
+
+ /**
+ * Triggers an allocation of an object of type <T> and forces a call to
+ * the private constructor.
+ *
+ * @param constructor Constructor to call
+ * @param <T> type of the object to create
+ * @return created object of type <T>
+ * @throws TestUtilsException if there are reflection errors while calling
+ * the constructor
+ */
+ public static <T> T callConstructor(Constructor<T> constructor)
+ throws TestUtilsException {
+ try {
+ constructor.setAccessible(true);
+ return constructor.newInstance();
+ } catch (InstantiationException | IllegalAccessException |
+ InvocationTargetException error) {
+ throw new TestUtilsException("callConstructor failed", error);
+ }
+ }
+
+ /**
+ * Avoid instantiation.
+ */
+ private TestUtils() {}
+
+ /**
+ * Exception that can be thrown if problems are encountered while executing
+ * the utility method. These are usually problems accessing fields/methods
+ * through reflection. The original exception can be found by examining the
+ * cause.
+ */
+ public static class TestUtilsException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs a new exception with the specified detail message and
+ * cause.
+ *
+ * @param message the detail message
+ * @param cause the original cause of this exception
+ */
+ public TestUtilsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+}
diff --git a/utils/junit/src/main/java/org/onlab/junit/UtilityClassChecker.java b/utils/junit/src/main/java/org/onlab/junit/UtilityClassChecker.java
new file mode 100644
index 0000000..bd272d1
--- /dev/null
+++ b/utils/junit/src/main/java/org/onlab/junit/UtilityClassChecker.java
@@ -0,0 +1,134 @@
+package org.onlab.junit;
+
+import org.hamcrest.Description;
+import org.hamcrest.StringDescription;
+import org.onlab.junit.TestUtils.TestUtilsException;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+
+/**
+ * Hamcrest style class for verifying that a class follows the
+ * accepted rules for utility classes.
+ *
+ * The rules that are enforced for utility classes:
+ * - the class must be declared final
+ * - the class must have only one constructor
+ * - the constructor must be private and inaccessible to callers
+ * - the class must have only static methods
+ */
+
+public class UtilityClassChecker {
+
+ private String failureReason = "";
+
+ /**
+ * Method to determine if a given class is a properly specified
+ * utility class. In addition to checking that the class meets the criteria
+ * for utility classes, an object of the class type is allocated to force
+ * test code coverage onto the class constructor.
+ *
+ * @param clazz the class to check
+ * @return true if the given class is a properly specified utility class.
+ */
+ private boolean isProperlyDefinedUtilityClass(Class<?> clazz) {
+ // class must be declared final
+ if (!Modifier.isFinal(clazz.getModifiers())) {
+ failureReason = "a class that is not final";
+ return false;
+ }
+
+ // class must have only one constructor
+ final Constructor<?>[] constructors = clazz.getDeclaredConstructors();
+ if (constructors.length != 1) {
+ failureReason = "a class with more than one constructor";
+ return false;
+ }
+
+ // constructor must not be accessible outside of the class
+ final Constructor<?> constructor = constructors[0];
+ if (constructor.isAccessible()) {
+ failureReason = "a class with an accessible default constructor";
+ return false;
+ }
+
+ // constructor must be private
+ if (!Modifier.isPrivate(constructor.getModifiers())) {
+ failureReason = "a class with a default constructor that is not private";
+ return false;
+ }
+
+ // class must have only static methods
+ for (final Method method : clazz.getMethods()) {
+ if (method.getDeclaringClass().equals(clazz)) {
+ if (!Modifier.isStatic(method.getModifiers())) {
+ failureReason = "a class with one or more non-static methods";
+ return false;
+ }
+ }
+
+ }
+
+ try {
+ final Object newObject = TestUtils.callConstructor(constructor);
+ if (newObject == null) {
+ failureReason = "could not instantiate a new object";
+ return false;
+ }
+ } catch (TestUtilsException e) {
+ failureReason = "could not instantiate a new object";
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Describe why an error was reported. Uses Hamcrest style Description
+ * interfaces.
+ *
+ * @param description the Description object to use for reporting the
+ * mismatch
+ */
+ public void describeMismatch(Description description) {
+ description.appendText(failureReason);
+ }
+
+ /**
+ * Describe the source object that caused an error, using a Hamcrest
+ * Matcher style interface. In this case, it always returns
+ * that we are looking for a properly defined utility class.
+ *
+ * @param description the Description object to use to report the "to"
+ * object
+ */
+ public void describeTo(Description description) {
+ description.appendText("a properly defined utility class");
+ }
+
+ /**
+ * Assert that the given class adheres to the utility class rules.
+ *
+ * @param clazz the class to check
+ *
+ * @throws java.lang.AssertionError if the class is not a valid
+ * utility class
+ */
+ public static void assertThatClassIsUtility(Class<?> clazz) {
+ final UtilityClassChecker checker = new UtilityClassChecker();
+ if (!checker.isProperlyDefinedUtilityClass(clazz)) {
+ final Description toDescription = new StringDescription();
+ final Description mismatchDescription = new StringDescription();
+
+ checker.describeTo(toDescription);
+ checker.describeMismatch(mismatchDescription);
+ final String reason =
+ "\n" +
+ "Expected: is \"" + toDescription.toString() + "\"\n" +
+ " but : was \"" + mismatchDescription.toString() + "\"";
+
+ throw new AssertionError(reason);
+ }
+ }
+}
diff --git a/utils/junit/src/test/java/org/onlab/junit/ImmutableClassCheckerTest.java b/utils/junit/src/test/java/org/onlab/junit/ImmutableClassCheckerTest.java
new file mode 100644
index 0000000..b4a6ff5
--- /dev/null
+++ b/utils/junit/src/test/java/org/onlab/junit/ImmutableClassCheckerTest.java
@@ -0,0 +1,120 @@
+package org.onlab.junit;
+
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+
+/**
+ * Set of unit tests to check the implementation of the immutable class
+ * checker.
+ */
+public class ImmutableClassCheckerTest {
+ /**
+ * Test class for non final class check.
+ */
+ // CHECKSTYLE IGNORE FinalClass FOR NEXT 1 LINES
+ static class NonFinal {
+ private NonFinal() { }
+ }
+
+ /**
+ * Check that a non final class correctly produces an error.
+ * @throws Exception if any of the reflection lookups fail.
+ */
+ @Test
+ public void testNonFinalClass() throws Exception {
+ boolean gotException = false;
+ try {
+ assertThatClassIsImmutable(NonFinal.class);
+ } catch (AssertionError assertion) {
+ assertThat(assertion.getMessage(),
+ containsString("is not final"));
+ gotException = true;
+ }
+ assertThat(gotException, is(true));
+ }
+
+ /**
+ * Test class for non private member class check.
+ */
+ static final class FinalProtectedMember {
+ protected final int x = 0;
+ }
+
+ /**
+ * Check that a final class with a non-private member is properly detected.
+ *
+ * @throws Exception if any of the reflection lookups fail.
+ */
+ @Test
+ public void testFinalProtectedMember() throws Exception {
+ boolean gotException = false;
+ try {
+ assertThatClassIsImmutable(FinalProtectedMember.class);
+ } catch (AssertionError assertion) {
+ assertThat(assertion.getMessage(),
+ containsString("a field named 'x' that is not private"));
+ gotException = true;
+ }
+ assertThat(gotException, is(true));
+ }
+
+ /**
+ * Test class for non private member class check.
+ */
+ static final class NotFinalPrivateMember {
+ private int x = 0;
+ }
+
+ /**
+ * Check that a final class with a non-final private
+ * member is properly detected.
+ *
+ * @throws Exception if any of the reflection lookups fail.
+ */
+ @Test
+ public void testNotFinalPrivateMember() throws Exception {
+ boolean gotException = false;
+ try {
+ assertThatClassIsImmutable(NotFinalPrivateMember.class);
+ } catch (AssertionError assertion) {
+ assertThat(assertion.getMessage(),
+ containsString("a field named 'x' that is not final"));
+ gotException = true;
+ }
+ assertThat(gotException, is(true));
+ }
+
+ /**
+ * Test class for non private member class check.
+ */
+ static final class ClassWithSetter {
+ private final int x = 0;
+ public void setX(int newX) {
+ }
+ }
+
+ /**
+ * Check that a final class with a final private
+ * member that is modifyable by a setter is properly detected.
+ *
+ * @throws Exception if any of the reflection lookups fail.
+ */
+ @Test
+ public void testClassWithSetter() throws Exception {
+ boolean gotException = false;
+ try {
+ assertThatClassIsImmutable(ClassWithSetter.class);
+ } catch (AssertionError assertion) {
+ assertThat(assertion.getMessage(),
+ containsString("a class with a setter named 'setX'"));
+ gotException = true;
+ }
+ assertThat(gotException, is(true));
+ }
+
+}
+
diff --git a/utils/junit/src/test/java/org/onlab/junit/TestUtilsTest.java b/utils/junit/src/test/java/org/onlab/junit/TestUtilsTest.java
new file mode 100644
index 0000000..c57b351
--- /dev/null
+++ b/utils/junit/src/test/java/org/onlab/junit/TestUtilsTest.java
@@ -0,0 +1,170 @@
+package org.onlab.junit;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils.TestUtilsException;
+
+/**
+ * Test and usage examples for TestUtils.
+ */
+public class TestUtilsTest {
+
+ /**
+ * Test data.
+ */
+ private static final class TestClass {
+
+ @SuppressWarnings("unused")
+ private int privateField = 42;
+
+ @SuppressWarnings("unused")
+ protected int protectedField = 2501; // CHECKSTYLE IGNORE THIS LINE
+
+ /**
+ * Protected method with multiple argument.
+ *
+ * @param x simply returns
+ * @param y not used
+ * @return x
+ */
+ @SuppressWarnings("unused")
+ private int privateMethod(Number x, Long y) {
+ return x.intValue();
+ }
+
+ /**
+ * Protected method with no argument.
+ *
+ * @return int
+ */
+ @SuppressWarnings("unused")
+ protected int protectedMethod() {
+ return 42;
+ }
+
+ /**
+ * Method returning array.
+ *
+ * @param ary random array
+ * @return ary
+ */
+ @SuppressWarnings("unused")
+ private int[] arrayReturnMethod(int[] ary) {
+ return ary;
+ }
+
+ /**
+ * Method without return value.
+ *
+ * @param s ignored
+ */
+ @SuppressWarnings("unused")
+ private void voidMethod(String s) {
+ System.out.println(s);
+ }
+ }
+
+ private TestClass test;
+
+ /**
+ * Sets up the test fixture.
+ */
+ @Before
+ public void setUp() {
+ test = new TestClass();
+ }
+
+ /**
+ * Example to access private field.
+ *
+ * @throws TestUtilsException TestUtils error
+ */
+ @Test
+ public void testSetGetPrivateField() throws TestUtilsException {
+
+ assertEquals(42, TestUtils.getField(test, "privateField"));
+ TestUtils.setField(test, "privateField", 0xDEAD);
+ assertEquals(0xDEAD, TestUtils.getField(test, "privateField"));
+ }
+
+ /**
+ * Example to access protected field.
+ *
+ * @throws TestUtilsException TestUtils error
+ */
+ @Test
+ public void testSetGetProtectedField() throws TestUtilsException {
+
+ assertEquals(2501, TestUtils.getField(test, "protectedField"));
+ TestUtils.setField(test, "protectedField", 0xBEEF);
+ assertEquals(0xBEEF, TestUtils.getField(test, "protectedField"));
+ }
+
+ /**
+ * Example to call private method and multiple parameters.
+ * <p/>
+ * It also illustrates that paramTypes must match declared type,
+ * not the runtime types of arguments.
+ *
+ * @throws TestUtilsException TestUtils error
+ */
+ @Test
+ public void testCallPrivateMethod() throws TestUtilsException {
+
+ int result = TestUtils.callMethod(test, "privateMethod",
+ new Class<?>[] {Number.class, Long.class},
+ Long.valueOf(42), Long.valueOf(32));
+ assertEquals(42, result);
+ }
+
+ /**
+ * Example to call protected method and no parameters.
+ *
+ * @throws TestUtilsException TestUtils error
+ */
+ @Test
+ public void testCallProtectedMethod() throws TestUtilsException {
+
+ int result = TestUtils.callMethod(test, "protectedMethod",
+ new Class<?>[] {});
+ assertEquals(42, result);
+ }
+
+ /**
+ * Example to call method returning array.
+ * <p/>
+ * Note: It is not required to receive as Object.
+ * Following is just verifying it is not Boxed arrays.
+ *
+ * @throws TestUtilsException TestUtils error
+ */
+ @Test
+ public void testCallArrayReturnMethod() throws TestUtilsException {
+
+ int[] array = {1, 2, 3};
+ Object aryResult = TestUtils.callMethod(test, "arrayReturnMethod",
+ new Class<?>[] {int[].class}, array);
+ assertEquals(int[].class, aryResult.getClass());
+ assertArrayEquals(array, (int[]) aryResult);
+ }
+
+
+ /**
+ * Example to call void returning method.
+ * <p/>
+ * Note: Return value will be null for void methods.
+ *
+ * @throws TestUtilsException TestUtils error
+ */
+ @Test
+ public void testCallVoidReturnMethod() throws TestUtilsException {
+
+ Object voidResult = TestUtils.callMethod(test, "voidMethod",
+ String.class, "foobar");
+ assertNull(voidResult);
+ }
+}
diff --git a/utils/junit/src/test/java/org/onlab/junit/UtilityClassCheckerTest.java b/utils/junit/src/test/java/org/onlab/junit/UtilityClassCheckerTest.java
new file mode 100644
index 0000000..7f56d81
--- /dev/null
+++ b/utils/junit/src/test/java/org/onlab/junit/UtilityClassCheckerTest.java
@@ -0,0 +1,145 @@
+package org.onlab.junit;
+
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.onlab.junit.UtilityClassChecker.assertThatClassIsUtility;
+
+/**
+ * Set of unit tests to check the implementation of the utility class
+ * checker.
+ */
+public class UtilityClassCheckerTest {
+
+ // CHECKSTYLE:OFF test data intentionally not final
+ /**
+ * Test class for non final class check.
+ */
+ static class NonFinal {
+ private NonFinal() { }
+ }
+ // CHECKSTYLE:ON
+
+ /**
+ * Check that a non final class correctly produces an error.
+ * @throws Exception if any of the reflection lookups fail.
+ */
+ @Test
+ public void testNonFinalClass() throws Exception {
+ boolean gotException = false;
+ try {
+ assertThatClassIsUtility(NonFinal.class);
+ } catch (AssertionError assertion) {
+ assertThat(assertion.getMessage(),
+ containsString("is not final"));
+ gotException = true;
+ }
+ assertThat(gotException, is(true));
+ }
+
+ /**
+ * Test class for final no constructor class check.
+ */
+ static final class FinalNoConstructor {
+ }
+
+ /**
+ * Check that a final class with no declared constructor correctly produces
+ * an error. In this case, the compiler generates a default constructor
+ * for you, but the constructor is 'protected' and will fail the check.
+ *
+ * @throws Exception if any of the reflection lookups fail.
+ */
+ @Test
+ public void testFinalNoConstructorClass() throws Exception {
+ boolean gotException = false;
+ try {
+ assertThatClassIsUtility(FinalNoConstructor.class);
+ } catch (AssertionError assertion) {
+ assertThat(assertion.getMessage(),
+ containsString("class with a default constructor that " +
+ "is not private"));
+ gotException = true;
+ }
+ assertThat(gotException, is(true));
+ }
+
+ /**
+ * Test class for class with more than one constructor check.
+ */
+ static final class TwoConstructors {
+ private TwoConstructors() { }
+ private TwoConstructors(int x) { }
+ }
+
+ /**
+ * Check that a non static class correctly produces an error.
+ * @throws Exception if any of the reflection lookups fail.
+ */
+ @Test
+ public void testOnlyOneConstructor() throws Exception {
+ boolean gotException = false;
+ try {
+ assertThatClassIsUtility(TwoConstructors.class);
+ } catch (AssertionError assertion) {
+ assertThat(assertion.getMessage(),
+ containsString("more than one constructor"));
+ gotException = true;
+ }
+ assertThat(gotException, is(true));
+ }
+
+ /**
+ * Test class with a non private constructor.
+ */
+ static final class NonPrivateConstructor {
+ protected NonPrivateConstructor() { }
+ }
+
+ /**
+ * Check that a class with a non private constructor correctly
+ * produces an error.
+ * @throws Exception if any of the reflection lookups fail.
+ */
+ @Test
+ public void testNonPrivateConstructor() throws Exception {
+
+ boolean gotException = false;
+ try {
+ assertThatClassIsUtility(NonPrivateConstructor.class);
+ } catch (AssertionError assertion) {
+ assertThat(assertion.getMessage(),
+ containsString("constructor that is not private"));
+ gotException = true;
+ }
+ assertThat(gotException, is(true));
+ }
+
+ /**
+ * Test class with a non static method.
+ */
+ static final class NonStaticMethod {
+ private NonStaticMethod() { }
+ public void aPublicMethod() { }
+ }
+
+ /**
+ * Check that a class with a non static method correctly produces an error.
+ * @throws Exception if any of the reflection lookups fail.
+ */
+ @Test
+ public void testNonStaticMethod() throws Exception {
+
+ boolean gotException = false;
+ try {
+ assertThatClassIsUtility(NonStaticMethod.class);
+ } catch (AssertionError assertion) {
+ assertThat(assertion.getMessage(),
+ containsString("one or more non-static methods"));
+ gotException = true;
+ }
+ assertThat(gotException, is(true));
+ }
+}