1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.collect.testing;
18
19 import static java.util.Collections.disjoint;
20 import static java.util.logging.Level.FINER;
21
22 import com.google.common.collect.testing.features.ConflictingRequirementsException;
23 import com.google.common.collect.testing.features.Feature;
24 import com.google.common.collect.testing.features.FeatureUtil;
25 import com.google.common.collect.testing.features.TesterRequirements;
26
27 import junit.framework.Test;
28 import junit.framework.TestCase;
29 import junit.framework.TestSuite;
30
31 import java.lang.reflect.Method;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collection;
35 import java.util.Collections;
36 import java.util.Enumeration;
37 import java.util.HashSet;
38 import java.util.LinkedHashSet;
39 import java.util.List;
40 import java.util.Set;
41 import java.util.logging.Logger;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public abstract class FeatureSpecificTestSuiteBuilder<
60 B extends FeatureSpecificTestSuiteBuilder<B, G>, G> {
61 @SuppressWarnings("unchecked")
62 protected B self() {
63 return (B) this;
64 }
65
66
67
68 private G subjectGenerator;
69
70 private Runnable setUp;
71
72 private Runnable tearDown;
73
74 protected B usingGenerator(G subjectGenerator) {
75 this.subjectGenerator = subjectGenerator;
76 return self();
77 }
78
79 public G getSubjectGenerator() {
80 return subjectGenerator;
81 }
82
83 public B withSetUp(Runnable setUp) {
84 this.setUp = setUp;
85 return self();
86 }
87
88 protected Runnable getSetUp() {
89 return setUp;
90 }
91
92 public B withTearDown(Runnable tearDown) {
93 this.tearDown = tearDown;
94 return self();
95 }
96
97 protected Runnable getTearDown() {
98 return tearDown;
99 }
100
101
102
103 private Set<Feature<?>> features = new LinkedHashSet<Feature<?>>();
104
105
106
107
108
109
110 public B withFeatures(Feature<?>... features) {
111 return withFeatures(Arrays.asList(features));
112 }
113
114 public B withFeatures(Iterable<? extends Feature<?>> features) {
115 for (Feature<?> feature : features) {
116 this.features.add(feature);
117 }
118 return self();
119 }
120
121 public Set<Feature<?>> getFeatures() {
122 return Collections.unmodifiableSet(features);
123 }
124
125
126
127 private String name;
128
129
130 public B named(String name) {
131 if (name.contains("(")) {
132 throw new IllegalArgumentException("Eclipse hides all characters after "
133 + "'('; please use '[]' or other characters instead of parentheses");
134 }
135 this.name = name;
136 return self();
137 }
138
139 public String getName() {
140 return name;
141 }
142
143
144
145 private Set<Method> suppressedTests = new HashSet<Method>();
146
147
148
149
150
151
152
153
154
155 public B suppressing(Method... methods) {
156 return suppressing(Arrays.asList(methods));
157 }
158
159 public B suppressing(Collection<Method> methods) {
160 suppressedTests.addAll(methods);
161 return self();
162 }
163
164 public Set<Method> getSuppressedTests() {
165 return suppressedTests;
166 }
167
168 private static final Logger logger = Logger.getLogger(
169 FeatureSpecificTestSuiteBuilder.class.getName());
170
171
172
173
174
175
176
177
178
179 @SuppressWarnings("unchecked")
180 public TestSuite createTestSuite() {
181 checkCanCreate();
182
183 logger.fine(" Testing: " + name);
184 logger.fine("Features: " + formatFeatureSet(features));
185
186 FeatureUtil.addImpliedFeatures(features);
187
188 logger.fine("Expanded: " + formatFeatureSet(features));
189
190
191 List<Class<? extends AbstractTester>> testers = getTesters();
192
193 TestSuite suite = new TestSuite(name);
194 for (Class<? extends AbstractTester> testerClass : testers) {
195 final TestSuite testerSuite = makeSuiteForTesterClass(
196 (Class<? extends AbstractTester<?>>) testerClass);
197 if (testerSuite.countTestCases() > 0) {
198 suite.addTest(testerSuite);
199 }
200 }
201 return suite;
202 }
203
204
205
206
207
208 protected void checkCanCreate() {
209 if (subjectGenerator == null) {
210 throw new IllegalStateException("Call using() before createTestSuite().");
211 }
212 if (name == null) {
213 throw new IllegalStateException("Call named() before createTestSuite().");
214 }
215 if (features == null) {
216 throw new IllegalStateException(
217 "Call withFeatures() before createTestSuite().");
218 }
219 }
220
221
222 protected abstract List<Class<? extends AbstractTester>>
223 getTesters();
224
225 private boolean matches(Test test) {
226 final Method method;
227 try {
228 method = extractMethod(test);
229 } catch (IllegalArgumentException e) {
230 logger.finer(Platform.format(
231 "%s: including by default: %s", test, e.getMessage()));
232 return true;
233 }
234 if (suppressedTests.contains(method)) {
235 logger.finer(Platform.format(
236 "%s: excluding because it was explicitly suppressed.", test));
237 return false;
238 }
239 final TesterRequirements requirements;
240 try {
241 requirements = FeatureUtil.getTesterRequirements(method);
242 } catch (ConflictingRequirementsException e) {
243 throw new RuntimeException(e);
244 }
245 if (!features.containsAll(requirements.getPresentFeatures())) {
246 if (logger.isLoggable(FINER)) {
247 Set<Feature<?>> missingFeatures =
248 Helpers.copyToSet(requirements.getPresentFeatures());
249 missingFeatures.removeAll(features);
250 logger.finer(Platform.format(
251 "%s: skipping because these features are absent: %s",
252 method, missingFeatures));
253 }
254 return false;
255 }
256 if (intersect(features, requirements.getAbsentFeatures())) {
257 if (logger.isLoggable(FINER)) {
258 Set<Feature<?>> unwantedFeatures =
259 Helpers.copyToSet(requirements.getAbsentFeatures());
260 unwantedFeatures.retainAll(features);
261 logger.finer(Platform.format(
262 "%s: skipping because these features are present: %s",
263 method, unwantedFeatures));
264 }
265 return false;
266 }
267 return true;
268 }
269
270 private static boolean intersect(Set<?> a, Set<?> b) {
271 return !disjoint(a, b);
272 }
273
274 private static Method extractMethod(Test test) {
275 if (test instanceof AbstractTester) {
276 AbstractTester<?> tester = (AbstractTester<?>) test;
277 return Helpers.getMethod(tester.getClass(), tester.getTestMethodName());
278 } else if (test instanceof TestCase) {
279 TestCase testCase = (TestCase) test;
280 return Helpers.getMethod(testCase.getClass(), testCase.getName());
281 } else {
282 throw new IllegalArgumentException(
283 "unable to extract method from test: not a TestCase.");
284 }
285 }
286
287 protected TestSuite makeSuiteForTesterClass(
288 Class<? extends AbstractTester<?>> testerClass) {
289 final TestSuite candidateTests = new TestSuite(testerClass);
290 final TestSuite suite = filterSuite(candidateTests);
291
292 Enumeration<?> allTests = suite.tests();
293 while (allTests.hasMoreElements()) {
294 Object test = allTests.nextElement();
295 if (test instanceof AbstractTester) {
296 @SuppressWarnings("unchecked")
297 AbstractTester<? super G> tester = (AbstractTester<? super G>) test;
298 tester.init(subjectGenerator, name, setUp, tearDown);
299 }
300 }
301
302 return suite;
303 }
304
305 private TestSuite filterSuite(TestSuite suite) {
306 TestSuite filtered = new TestSuite(suite.getName());
307 final Enumeration<?> tests = suite.tests();
308 while (tests.hasMoreElements()) {
309 Test test = (Test) tests.nextElement();
310 if (matches(test)) {
311 filtered.addTest(test);
312 }
313 }
314 return filtered;
315 }
316
317 protected static String formatFeatureSet(Set<? extends Feature<?>> features) {
318 List<String> temp = new ArrayList<String>();
319 for (Feature<?> feature : features) {
320 Object featureAsObject = feature;
321 if (featureAsObject instanceof Enum) {
322 Enum<?> f = (Enum<?>) featureAsObject;
323 temp.add(Platform.classGetSimpleName(
324 f.getDeclaringClass()) + "." + feature);
325 } else {
326 temp.add(feature.toString());
327 }
328 }
329 return temp.toString();
330 }
331 }