blob: 9c623ceede65c4c8d792d5e9715a1bfe56492236 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska24c849c2014-10-27 09:53:05 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska24c849c2014-10-27 09:53:05 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
Pavlin Radoslavovd26f57a2014-10-23 17:19:45 -070016package org.onlab.junit;
17
18import org.hamcrest.Description;
19import org.hamcrest.StringDescription;
20import org.onlab.junit.TestUtils.TestUtilsException;
21
22import java.lang.reflect.Constructor;
23import java.lang.reflect.Method;
24import java.lang.reflect.Modifier;
25
26
27/**
28 * Hamcrest style class for verifying that a class follows the
29 * accepted rules for utility classes.
30 *
31 * The rules that are enforced for utility classes:
32 * - the class must be declared final
33 * - the class must have only one constructor
34 * - the constructor must be private and inaccessible to callers
35 * - the class must have only static methods
36 */
37
38public class UtilityClassChecker {
39
40 private String failureReason = "";
41
42 /**
43 * Method to determine if a given class is a properly specified
44 * utility class. In addition to checking that the class meets the criteria
45 * for utility classes, an object of the class type is allocated to force
46 * test code coverage onto the class constructor.
47 *
48 * @param clazz the class to check
49 * @return true if the given class is a properly specified utility class.
50 */
51 private boolean isProperlyDefinedUtilityClass(Class<?> clazz) {
52 // class must be declared final
53 if (!Modifier.isFinal(clazz.getModifiers())) {
54 failureReason = "a class that is not final";
55 return false;
56 }
57
58 // class must have only one constructor
59 final Constructor<?>[] constructors = clazz.getDeclaredConstructors();
60 if (constructors.length != 1) {
61 failureReason = "a class with more than one constructor";
62 return false;
63 }
64
65 // constructor must not be accessible outside of the class
66 final Constructor<?> constructor = constructors[0];
67 if (constructor.isAccessible()) {
68 failureReason = "a class with an accessible default constructor";
69 return false;
70 }
71
72 // constructor must be private
73 if (!Modifier.isPrivate(constructor.getModifiers())) {
74 failureReason = "a class with a default constructor that is not private";
75 return false;
76 }
77
78 // class must have only static methods
79 for (final Method method : clazz.getMethods()) {
80 if (method.getDeclaringClass().equals(clazz)) {
81 if (!Modifier.isStatic(method.getModifiers())) {
82 failureReason = "a class with one or more non-static methods";
83 return false;
84 }
85 }
86
87 }
88
89 try {
90 final Object newObject = TestUtils.callConstructor(constructor);
91 if (newObject == null) {
92 failureReason = "could not instantiate a new object";
93 return false;
94 }
95 } catch (TestUtilsException e) {
96 failureReason = "could not instantiate a new object";
97 return false;
98 }
99 return true;
100 }
101
102 /**
103 * Describe why an error was reported. Uses Hamcrest style Description
104 * interfaces.
105 *
106 * @param description the Description object to use for reporting the
107 * mismatch
108 */
109 public void describeMismatch(Description description) {
110 description.appendText(failureReason);
111 }
112
113 /**
114 * Describe the source object that caused an error, using a Hamcrest
115 * Matcher style interface. In this case, it always returns
116 * that we are looking for a properly defined utility class.
117 *
118 * @param description the Description object to use to report the "to"
119 * object
120 */
121 public void describeTo(Description description) {
122 description.appendText("a properly defined utility class");
123 }
124
125 /**
126 * Assert that the given class adheres to the utility class rules.
127 *
128 * @param clazz the class to check
129 *
130 * @throws java.lang.AssertionError if the class is not a valid
131 * utility class
132 */
133 public static void assertThatClassIsUtility(Class<?> clazz) {
134 final UtilityClassChecker checker = new UtilityClassChecker();
135 if (!checker.isProperlyDefinedUtilityClass(clazz)) {
136 final Description toDescription = new StringDescription();
137 final Description mismatchDescription = new StringDescription();
138
139 checker.describeTo(toDescription);
140 checker.describeMismatch(mismatchDescription);
141 final String reason =
142 "\n" +
143 "Expected: is \"" + toDescription.toString() + "\"\n" +
144 " but : was \"" + mismatchDescription.toString() + "\"";
145
146 throw new AssertionError(reason);
147 }
148 }
149}