Add a utility class checker for unit tests

Added a utility class checker and a unit test for it.  The
utility class checker makes sure that a utility class adheres
to the following rules:

    - 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

Putting this checker in your unit tests will prevent future modifications
to your class that break the utility class assumptions.  The checker
also triggers an object creation through reflection that will
cause coverage events from the test.

Change-Id: Ia469908aefd5ad5c9a7387e6168576d50da01b3e
diff --git a/src/test/java/net/onrc/onos/core/util/UtilityClassChecker.java b/src/test/java/net/onrc/onos/core/util/UtilityClassChecker.java
new file mode 100644
index 0000000..d3fcc65
--- /dev/null
+++ b/src/test/java/net/onrc/onos/core/util/UtilityClassChecker.java
@@ -0,0 +1,135 @@
+package net.onrc.onos.core.util;
+
+import org.hamcrest.Description;
+import org.hamcrest.StringDescription;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+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;
+                }
+            }
+
+        }
+
+        // Trigger an allocation of the object to force the hidden
+        // constructor call
+        try {
+            constructor.setAccessible(true);
+            constructor.newInstance();
+            constructor.setAccessible(false);
+        } catch (InstantiationException|IllegalAccessException|
+                 InvocationTargetException error) {
+            failureReason = error.getCause() + " when calling constructor";
+            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/src/test/java/net/onrc/onos/core/util/UtilityClassCheckerTest.java b/src/test/java/net/onrc/onos/core/util/UtilityClassCheckerTest.java
new file mode 100644
index 0000000..57ae6c5
--- /dev/null
+++ b/src/test/java/net/onrc/onos/core/util/UtilityClassCheckerTest.java
@@ -0,0 +1,142 @@
+package net.onrc.onos.core.util;
+
+import org.junit.Test;
+
+import static net.onrc.onos.core.util.UtilityClassChecker.assertThatClassIsUtility;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Set of unit tests to check the implementation of the utility class
+ * checker.
+ */
+public class UtilityClassCheckerTest {
+    /**
+     * Test class for non final class check.
+     */
+    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 {
+            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.
+     */
+    final static 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.
+     */
+    final static 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.
+     */
+    final static 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));
+    }
+}