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