blob: bd272d1c549329d1db11fcbff96397c28419841f [file] [log] [blame]
Pavlin Radoslavovd26f57a2014-10-23 17:19:45 -07001package org.onlab.junit;
2
3import org.hamcrest.Description;
4import org.hamcrest.StringDescription;
5import org.onlab.junit.TestUtils.TestUtilsException;
6
7import java.lang.reflect.Constructor;
8import java.lang.reflect.Method;
9import java.lang.reflect.Modifier;
10
11
12/**
13 * Hamcrest style class for verifying that a class follows the
14 * accepted rules for utility classes.
15 *
16 * The rules that are enforced for utility classes:
17 * - the class must be declared final
18 * - the class must have only one constructor
19 * - the constructor must be private and inaccessible to callers
20 * - the class must have only static methods
21 */
22
23public class UtilityClassChecker {
24
25 private String failureReason = "";
26
27 /**
28 * Method to determine if a given class is a properly specified
29 * utility class. In addition to checking that the class meets the criteria
30 * for utility classes, an object of the class type is allocated to force
31 * test code coverage onto the class constructor.
32 *
33 * @param clazz the class to check
34 * @return true if the given class is a properly specified utility class.
35 */
36 private boolean isProperlyDefinedUtilityClass(Class<?> clazz) {
37 // class must be declared final
38 if (!Modifier.isFinal(clazz.getModifiers())) {
39 failureReason = "a class that is not final";
40 return false;
41 }
42
43 // class must have only one constructor
44 final Constructor<?>[] constructors = clazz.getDeclaredConstructors();
45 if (constructors.length != 1) {
46 failureReason = "a class with more than one constructor";
47 return false;
48 }
49
50 // constructor must not be accessible outside of the class
51 final Constructor<?> constructor = constructors[0];
52 if (constructor.isAccessible()) {
53 failureReason = "a class with an accessible default constructor";
54 return false;
55 }
56
57 // constructor must be private
58 if (!Modifier.isPrivate(constructor.getModifiers())) {
59 failureReason = "a class with a default constructor that is not private";
60 return false;
61 }
62
63 // class must have only static methods
64 for (final Method method : clazz.getMethods()) {
65 if (method.getDeclaringClass().equals(clazz)) {
66 if (!Modifier.isStatic(method.getModifiers())) {
67 failureReason = "a class with one or more non-static methods";
68 return false;
69 }
70 }
71
72 }
73
74 try {
75 final Object newObject = TestUtils.callConstructor(constructor);
76 if (newObject == null) {
77 failureReason = "could not instantiate a new object";
78 return false;
79 }
80 } catch (TestUtilsException e) {
81 failureReason = "could not instantiate a new object";
82 return false;
83 }
84 return true;
85 }
86
87 /**
88 * Describe why an error was reported. Uses Hamcrest style Description
89 * interfaces.
90 *
91 * @param description the Description object to use for reporting the
92 * mismatch
93 */
94 public void describeMismatch(Description description) {
95 description.appendText(failureReason);
96 }
97
98 /**
99 * Describe the source object that caused an error, using a Hamcrest
100 * Matcher style interface. In this case, it always returns
101 * that we are looking for a properly defined utility class.
102 *
103 * @param description the Description object to use to report the "to"
104 * object
105 */
106 public void describeTo(Description description) {
107 description.appendText("a properly defined utility class");
108 }
109
110 /**
111 * Assert that the given class adheres to the utility class rules.
112 *
113 * @param clazz the class to check
114 *
115 * @throws java.lang.AssertionError if the class is not a valid
116 * utility class
117 */
118 public static void assertThatClassIsUtility(Class<?> clazz) {
119 final UtilityClassChecker checker = new UtilityClassChecker();
120 if (!checker.isProperlyDefinedUtilityClass(clazz)) {
121 final Description toDescription = new StringDescription();
122 final Description mismatchDescription = new StringDescription();
123
124 checker.describeTo(toDescription);
125 checker.describeMismatch(mismatchDescription);
126 final String reason =
127 "\n" +
128 "Expected: is \"" + toDescription.toString() + "\"\n" +
129 " but : was \"" + mismatchDescription.toString() + "\"";
130
131 throw new AssertionError(reason);
132 }
133 }
134}