Merge lp:~yshavit/namedparameterizedrunner/init_dump into lp:namedparameterizedrunner
- init_dump
- Merge into trunk
Proposed by
Yuval Shavit
Status: | Merged |
---|---|
Merged at revision: | 2 |
Proposed branch: | lp:~yshavit/namedparameterizedrunner/init_dump |
Merge into: | lp:namedparameterizedrunner |
Diff against target: |
1616 lines (+1551/-0) 11 files modified
.bzrignore (+4/-0) pom.xml (+67/-0) src/main/java/com/motpot/npr/Failing.java (+22/-0) src/main/java/com/motpot/npr/NamedParameterizedRunner.java (+570/-0) src/main/java/com/motpot/npr/OnlyIf.java (+20/-0) src/main/java/com/motpot/npr/OnlyIfNot.java (+20/-0) src/main/java/com/motpot/npr/Parameterization.java (+152/-0) src/main/java/com/motpot/npr/ParameterizationBuilder.java (+159/-0) src/test/java/com/motpot/npr/NamedParameterizedRunnerTest.java (+316/-0) src/test/java/com/motpot/npr/OnlyIfUsageTest.java (+68/-0) src/test/java/com/motpot/npr/ParameterizationBuilderTest.java (+153/-0) |
To merge this branch: | bzr merge lp:~yshavit/namedparameterizedrunner/init_dump |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Yuval Shavit | Pending | ||
Review via email:
|
Commit message
Description of the change
Initial commit, copied from lp:akiban-server/trunk
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file '.bzrignore' |
2 | --- .bzrignore 1970-01-01 00:00:00 +0000 |
3 | +++ .bzrignore 2011-09-08 16:01:22 +0000 |
4 | @@ -0,0 +1,4 @@ |
5 | +named-parameterized-runner.iml |
6 | +named-parameterized-runner.ipr |
7 | +named-parameterized-runner.iws |
8 | +./target |
9 | |
10 | === added file 'pom.xml' |
11 | --- pom.xml 1970-01-01 00:00:00 +0000 |
12 | +++ pom.xml 2011-09-08 16:01:22 +0000 |
13 | @@ -0,0 +1,67 @@ |
14 | +<?xml version="1.0" encoding="UTF-8"?> |
15 | +<project> |
16 | + <modelVersion>4.0.0</modelVersion> |
17 | + <groupId>com.motpot</groupId> |
18 | + <artifactId>named-parameterized-runner</artifactId> |
19 | + <packaging>jar</packaging> |
20 | + <version>0.8.0</version> |
21 | + <name>named-parameterized-runner</name> |
22 | + <dependencies> |
23 | + <dependency> |
24 | + <groupId>junit</groupId> |
25 | + <artifactId>junit</artifactId> |
26 | + <version>4.8.1</version> |
27 | + </dependency> |
28 | + <dependency> |
29 | + <groupId>org.slf4j</groupId> |
30 | + <artifactId>slf4j-api</artifactId> |
31 | + <version>1.6.1</version> |
32 | + </dependency> |
33 | + </dependencies> |
34 | + <build> |
35 | + <plugins> |
36 | + <plugin> |
37 | + <groupId>org.apache.maven.plugins</groupId> |
38 | + <artifactId>maven-jar-plugin</artifactId> |
39 | + <version>2.2</version> |
40 | + <executions> |
41 | + <execution> |
42 | + <goals> |
43 | + <goal>test-jar</goal> |
44 | + </goals> |
45 | + </execution> |
46 | + </executions> |
47 | + </plugin> |
48 | + <plugin> |
49 | + <groupId>org.apache.maven.plugins</groupId> |
50 | + <artifactId>maven-compiler-plugin</artifactId> |
51 | + <version>2.3.2</version> |
52 | + <configuration> |
53 | + <source>1.6</source> |
54 | + <target>1.6</target> |
55 | + </configuration> |
56 | + </plugin> |
57 | + <plugin> |
58 | + <groupId>org.apache.maven.plugins</groupId> |
59 | + <artifactId>maven-source-plugin</artifactId> |
60 | + <version>2.1.2</version> |
61 | + <executions> |
62 | + <execution> |
63 | + <id>attach-sources</id> |
64 | + <phase>package</phase> |
65 | + <goals> |
66 | + <goal>jar</goal> |
67 | + </goals> |
68 | + </execution> |
69 | + </executions> |
70 | + </plugin> |
71 | + <plugin> |
72 | + <groupId>org.apache.maven.plugins</groupId> |
73 | + <artifactId>maven-surefire-plugin</artifactId> |
74 | + <version>2.6</version> |
75 | + </plugin> |
76 | + </plugins> |
77 | +</build> |
78 | + |
79 | +</project> |
80 | + |
81 | |
82 | === added directory 'src' |
83 | === added directory 'src/main' |
84 | === added directory 'src/main/java' |
85 | === added directory 'src/main/java/com' |
86 | === added directory 'src/main/java/com/motpot' |
87 | === added directory 'src/main/java/com/motpot/npr' |
88 | === added file 'src/main/java/com/motpot/npr/Failing.java' |
89 | --- src/main/java/com/motpot/npr/Failing.java 1970-01-01 00:00:00 +0000 |
90 | +++ src/main/java/com/motpot/npr/Failing.java 2011-09-08 16:01:22 +0000 |
91 | @@ -0,0 +1,22 @@ |
92 | +package com.motpot.npr; |
93 | + |
94 | +import java.lang.annotation.ElementType; |
95 | +import java.lang.annotation.Retention; |
96 | +import java.lang.annotation.RetentionPolicy; |
97 | +import java.lang.annotation.Target; |
98 | + |
99 | +/** |
100 | + * Represents a failing test. |
101 | + */ |
102 | +@Retention(RetentionPolicy.RUNTIME) |
103 | +@Target(ElementType.METHOD) |
104 | +public @interface Failing |
105 | +{ |
106 | + /** |
107 | + * For parameterized tests, the optional list of parameterizations that are failing. |
108 | + * |
109 | + * This is ignored by non-parameterized class runners. For parameterized runners, |
110 | + * not specifying this value means that all parameterizations are failing. |
111 | + */ |
112 | + String[] value() default {}; |
113 | +} |
114 | |
115 | === added file 'src/main/java/com/motpot/npr/NamedParameterizedRunner.java' |
116 | --- src/main/java/com/motpot/npr/NamedParameterizedRunner.java 1970-01-01 00:00:00 +0000 |
117 | +++ src/main/java/com/motpot/npr/NamedParameterizedRunner.java 2011-09-08 16:01:22 +0000 |
118 | @@ -0,0 +1,570 @@ |
119 | +package com.motpot.npr; |
120 | + |
121 | +import org.slf4j.Logger; |
122 | +import org.slf4j.LoggerFactory; |
123 | +import org.junit.Ignore; |
124 | +import org.junit.internal.builders.IgnoredClassRunner; |
125 | +import org.junit.runner.Runner; |
126 | +import org.junit.runner.notification.RunNotifier; |
127 | +import org.junit.runners.BlockJUnit4ClassRunner; |
128 | +import org.junit.runners.Suite; |
129 | +import org.junit.runners.model.FrameworkMethod; |
130 | +import org.junit.runners.model.InitializationError; |
131 | +import org.junit.runners.model.Statement; |
132 | +import org.junit.runners.model.TestClass; |
133 | + |
134 | +import java.lang.annotation.ElementType; |
135 | +import java.lang.annotation.Retention; |
136 | +import java.lang.annotation.RetentionPolicy; |
137 | +import java.lang.annotation.Target; |
138 | +import java.lang.reflect.Field; |
139 | +import java.lang.reflect.Method; |
140 | +import java.lang.reflect.Modifier; |
141 | +import java.lang.reflect.ParameterizedType; |
142 | +import java.lang.reflect.Type; |
143 | +import java.util.*; |
144 | +import java.util.regex.Pattern; |
145 | + |
146 | +public final class NamedParameterizedRunner extends Suite |
147 | +{ |
148 | + /** |
149 | + * <p>Parameterization override filter (works by name).</p> |
150 | + * |
151 | + * <p>If this property is set, then only parameterization names that match its value will be processed. These |
152 | + * names behave like @Failing names (in terms of regexes, etc). If this property is set and a test that matches |
153 | + * it is marked as @Failing, that test will still get run. For instance, if a parameterization named |
154 | + * <tt>myFooTest</tt> is marked as failing for a given test (either because the entire parameterization is marked |
155 | + * as failing, or because of a <tt>@Failing</tt> annotation on the method), and if you have a system property |
156 | + * <tt>{@value} == "/myFoo/"</tt>, then the test <em>will</em> be run. |
157 | + */ |
158 | + public final static String PARAMETERIZATION_OVERRIDE = "test.params"; |
159 | + /** |
160 | + * <p>Annotation for a method which provides parameters for an |
161 | + * {@link NamedParameterizedRunner} suite.</p> |
162 | + * |
163 | + * <p>A class that is run with {@linkplain NamedParameterizedRunner} <em>must</em> have exactly one method |
164 | + * marked with this annotation, and that method must:</p> |
165 | + * <ul> |
166 | + * <li>be public</li> |
167 | + * <li>be static</li> |
168 | + * <li>take no arguments</li> |
169 | + * <li>return Collection of {@link Parameterization} objects.</li> |
170 | + * </ul> |
171 | + */ |
172 | + @Retention(RetentionPolicy.RUNTIME) |
173 | + @Target(ElementType.METHOD) |
174 | + public static @interface TestParameters { |
175 | + } |
176 | + |
177 | + private final static Logger logger = LoggerFactory.getLogger(NamedParameterizedRunner.class); |
178 | + private final List<Runner> runners; |
179 | + |
180 | + /** |
181 | + * Adapted from {@link org.junit.runners.Parameterized}'s nested class |
182 | + * @see |
183 | + */ |
184 | + static class ReifiedParamRunner extends BlockJUnit4ClassRunner |
185 | + { |
186 | + private final Parameterization parameterization; |
187 | + private final boolean overrideOn; |
188 | + private Object testObject = null; |
189 | + |
190 | + private static class OnlyIfErrorFrameworkMethod extends FrameworkMethod { |
191 | + private final OnlyIfException exception; |
192 | + |
193 | + private OnlyIfErrorFrameworkMethod(Method method, OnlyIfException exception) { |
194 | + super(method); |
195 | + this.exception = exception; |
196 | + } |
197 | + |
198 | + @Override |
199 | + public Object invokeExplosively(Object target, Object... params) throws Throwable { |
200 | + throw exception; |
201 | + } |
202 | + } |
203 | + |
204 | + public ReifiedParamRunner(Class<?> klass, Parameterization parameterization, boolean overrideOn) |
205 | + throws InitializationError |
206 | + { |
207 | + super(klass); |
208 | + this.parameterization = parameterization; |
209 | + this.overrideOn = overrideOn; |
210 | + } |
211 | + |
212 | + /** |
213 | + * For debugging. |
214 | + * @return parameterization.toString() |
215 | + * @throws NullPointerException if parameterization is null |
216 | + */ |
217 | + String paramToString() |
218 | + { |
219 | + return parameterization.toString(); |
220 | + } |
221 | + |
222 | + /** |
223 | + * For debugging |
224 | + * @return the override value |
225 | + */ |
226 | + boolean overrideOn() |
227 | + { |
228 | + return overrideOn; |
229 | + } |
230 | + |
231 | + /** |
232 | + * For debugging. |
233 | + * @param name the name of the child FrameworkMethod to get |
234 | + * @return the child, or null if it doesn't exist |
235 | + */ |
236 | + FrameworkMethod getChild(String name) |
237 | + { |
238 | + for (FrameworkMethod method : getChildren()) |
239 | + { |
240 | + if (method.getMethod().toString().equals(name)) |
241 | + { |
242 | + return method; |
243 | + } |
244 | + } |
245 | + return null; |
246 | + } |
247 | + |
248 | + /** |
249 | + * For debugging |
250 | + * @return the number of children in this runner |
251 | + */ |
252 | + int getChildrenCount() { |
253 | + return getChildren().size(); |
254 | + } |
255 | + |
256 | + /** |
257 | + * For debugging (and only for assertion messages) |
258 | + * @return shows human-readable info about the children |
259 | + */ |
260 | + String describeChildren() { |
261 | + List<FrameworkMethod> children = getChildren(); |
262 | + if (children == null) { |
263 | + return "null"; |
264 | + } |
265 | + List<String> descriptions = new ArrayList<String>(children.size()); |
266 | + for (FrameworkMethod method : children) { |
267 | + descriptions.add( method.getName() ); |
268 | + } |
269 | + return descriptions.toString(); |
270 | + } |
271 | + |
272 | + @Override |
273 | + public Object createTest() throws Exception |
274 | + { |
275 | + if (testObject != null) { |
276 | + return testObject; |
277 | + } |
278 | + try |
279 | + { |
280 | + testObject = getTestClass().getOnlyConstructor().newInstance(parameterization.getArguments()); |
281 | + return testObject; |
282 | + } |
283 | + catch(IllegalArgumentException e) |
284 | + { |
285 | + throw new IllegalArgumentException("parameters: " + Arrays.toString(parameterization.getArguments()), e); |
286 | + } |
287 | + } |
288 | + |
289 | + @Override |
290 | + protected List<FrameworkMethod> getChildren() { |
291 | + List<FrameworkMethod> ret = super.getChildren(); |
292 | + for(ListIterator<FrameworkMethod> iter = ret.listIterator(); iter.hasNext(); ) { |
293 | + FrameworkMethod frameworkMethod = iter.next(); |
294 | + try { |
295 | + if (!onlyIfFilter(frameworkMethod)) { |
296 | + iter.remove(); |
297 | + } |
298 | + } catch (OnlyIfException e) { |
299 | + iter.set(new OnlyIfErrorFrameworkMethod(frameworkMethod.getMethod(), e)); |
300 | + } |
301 | + } |
302 | + return ret; |
303 | + } |
304 | + |
305 | + @Override |
306 | + public String getName() |
307 | + { |
308 | + return parameterization.getName(); |
309 | + } |
310 | + |
311 | + @Override |
312 | + public String testName(FrameworkMethod method) |
313 | + { |
314 | + return String.format("%s [%s]", method.getName(), parameterization.getName()); |
315 | + } |
316 | + |
317 | + @Override |
318 | + protected void validateConstructor(List<Throwable> errors) |
319 | + { |
320 | + validateOnlyOneConstructor(errors); |
321 | + } |
322 | + |
323 | + @Override |
324 | + protected Statement classBlock(RunNotifier notifier) |
325 | + { |
326 | + return childrenInvoker(notifier); |
327 | + } |
328 | + |
329 | + @Override |
330 | + protected void runChild(FrameworkMethod method, RunNotifier notifier) |
331 | + { |
332 | + if (expectedToPass(method)) |
333 | + { |
334 | + super.runChild(method, notifier); |
335 | + } |
336 | + else |
337 | + { |
338 | + notifier.fireTestIgnored( describeChild(method)); |
339 | + } |
340 | + } |
341 | + |
342 | + private static class OnlyIfException extends Exception { |
343 | + private OnlyIfException(String message, Throwable cause) { |
344 | + super(message, cause); |
345 | + } |
346 | + |
347 | + private OnlyIfException(String message) { |
348 | + super(message); |
349 | + } |
350 | + } |
351 | + |
352 | + boolean onlyIfFilter(FrameworkMethod method) throws OnlyIfException { |
353 | + OnlyIf onlyIf = method.getAnnotation(OnlyIf.class); |
354 | + if (onlyIf != null) { |
355 | + if (!runOnlyIf(onlyIf.value(), true)) { |
356 | + return false; |
357 | + } |
358 | + } |
359 | + OnlyIfNot onlyIfNot = method.getAnnotation(OnlyIfNot.class); |
360 | + return onlyIfNot == null || runOnlyIf(onlyIfNot.value(), false); |
361 | + } |
362 | + |
363 | + private boolean runOnlyIf(String methodName, boolean mustEqual) throws OnlyIfException { |
364 | + boolean result; |
365 | + if (methodName.endsWith("()")) { |
366 | + methodName = methodName.substring(0, methodName.length() - 2); // snip off the "()" |
367 | + result = execOnlyIfMethod(methodName); |
368 | + } |
369 | + else { |
370 | + result = getOnlyIfField(methodName); |
371 | + } |
372 | + return result == mustEqual; |
373 | + } |
374 | + |
375 | + |
376 | + private boolean execOnlyIfMethod(String methodName) throws OnlyIfException { |
377 | + final Object result; |
378 | + final Method onlyIfMethod; |
379 | + final Class<?> testClass = getTestClass().getJavaClass(); |
380 | + try { |
381 | + onlyIfMethod = testClass.getMethod(methodName); |
382 | + } catch (NoSuchMethodException e) { |
383 | + throw new OnlyIfException("no such method: public boolean " + methodName + "()", e); |
384 | + } |
385 | + if (!onlyIfMethod.getReturnType().equals(boolean.class)) { |
386 | + throw new OnlyIfException("no such method: public boolean " + methodName + "()"); |
387 | + } |
388 | + try { |
389 | + Object test = createTest(); |
390 | + result = onlyIfMethod.invoke(test); |
391 | + } catch (Exception e) { |
392 | + throw new OnlyIfException("couldn't invoke " + methodName + "()", e); |
393 | + } |
394 | + try { |
395 | + return (Boolean) result; |
396 | + } catch (ClassCastException e) { |
397 | + throw new OnlyIfException("method " + methodName + "() didn't return boolean", e); |
398 | + } |
399 | + } |
400 | + |
401 | + private boolean getOnlyIfField(String fieldName) throws OnlyIfException { |
402 | + final Class<?> testClass = getTestClass().getJavaClass(); |
403 | + final Field field; |
404 | + try { |
405 | + field = testClass.getField(fieldName); |
406 | + } catch (Exception e) { |
407 | + throw new OnlyIfException("Can't get field: " + fieldName, e); |
408 | + } |
409 | + try { |
410 | + Object test = createTest(); |
411 | + return field.getBoolean(test); |
412 | + } catch (Exception e) { |
413 | + throw new OnlyIfException("couldn't get boolean field " + fieldName + "()", e); |
414 | + } |
415 | + } |
416 | + |
417 | + boolean expectedToPass(FrameworkMethod method) |
418 | + { |
419 | + if (overrideOn) |
420 | + { |
421 | + return true; |
422 | + } |
423 | + if (! parameterization.expectedToPass()) |
424 | + { |
425 | + return false; |
426 | + } |
427 | + Failing failing = method.getAnnotation(Failing.class); |
428 | + if (failing == null) |
429 | + { |
430 | + return true; |
431 | + } |
432 | + if (failing.value().length == 0) |
433 | + { |
434 | + return false; |
435 | + } |
436 | + |
437 | + for (final String paramName : failing.value()) |
438 | + { |
439 | + if (paramNameUsesRegex(paramName)) |
440 | + { |
441 | + if (paramNameMatchesRegex(parameterization.getName(), paramName)) |
442 | + { |
443 | + return false; |
444 | + } |
445 | + } |
446 | + else if (parameterization.getName().equals(paramName)) |
447 | + { |
448 | + return false; |
449 | + } |
450 | + } |
451 | + return true; |
452 | + } |
453 | + } |
454 | + |
455 | + static boolean paramNameUsesRegex(String paramName) |
456 | + { |
457 | + return paramName.length() > 2 |
458 | + && (paramName.charAt(0)=='/') |
459 | + && (paramName.charAt(paramName.length()-1)=='/'); |
460 | + } |
461 | + |
462 | + /** |
463 | + * Returns whether a given parameterization matches a given regex. The regex should be in "/regex/" format. |
464 | + * @param paramName the haystack, as it were |
465 | + * @param paramRegex a string that starts and ends with '/', and between them has a needle. |
466 | + * @return whether the paramRegex is found in paramName |
467 | + */ |
468 | + static boolean paramNameMatchesRegex(String paramName, String paramRegex) |
469 | + { |
470 | + assert paramRegex.charAt(0)=='/'; |
471 | + assert paramRegex.charAt(paramRegex.length()-1)=='/'; |
472 | + assert paramRegex.length() > 2; |
473 | + String regex = paramRegex.substring(1, paramRegex.length()-1); |
474 | + return Pattern.compile(regex).matcher(paramName).find(); |
475 | + } |
476 | + |
477 | + @SuppressWarnings("unused") // Invoked by reflection |
478 | + public NamedParameterizedRunner(Class<?> klass) throws Throwable |
479 | + { |
480 | + super(klass, Collections.<Runner>emptyList()); |
481 | + |
482 | + if (getTestClass().getJavaClass().getAnnotation(Ignore.class) != null) |
483 | + { |
484 | + runners = Collections.unmodifiableList(Arrays.asList((Runner)new IgnoredClassRunner(klass))); |
485 | + return; |
486 | + } |
487 | + |
488 | + List<Runner> localRunners = new LinkedList<Runner>(); |
489 | + |
490 | + Collection<Parameterization> parameterizations = getParameterizations(); |
491 | + checkFailingParameterizations(parameterizations); |
492 | + |
493 | + final String override = System.getProperty(PARAMETERIZATION_OVERRIDE); |
494 | + final boolean overrideIsRegex = (override != null) && paramNameUsesRegex(override); |
495 | + if (override != null) |
496 | + { |
497 | + String msg = "Override is set to"; |
498 | + if (overrideIsRegex) |
499 | + { |
500 | + msg += " regex"; |
501 | + } |
502 | + msg += ":" + override; |
503 | + logger.info(msg); |
504 | + } |
505 | + for (Parameterization param : parameterizations) |
506 | + { |
507 | + final boolean useThisParam; |
508 | + if (override == null) |
509 | + { |
510 | + useThisParam = true; |
511 | + } |
512 | + else if (overrideIsRegex) |
513 | + { |
514 | + useThisParam = paramNameMatchesRegex(param.getName(), override); |
515 | + } |
516 | + else |
517 | + { |
518 | + useThisParam = param.getName().equals(override); |
519 | + } |
520 | + if (useThisParam) |
521 | + { |
522 | + if (override != null) |
523 | + { |
524 | + logger.info("Adding parameterization: " + param.getName()); |
525 | + } |
526 | + localRunners.add(new ReifiedParamRunner(getTestClass().getJavaClass(), param, override != null)); |
527 | + } |
528 | + } |
529 | + runners = Collections.unmodifiableList(localRunners); |
530 | + } |
531 | + |
532 | + @Override |
533 | + protected List<Runner> getChildren() |
534 | + { |
535 | + return runners; |
536 | + } |
537 | + |
538 | + /** |
539 | + * Gets the parameterization |
540 | + * @return the parameterization collection |
541 | + * @throws Throwable if the annotation requirements are not met, or if there's an error in invoking |
542 | + * the class's "get parameterizations" method. |
543 | + */ |
544 | + private Collection<Parameterization> getParameterizations() throws Throwable |
545 | + { |
546 | + TestClass cls = getTestClass(); |
547 | + List<FrameworkMethod> methods = cls.getAnnotatedMethods(TestParameters.class); |
548 | + |
549 | + if (methods.size() != 1) |
550 | + { |
551 | + throw new Exception("class " + cls.getName() + " must have exactly 1 method annotated with " |
552 | + + TestParameters.class.getSimpleName() +"; found " + methods.size()); |
553 | + } |
554 | + |
555 | + FrameworkMethod method = methods.get(0); |
556 | + checkParameterizationMethod(method); |
557 | + |
558 | + @SuppressWarnings("unchecked") |
559 | + Collection<Parameterization> ret = (Collection<Parameterization>) method.invokeExplosively(null); |
560 | + checkParameterizations(ret); |
561 | + return ret; |
562 | + } |
563 | + |
564 | + /** |
565 | + * Checks the parameterizations collection for correctness |
566 | + * @param collection the collection |
567 | + * @throws Exception if the collection was null or contained duplicates |
568 | + */ |
569 | + private void checkParameterizations(Collection<Parameterization> collection) throws Exception |
570 | + { |
571 | + if (collection == null) |
572 | + { |
573 | + throw new Exception("parameterizations collection may not return null"); |
574 | + } |
575 | + Set<String> duplicates = new HashSet<String>(); |
576 | + Set<Parameterization> checkSet = new HashSet<Parameterization>(); |
577 | + for (Parameterization param : collection) |
578 | + { |
579 | + if (param == null) |
580 | + { |
581 | + throw new Exception("parameterization collection may not contain null values"); |
582 | + } |
583 | + if (!checkSet.add(param)) |
584 | + { |
585 | + duplicates.add(param.getName()); |
586 | + } |
587 | + } |
588 | + if (duplicates.size() != 0) |
589 | + { |
590 | + throw new Exception("duplicated parameterization names: " + duplicates); |
591 | + } |
592 | + } |
593 | + |
594 | + /** |
595 | + * Checks the @Failing annotations on @Test methods. |
596 | + * |
597 | + * Specifically, this checks that no item in the @Failing list is repeated, and that each item in that list |
598 | + * corresponds to an actual parameterization. |
599 | + * @param parameterizations the known parameterizations; will not be modified. |
600 | + * @throws org.junit.runners.model.InitializationError if a check fails |
601 | + */ |
602 | + private void checkFailingParameterizations(Collection<Parameterization> parameterizations) throws InitializationError |
603 | + { |
604 | + Collection<String> paramNames = new HashSet<String>(); |
605 | + for (Parameterization param : parameterizations) |
606 | + { |
607 | + boolean added = paramNames.add(param.getName()); |
608 | + assert added : "uncaught duplicate: " + param; |
609 | + } |
610 | + |
611 | + Set<String> duplicatesChecker = new HashSet<String>(); |
612 | + for(FrameworkMethod method : super.getTestClass().getAnnotatedMethods(Failing.class)) |
613 | + { |
614 | + String[] failing = method.getAnnotation(Failing.class).value(); |
615 | + if (failing != null) |
616 | + { |
617 | + for(int i=0; i < failing.length; ++i) |
618 | + { |
619 | + if (!duplicatesChecker.add(failing[i])) |
620 | + { |
621 | + throw new InitializationError("duplicate parameterization name in @Failing list: " |
622 | + + method.getName() + "[" + i + "]<" + failing[i] + ">"); |
623 | + } |
624 | + if ( !(paramNameUsesRegex(failing[i]) || paramNames.contains(failing[i])) ) |
625 | + { |
626 | + throw new InitializationError("parameterization is marked as failing, " |
627 | + + "but isn't in list of parameterizations: " |
628 | + + method.getName() + "[" + i + "]<" + failing[i] +"> not in " + paramNames); |
629 | + } |
630 | + } |
631 | + duplicatesChecker.clear(); |
632 | + } |
633 | + } |
634 | + } |
635 | + |
636 | + |
637 | + /** |
638 | + * Checks the parameterization method for correctness. |
639 | + * @param frameworkMethod the method |
640 | + * @throws Exception if the annotation requirements are not met |
641 | + */ |
642 | + private static void checkParameterizationMethod(FrameworkMethod frameworkMethod) throws Exception |
643 | + { |
644 | + final Method method = frameworkMethod.getMethod(); |
645 | + |
646 | + if (method.getParameterTypes().length != 0) |
647 | + { |
648 | + throw new Exception(complainingThat(method, "must take no arguments")); |
649 | + } |
650 | + |
651 | + final int modifiers = frameworkMethod.getMethod().getModifiers(); |
652 | + if (! Modifier.isPublic(modifiers)) |
653 | + { |
654 | + throw new Exception(complainingThat(method, "must be public")); |
655 | + } |
656 | + if (! Modifier.isStatic(modifiers)) |
657 | + { |
658 | + throw new Exception(complainingThat(method, "must be static")); |
659 | + } |
660 | + |
661 | + final Type genericRet = method.getGenericReturnType(); |
662 | + final String mustReturnCorrectly = "must return Collection of " + Parameterization.class; |
663 | + if (! (genericRet instanceof ParameterizedType)) |
664 | + { |
665 | + throw new Exception(complainingThat(method, mustReturnCorrectly)); |
666 | + } |
667 | + final ParameterizedType ret = (ParameterizedType) genericRet; |
668 | + if (!(ret.getRawType() instanceof Class) && Collection.class.isAssignableFrom((Class)ret.getRawType())) |
669 | + { |
670 | + throw new Exception(complainingThat(method, mustReturnCorrectly)); |
671 | + } |
672 | + if (ret.getActualTypeArguments().length != 1) |
673 | + { |
674 | + throw new Exception(complainingThat(method, mustReturnCorrectly + "; raw Collection is not allowed")); |
675 | + } |
676 | + if (!ret.getActualTypeArguments()[0].equals(Parameterization.class)) |
677 | + { |
678 | + throw new Exception(complainingThat(method, mustReturnCorrectly)); |
679 | + } |
680 | + } |
681 | + |
682 | + private static String complainingThat(Method method, String mustBe) |
683 | + { |
684 | + assert method != null; |
685 | + assert mustBe != null; |
686 | + return String.format("%s.%s() %s", method.getDeclaringClass().getName(), method.getName(), mustBe); |
687 | + } |
688 | +} |
689 | |
690 | === added file 'src/main/java/com/motpot/npr/OnlyIf.java' |
691 | --- src/main/java/com/motpot/npr/OnlyIf.java 1970-01-01 00:00:00 +0000 |
692 | +++ src/main/java/com/motpot/npr/OnlyIf.java 2011-09-08 16:01:22 +0000 |
693 | @@ -0,0 +1,20 @@ |
694 | +package com.motpot.npr; |
695 | + |
696 | +import java.lang.annotation.ElementType; |
697 | +import java.lang.annotation.Retention; |
698 | +import java.lang.annotation.RetentionPolicy; |
699 | +import java.lang.annotation.Target; |
700 | + |
701 | +/** |
702 | + * Represents a condition for running a test. |
703 | + */ |
704 | +@Retention(RetentionPolicy.RUNTIME) |
705 | +@Target(ElementType.METHOD) |
706 | +public @interface OnlyIf |
707 | +{ |
708 | + /** |
709 | + * The method that should be invoked to see if this annotated method should be run |
710 | + * @return |
711 | + */ |
712 | + String value(); |
713 | +} |
714 | |
715 | === added file 'src/main/java/com/motpot/npr/OnlyIfNot.java' |
716 | --- src/main/java/com/motpot/npr/OnlyIfNot.java 1970-01-01 00:00:00 +0000 |
717 | +++ src/main/java/com/motpot/npr/OnlyIfNot.java 2011-09-08 16:01:22 +0000 |
718 | @@ -0,0 +1,20 @@ |
719 | +package com.motpot.npr; |
720 | + |
721 | +import java.lang.annotation.ElementType; |
722 | +import java.lang.annotation.Retention; |
723 | +import java.lang.annotation.RetentionPolicy; |
724 | +import java.lang.annotation.Target; |
725 | + |
726 | +/** |
727 | + * Represents a condition for running a test. |
728 | + */ |
729 | +@Retention(RetentionPolicy.RUNTIME) |
730 | +@Target(ElementType.METHOD) |
731 | +public @interface OnlyIfNot |
732 | +{ |
733 | + /** |
734 | + * The method that should be invoked to see if this annotated method should be run |
735 | + * @return |
736 | + */ |
737 | + String value(); |
738 | +} |
739 | |
740 | === added file 'src/main/java/com/motpot/npr/Parameterization.java' |
741 | --- src/main/java/com/motpot/npr/Parameterization.java 1970-01-01 00:00:00 +0000 |
742 | +++ src/main/java/com/motpot/npr/Parameterization.java 2011-09-08 16:01:22 +0000 |
743 | @@ -0,0 +1,152 @@ |
744 | +package com.motpot.npr; |
745 | + |
746 | +import java.util.Arrays; |
747 | +import java.util.Iterator; |
748 | +import java.util.LinkedList; |
749 | +import java.util.List; |
750 | + |
751 | +public final class Parameterization |
752 | +{ |
753 | + private String name; |
754 | + private final List<Object> args; |
755 | + private boolean expectedToPass; |
756 | + |
757 | + public static Parameterization create(String name, Object... args) |
758 | + { |
759 | + return new Parameterization(name, true, args); |
760 | + } |
761 | + |
762 | + public static Parameterization failing(String name, Object... args) |
763 | + { |
764 | + return new Parameterization(name, false, args); |
765 | + } |
766 | + |
767 | + public Parameterization(String name, boolean isPassing, Object... args) |
768 | + { |
769 | + if (name == null) { |
770 | + throw new IllegalArgumentException("name can't be null"); |
771 | + } |
772 | + if (args == null) { |
773 | + throw new IllegalArgumentException("args can't be null"); |
774 | + } |
775 | + this.name = name; |
776 | + this.args = new LinkedList<Object>(Arrays.asList(args)); |
777 | + this.expectedToPass = isPassing; |
778 | + } |
779 | + |
780 | + /** |
781 | + * <p>Creates a copy of this parameterization.</p> |
782 | + * |
783 | + * <p>The original parameterization's arguments list is copied shallowly.</p> |
784 | + * @param copyFrom the original parameterization |
785 | + */ |
786 | + public Parameterization(Parameterization copyFrom) |
787 | + { |
788 | + this.name = copyFrom.name; |
789 | + this.args = new LinkedList<Object>(copyFrom.args); |
790 | + this.expectedToPass = copyFrom.expectedToPass; |
791 | + } |
792 | + |
793 | + public String getName() |
794 | + { |
795 | + return name; |
796 | + } |
797 | + |
798 | + public void setName(String name) |
799 | + { |
800 | + this.name = name; |
801 | + } |
802 | + |
803 | + /** |
804 | + * Returns this parameterization's arguments as an Object array. |
805 | + * |
806 | + * This array is <em>not</em> backed by the parameterization, so changes in one are not reflected in the other. |
807 | + * However, the array is a shallow copy, so modifying an element of the parameterization in the list will be |
808 | + * reflected in the array, and the vice versa. |
809 | + * @return the arguments |
810 | + */ |
811 | + public Object[] getArguments() |
812 | + { |
813 | + return args.toArray(); |
814 | + } |
815 | + |
816 | + /** |
817 | + * Returns this parameterization's arguments as a list. |
818 | + * |
819 | + * This list is backed by the parameterization, so changes to one are reflected in the other. |
820 | + * @return a list of arguments, backed by the parameterization |
821 | + */ |
822 | + public List<Object> getArgsAsList() |
823 | + { |
824 | + return args; |
825 | + } |
826 | + |
827 | + public boolean expectedToPass() |
828 | + { |
829 | + return expectedToPass; |
830 | + } |
831 | + |
832 | + public void setExpectedToPass(boolean expectedToPass) |
833 | + { |
834 | + this.expectedToPass = expectedToPass; |
835 | + } |
836 | + |
837 | + @Override |
838 | + public boolean equals(Object o) |
839 | + { |
840 | + if (this == o) |
841 | + { |
842 | + return true; |
843 | + } |
844 | + if (o == null || getClass() != o.getClass()) |
845 | + { |
846 | + return false; |
847 | + } |
848 | + Parameterization that = (Parameterization) o; |
849 | + return name.equals(that.name); |
850 | + } |
851 | + |
852 | + @Override |
853 | + public int hashCode() |
854 | + { |
855 | + return name.hashCode(); |
856 | + } |
857 | + |
858 | + public boolean equivalent(Parameterization other) |
859 | + { |
860 | + if (!this.name.equals(other.name)) |
861 | + return false; |
862 | + if (this.expectedToPass != other.expectedToPass) |
863 | + return false; |
864 | + if (this.args.size() != other.args.size()) |
865 | + return false; |
866 | + |
867 | + Iterator<Object> myIter = this.args.iterator(); |
868 | + Iterator<Object> otherIter = other.args.iterator(); |
869 | + |
870 | + while (myIter.hasNext()) |
871 | + { |
872 | + Object mine = myIter.next(); |
873 | + Object his = otherIter.next(); |
874 | + |
875 | + if (mine == null) |
876 | + { |
877 | + if (his != null) |
878 | + { |
879 | + return false; |
880 | + } |
881 | + } |
882 | + else if (!mine.equals(his)) |
883 | + { |
884 | + return false; |
885 | + } |
886 | + } |
887 | + return true; |
888 | + } |
889 | + |
890 | + @Override |
891 | + public String toString() |
892 | + { |
893 | + return "Parameterization[" + (expectedToPass? "PASSING " : "FAILING ") + name + ": " + args + " ]"; |
894 | + } |
895 | +} |
896 | |
897 | === added file 'src/main/java/com/motpot/npr/ParameterizationBuilder.java' |
898 | --- src/main/java/com/motpot/npr/ParameterizationBuilder.java 1970-01-01 00:00:00 +0000 |
899 | +++ src/main/java/com/motpot/npr/ParameterizationBuilder.java 2011-09-08 16:01:22 +0000 |
900 | @@ -0,0 +1,159 @@ |
901 | +package com.motpot.npr; |
902 | + |
903 | +import java.util.LinkedList; |
904 | +import java.util.List; |
905 | + |
906 | +public final class ParameterizationBuilder |
907 | +{ |
908 | + private final List<Parameterization> list; |
909 | + |
910 | + /** |
911 | + * Creates a builder with no starting parameterizations. |
912 | + */ |
913 | + public ParameterizationBuilder() |
914 | + { |
915 | + this(new LinkedList<Parameterization>()); |
916 | + } |
917 | + |
918 | + /** |
919 | + * Creates a builder backed by a list. Changes to the one will be reflected in the other, and {@linkplain #asList()} |
920 | + * will return the same instance as is passed to this method. |
921 | + * @param backingList The list that backs this builder. |
922 | + */ |
923 | + public ParameterizationBuilder(List<Parameterization> backingList) |
924 | + { |
925 | + list = backingList; |
926 | + } |
927 | + |
928 | + /** |
929 | + * Adds a parameterization to the end of the list. |
930 | + * @param name the parameterization's name |
931 | + * @param args the parameterization's arguments |
932 | + */ |
933 | + public void add(String name, Object... args) |
934 | + { |
935 | + list.add(Parameterization.create(name, args)); |
936 | + } |
937 | + |
938 | + /** |
939 | + * Adds a parameterization that's expected to fail to the end of the list |
940 | + * @param name the parameterization's name |
941 | + * @param args the parameterization's arguments |
942 | + */ |
943 | + public void addFailing(String name, Object... args) |
944 | + { |
945 | + list.add(Parameterization.failing(name, args)); |
946 | + } |
947 | + |
948 | + /** |
949 | + * Adds a parameterization to the end of the list. |
950 | + * @param name the parameterization's name |
951 | + * @param expectedToPass whether the parameterization is expected to pass any tests |
952 | + * @param args the parameterization's arguments |
953 | + */ |
954 | + public void create(String name, boolean expectedToPass, Object... args) |
955 | + { |
956 | + list.add(new Parameterization(name, expectedToPass, args)); |
957 | + } |
958 | + |
959 | + /** |
960 | + * <p>Performs a cartesian product of parameterizations by prepending each of the incoming args to each |
961 | + * existing parameter's arg list.</p> |
962 | + * |
963 | + * For instance, if you have parameters <tt>[ ("A", 1, 2, 3) and ("B", 4, 5, 6) ]</tt>, and you called |
964 | + * <tt>multiplyParametersByPrepending("foo-", 'c', "bar", 'd')</tt>, you would get the following |
965 | + * parameterizations, in this order: |
966 | + * <ol> |
967 | + * <li>"foo-A", 'c', 1, 2, 3</li> |
968 | + * <li>"bar-A", 'd', 1, 2, 3</li> |
969 | + * <li>"foo-B", 'c', 4, 5, 6</li> |
970 | + * <li>"bar-B", 'd', 1, 2, 3</li> |
971 | + * </ol> |
972 | + * @param args the list of named args to multiply by. Every other element (starting with the first) in the list must |
973 | + * be a String that represents the name prefix; the element following each prefix is the argument to append to |
974 | + * the args list. |
975 | + * @throws IllegalArgumentException if the args aren't given as String-Object pairings. |
976 | + * @throws IllegalStateException if there are no existing parameterizations to multiply by |
977 | + */ |
978 | + public void multiplyParametersByPrepending(Object... args) |
979 | + { |
980 | + cartesianProduct(false, args); |
981 | + } |
982 | + |
983 | + /** |
984 | + * <p>Performs a cartesian product of parameterizations by appending each of the incoming args to each |
985 | + * existing parameter's arg list. If you supply a prefix, it will also be appended to each parameter's name.</p> |
986 | + * |
987 | + * <p>This works just like {@link #multiplyParametersByPrepending(Object...)}, only each parameter label and |
988 | + * value is appended instead of prepended.</p> |
989 | + * @param args the list of named args to multiply by. Every other element (starting with the first) in the list must |
990 | + * be a String that represents the name suffix; the element following each suffix is the argument to append to |
991 | + * the args list. |
992 | + * @throws IllegalArgumentException if the args aren't given as String-Object pairings. |
993 | + * @throws IllegalStateException if there are no existing parameterizations to multiply by |
994 | + * @see #multiplyParametersByPrepending(Object...) |
995 | + */ |
996 | + public void multiplyParametersByAppending(Object... args) |
997 | + { |
998 | + cartesianProduct(true, args); |
999 | + } |
1000 | + |
1001 | + private void cartesianProduct(boolean append, Object[] args) |
1002 | + { |
1003 | + if (list.size() == 0) |
1004 | + { |
1005 | + throw new IllegalStateException("can't multiply yet -- no args defined"); |
1006 | + } |
1007 | + if ( (args.length % 2) != 0) |
1008 | + { |
1009 | + throw new IllegalArgumentException("odd number of arguments"); |
1010 | + } |
1011 | + |
1012 | + List<Parameterization> product = new LinkedList<Parameterization>(); |
1013 | + for (Parameterization param : list) |
1014 | + { |
1015 | + Object[] origArgs = param.getArguments(); |
1016 | + Object[] newArgs = new Object[origArgs.length + 1]; |
1017 | + System.arraycopy(origArgs, 0, newArgs, append ? 0 : 1, origArgs.length); |
1018 | + |
1019 | + for(int i=0; i < args.length; i+= 2) |
1020 | + { |
1021 | + String label; |
1022 | + try |
1023 | + { |
1024 | + label = (String)args[i]; |
1025 | + } |
1026 | + catch (ClassCastException e) |
1027 | + { |
1028 | + throw new IllegalArgumentException("argument at index " + i + " is not a String"); |
1029 | + } |
1030 | + |
1031 | + if (label == null) |
1032 | + { |
1033 | + label = param.getName(); |
1034 | + } |
1035 | + else |
1036 | + { |
1037 | + label = append ? |
1038 | + param.getName() + label |
1039 | + : label + param.getName(); |
1040 | + } |
1041 | + newArgs[append ? origArgs.length : 0] = args[i+1]; |
1042 | + product.add(new Parameterization(label, param.expectedToPass(), newArgs)); |
1043 | + } |
1044 | + } |
1045 | + |
1046 | + list.clear(); |
1047 | + list.addAll(product); |
1048 | + } |
1049 | + |
1050 | + /** |
1051 | + * Represents this builder's parameterizations. The returning list is backed by this builder, so changes |
1052 | + * to either are seen in both. |
1053 | + * @return the list that backs this builder |
1054 | + */ |
1055 | + public List<Parameterization> asList() |
1056 | + { |
1057 | + return list; |
1058 | + } |
1059 | +} |
1060 | |
1061 | === added directory 'src/test' |
1062 | === added directory 'src/test/java' |
1063 | === added directory 'src/test/java/com' |
1064 | === added directory 'src/test/java/com/motpot' |
1065 | === added directory 'src/test/java/com/motpot/npr' |
1066 | === added file 'src/test/java/com/motpot/npr/NamedParameterizedRunnerTest.java' |
1067 | --- src/test/java/com/motpot/npr/NamedParameterizedRunnerTest.java 1970-01-01 00:00:00 +0000 |
1068 | +++ src/test/java/com/motpot/npr/NamedParameterizedRunnerTest.java 2011-09-08 16:01:22 +0000 |
1069 | @@ -0,0 +1,316 @@ |
1070 | +package com.motpot.npr; |
1071 | + |
1072 | +import com.motpot.npr.NamedParameterizedRunner.ReifiedParamRunner; |
1073 | +import org.junit.After; |
1074 | +import org.junit.Before; |
1075 | +import org.junit.Test; |
1076 | +import org.junit.runner.Runner; |
1077 | +import org.junit.runners.model.FrameworkMethod; |
1078 | + |
1079 | +import java.util.*; |
1080 | + |
1081 | +import static junit.framework.Assert.*; |
1082 | + |
1083 | +public final class NamedParameterizedRunnerTest |
1084 | +{ |
1085 | + private final static List<Parameterization> builderList = new LinkedList<Parameterization>(); |
1086 | + private Properties oldProperties; |
1087 | + private Properties workingProperties; |
1088 | + |
1089 | + @Before |
1090 | + public void setUp() |
1091 | + { |
1092 | + builderList.clear(); |
1093 | + oldProperties = System.getProperties(); |
1094 | + workingProperties = new Properties(oldProperties); |
1095 | + System.setProperties(workingProperties); |
1096 | + } |
1097 | + |
1098 | + @After |
1099 | + public void tearDown() |
1100 | + { |
1101 | + System.setProperties(oldProperties); |
1102 | + } |
1103 | + |
1104 | + public final static class RunEverythingTest |
1105 | + { |
1106 | + @NamedParameterizedRunner.TestParameters |
1107 | + @SuppressWarnings("unused") |
1108 | + public static List<Parameterization> params() { return builderList; } |
1109 | + |
1110 | + public RunEverythingTest(char unused) |
1111 | + { |
1112 | + assert unused != 'a'; // just to shut it up about it not being used |
1113 | + } |
1114 | + |
1115 | + @Test |
1116 | + public void one() {} |
1117 | + |
1118 | + @Test |
1119 | + public void two() {} |
1120 | + } |
1121 | + |
1122 | + public final static class OneFailingTest |
1123 | + { |
1124 | + @NamedParameterizedRunner.TestParameters |
1125 | + @SuppressWarnings("unused") |
1126 | + public static List<Parameterization> params() { return builderList; } |
1127 | + |
1128 | + public OneFailingTest(char unused) |
1129 | + { |
1130 | + assert unused != 'a'; // just to shut it up about it not being used |
1131 | + } |
1132 | + |
1133 | + @Test |
1134 | + public void passing() {} |
1135 | + |
1136 | + @Failing @Test |
1137 | + public void failing() {} |
1138 | + } |
1139 | + |
1140 | + public final static class OnlyIfTest |
1141 | + { |
1142 | + @NamedParameterizedRunner.TestParameters |
1143 | + @SuppressWarnings("unused") |
1144 | + public static List<Parameterization> params() { return builderList; } |
1145 | + |
1146 | + private final int number; |
1147 | + @SuppressWarnings("unused") public final boolean numberIs1; // used by @OnlyIf |
1148 | + |
1149 | + public OnlyIfTest(int number) { |
1150 | + this.number = number; |
1151 | + this.numberIs1 = is1(); |
1152 | + } |
1153 | + |
1154 | + @Test |
1155 | + public void alwaysRun() {} |
1156 | + |
1157 | + @Test @OnlyIf("is1()") |
1158 | + public void testIs1Method() { |
1159 | + assertEquals("number", 1, number); |
1160 | + } |
1161 | + |
1162 | + @Test @OnlyIf("numberIs1") |
1163 | + public void testIs1Field() { |
1164 | + assertEquals("number", 1, number); |
1165 | + } |
1166 | + |
1167 | + public boolean is1() { |
1168 | + return number == 1; |
1169 | + } |
1170 | + } |
1171 | + |
1172 | + @Test |
1173 | + public void testRegexUsed() |
1174 | + { |
1175 | + assertFalse("//", NamedParameterizedRunner.paramNameUsesRegex("//")); |
1176 | + assertTrue("/a/", NamedParameterizedRunner.paramNameUsesRegex("/a/")); |
1177 | + } |
1178 | + |
1179 | + @Test |
1180 | + public void testRegexMatches() |
1181 | + { |
1182 | + assertFalse("/a/ against 'foo'", NamedParameterizedRunner.paramNameMatchesRegex("foo", "/a/")); |
1183 | + assertTrue("/a/ against 'foo bar'", NamedParameterizedRunner.paramNameMatchesRegex("foo bar", "/a/")); |
1184 | + assertFalse("/foo/ against 'o'", NamedParameterizedRunner.paramNameMatchesRegex("o", "/foo/")); |
1185 | + } |
1186 | + |
1187 | + @Test |
1188 | + public void testRunEverythingPasses() throws Throwable |
1189 | + { |
1190 | + ParameterizationBuilder builder = new ParameterizationBuilder(builderList); |
1191 | + builder.add("alpha", 'a'); |
1192 | + builder.add("beta", 'b'); |
1193 | + |
1194 | + NamedParameterizedRunner runner = new NamedParameterizedRunner(RunEverythingTest.class); |
1195 | + |
1196 | + Map<String,ReifiedParamRunner> map = testParameterizations(runner, |
1197 | + "Parameterization[PASSING alpha: [a] ]", |
1198 | + "Parameterization[PASSING beta: [b] ]"); |
1199 | + |
1200 | + testOverrides(map.values(), false); |
1201 | + |
1202 | + testFrameworkMethod(map.get("Parameterization[PASSING alpha: [a] ]"), RunEverythingTest.class, "one", true); |
1203 | + testFrameworkMethod(map.get("Parameterization[PASSING alpha: [a] ]"), RunEverythingTest.class, "two", true); |
1204 | + testFrameworkMethod(map.get("Parameterization[PASSING beta: [b] ]"), RunEverythingTest.class, "one", true); |
1205 | + testFrameworkMethod(map.get("Parameterization[PASSING beta: [b] ]"), RunEverythingTest.class, "two", true); |
1206 | + } |
1207 | + |
1208 | + @Test |
1209 | + public void testRunEverythingAlphaPasses() throws Throwable |
1210 | + { |
1211 | + ParameterizationBuilder builder = new ParameterizationBuilder(builderList); |
1212 | + builder.add("alpha", 'a'); |
1213 | + builder.addFailing("beta", 'b'); |
1214 | + |
1215 | + NamedParameterizedRunner runner = new NamedParameterizedRunner(RunEverythingTest.class); |
1216 | + |
1217 | + Map<String,ReifiedParamRunner> map = testParameterizations(runner, |
1218 | + "Parameterization[PASSING alpha: [a] ]", |
1219 | + "Parameterization[FAILING beta: [b] ]"); |
1220 | + |
1221 | + testOverrides(map.values(), false); |
1222 | + |
1223 | + testFrameworkMethod(map.get("Parameterization[PASSING alpha: [a] ]"), RunEverythingTest.class, "one", true); |
1224 | + testFrameworkMethod(map.get("Parameterization[PASSING alpha: [a] ]"), RunEverythingTest.class, "two", true); |
1225 | + testFrameworkMethod(map.get("Parameterization[FAILING beta: [b] ]"), RunEverythingTest.class, "one", false); |
1226 | + testFrameworkMethod(map.get("Parameterization[FAILING beta: [b] ]"), RunEverythingTest.class, "two", false); |
1227 | + } |
1228 | + |
1229 | + @Test |
1230 | + public void testOneMethodFailing() throws Throwable |
1231 | + { |
1232 | + ParameterizationBuilder builder = new ParameterizationBuilder(builderList); |
1233 | + builder.add("alpha", 'a'); |
1234 | + builder.add("beta", 'b'); |
1235 | + |
1236 | + NamedParameterizedRunner runner = new NamedParameterizedRunner(OneFailingTest.class); |
1237 | + |
1238 | + Map<String,ReifiedParamRunner> map = testParameterizations(runner, |
1239 | + "Parameterization[PASSING alpha: [a] ]", |
1240 | + "Parameterization[PASSING beta: [b] ]"); |
1241 | + |
1242 | + testOverrides(map.values(), false); |
1243 | + |
1244 | + testFrameworkMethod(map.get("Parameterization[PASSING alpha: [a] ]"), OneFailingTest.class, "passing", true); |
1245 | + testFrameworkMethod(map.get("Parameterization[PASSING alpha: [a] ]"), OneFailingTest.class, "failing", false); |
1246 | + testFrameworkMethod(map.get("Parameterization[PASSING beta: [b] ]"), OneFailingTest.class, "passing", true); |
1247 | + testFrameworkMethod(map.get("Parameterization[PASSING beta: [b] ]"), OneFailingTest.class, "failing", false); |
1248 | + } |
1249 | + |
1250 | + @Test |
1251 | + public void testOverrideOfFailingMethod() throws Throwable |
1252 | + { |
1253 | + workingProperties.put(NamedParameterizedRunner.PARAMETERIZATION_OVERRIDE, "beta"); |
1254 | + ParameterizationBuilder builder = new ParameterizationBuilder(builderList); |
1255 | + builder.add("alpha", 'a'); |
1256 | + builder.add("beta", 'b'); |
1257 | + |
1258 | + NamedParameterizedRunner runner = new NamedParameterizedRunner(OneFailingTest.class); |
1259 | + |
1260 | + Map<String,ReifiedParamRunner> map = testParameterizations(runner, |
1261 | + "Parameterization[PASSING beta: [b] ]"); |
1262 | + |
1263 | + testOverrides(map.values(), true); |
1264 | + |
1265 | + testFrameworkMethod(map.get("Parameterization[PASSING beta: [b] ]"), OneFailingTest.class, "passing", true); |
1266 | + testFrameworkMethod(map.get("Parameterization[PASSING beta: [b] ]"), OneFailingTest.class, "failing", true); |
1267 | + } |
1268 | + |
1269 | + @Test |
1270 | + public void testOverrideOfFailingParam() throws Throwable |
1271 | + { |
1272 | + workingProperties.put(NamedParameterizedRunner.PARAMETERIZATION_OVERRIDE, "beta"); |
1273 | + ParameterizationBuilder builder = new ParameterizationBuilder(builderList); |
1274 | + builder.add("alpha", 'a'); |
1275 | + builder.addFailing("beta", 'b'); |
1276 | + |
1277 | + NamedParameterizedRunner runner = new NamedParameterizedRunner(OneFailingTest.class); |
1278 | + |
1279 | + Map<String,ReifiedParamRunner> map = testParameterizations(runner, |
1280 | + "Parameterization[FAILING beta: [b] ]"); |
1281 | + |
1282 | + testOverrides(map.values(), true); |
1283 | + |
1284 | + testFrameworkMethod(map.get("Parameterization[FAILING beta: [b] ]"), OneFailingTest.class, "passing", true); |
1285 | + testFrameworkMethod(map.get("Parameterization[FAILING beta: [b] ]"), OneFailingTest.class, "failing", true); |
1286 | + } |
1287 | + |
1288 | + @Test |
1289 | + public void testOverrideUsingRegex() throws Throwable |
1290 | + { |
1291 | + workingProperties.put(NamedParameterizedRunner.PARAMETERIZATION_OVERRIDE, "/a/"); |
1292 | + ParameterizationBuilder builder = new ParameterizationBuilder(builderList); |
1293 | + builder.add("alpha", 'a'); |
1294 | + builder.addFailing("beta", 'b'); |
1295 | + |
1296 | + NamedParameterizedRunner runner = new NamedParameterizedRunner(OneFailingTest.class); |
1297 | + |
1298 | + Map<String,ReifiedParamRunner> map = testParameterizations(runner, |
1299 | + "Parameterization[PASSING alpha: [a] ]", |
1300 | + "Parameterization[FAILING beta: [b] ]"); |
1301 | + |
1302 | + testOverrides(map.values(), true); |
1303 | + |
1304 | + testFrameworkMethod(map.get("Parameterization[PASSING alpha: [a] ]"), OneFailingTest.class, "passing", true); |
1305 | + testFrameworkMethod(map.get("Parameterization[PASSING alpha: [a] ]"), OneFailingTest.class, "failing", true); |
1306 | + testFrameworkMethod(map.get("Parameterization[FAILING beta: [b] ]"), OneFailingTest.class, "passing", true); |
1307 | + testFrameworkMethod(map.get("Parameterization[FAILING beta: [b] ]"), OneFailingTest.class, "failing", true); |
1308 | + } |
1309 | + |
1310 | + @Test |
1311 | + public void testOnlyIf() throws Throwable { |
1312 | + ParameterizationBuilder builder = new ParameterizationBuilder(builderList); |
1313 | + builder.add("one", 1); |
1314 | + builder.add("two", 2); |
1315 | + |
1316 | + NamedParameterizedRunner runner = new NamedParameterizedRunner(OnlyIfTest.class); |
1317 | + |
1318 | + Map<String,ReifiedParamRunner> map = testParameterizations(runner, |
1319 | + "Parameterization[PASSING one: [1] ]", |
1320 | + "Parameterization[PASSING two: [2] ]"); |
1321 | + |
1322 | + { |
1323 | + ReifiedParamRunner forOne = map.get("Parameterization[PASSING one: [1] ]"); |
1324 | + assertEquals("param one: " + forOne.describeChildren(), 3, forOne.getChildrenCount()); |
1325 | + testFrameworkMethod(forOne, OnlyIfTest.class, "testIs1Method", true); |
1326 | + testFrameworkMethod(forOne, OnlyIfTest.class, "testIs1Field", true); |
1327 | + testFrameworkMethod(forOne, OnlyIfTest.class, "alwaysRun", true); |
1328 | + } |
1329 | + { |
1330 | + ReifiedParamRunner forTwo = map.get("Parameterization[PASSING two: [2] ]"); |
1331 | + assertEquals("param two: " + forTwo.describeChildren(), 1, forTwo.getChildrenCount()); |
1332 | + testFrameworkMethod(forTwo, OnlyIfTest.class, "alwaysRun", true); |
1333 | + } |
1334 | + } |
1335 | + |
1336 | + private static void testOverrides(Collection<ReifiedParamRunner> runners, boolean expectedOverride) |
1337 | + { |
1338 | + for (ReifiedParamRunner runner : runners) |
1339 | + { |
1340 | + assertEquals(runner.toString(), expectedOverride, runner.overrideOn()); |
1341 | + } |
1342 | + } |
1343 | + |
1344 | + private static void testFrameworkMethod(ReifiedParamRunner runner, Class<?> testClass, String testMethod, |
1345 | + boolean expectedToPass) |
1346 | + { |
1347 | + String frameworkMethodString = "public void " + testClass.getName() + "." + testMethod + "()"; |
1348 | + FrameworkMethod method = runner.getChild(frameworkMethodString); |
1349 | + assertNotNull(frameworkMethodString, method); |
1350 | + assertEquals(frameworkMethodString, expectedToPass, runner.expectedToPass(method)); |
1351 | + } |
1352 | + |
1353 | + /** |
1354 | + * Confirms that each given name has a {@linkplain ReifiedParamRunner} associated with it, and returns the |
1355 | + * name -> runner map |
1356 | + * @param runner the parameterized runner |
1357 | + * @param names the expected names |
1358 | + * @return a map of names to reified runners |
1359 | + */ |
1360 | + private static Map<String,ReifiedParamRunner> testParameterizations(NamedParameterizedRunner runner, String... names) |
1361 | + { |
1362 | + List<Runner> children = runner.getChildren(); |
1363 | + assertEquals("children.size()", names.length, children.size()); |
1364 | + |
1365 | + Set<String> expectedNames = new HashSet<String>(names.length, 1.0f); |
1366 | + for (String name : names) { |
1367 | + assertTrue("unexpected error, duplicate name: " + name, expectedNames.add(name)); |
1368 | + } |
1369 | + |
1370 | + Map<String,ReifiedParamRunner> foundRunners = new HashMap<String, ReifiedParamRunner>(); |
1371 | + for (Runner child : children) |
1372 | + { |
1373 | + ReifiedParamRunner reified = (ReifiedParamRunner)child; |
1374 | + String paramToString = reified.paramToString(); |
1375 | + assertNull("duplicate name: " + paramToString, foundRunners.put(paramToString, reified)); |
1376 | + } |
1377 | + |
1378 | + for (String expected : expectedNames) |
1379 | + { |
1380 | + assertTrue("didn't find expected param: " + expected, foundRunners.containsKey(expected)); |
1381 | + } |
1382 | + |
1383 | + return foundRunners; |
1384 | + } |
1385 | +} |
1386 | |
1387 | === added file 'src/test/java/com/motpot/npr/OnlyIfUsageTest.java' |
1388 | --- src/test/java/com/motpot/npr/OnlyIfUsageTest.java 1970-01-01 00:00:00 +0000 |
1389 | +++ src/test/java/com/motpot/npr/OnlyIfUsageTest.java 2011-09-08 16:01:22 +0000 |
1390 | @@ -0,0 +1,68 @@ |
1391 | +package com.motpot.npr; |
1392 | + |
1393 | +import org.junit.Test; |
1394 | +import org.junit.runner.RunWith; |
1395 | + |
1396 | +import java.util.List; |
1397 | + |
1398 | +import static org.junit.Assert.*; |
1399 | + |
1400 | +@RunWith(NamedParameterizedRunner.class) |
1401 | +public final class OnlyIfUsageTest { |
1402 | + @NamedParameterizedRunner.TestParameters |
1403 | + public static List<Parameterization> params() { |
1404 | + ParameterizationBuilder builder = new ParameterizationBuilder(); |
1405 | + builder.add("foo", "foo"); |
1406 | + builder.add("bar", "bar"); |
1407 | + return builder.asList(); |
1408 | + } |
1409 | + |
1410 | + private final String string; |
1411 | + public final boolean stringIsFoo; |
1412 | + |
1413 | + public OnlyIfUsageTest(String string) { |
1414 | + this.string = string; |
1415 | + stringIsFoo = "foo".equals(string); |
1416 | + } |
1417 | + |
1418 | + @Test @OnlyIf("isFoo()") |
1419 | + public void equalsFoo() { |
1420 | + assertEquals("string", "foo", string); |
1421 | + } |
1422 | + |
1423 | + @Test @OnlyIf("stringIsFoo") |
1424 | + public void equalsFooByField() { |
1425 | + assertEquals("string", "foo", string); |
1426 | + } |
1427 | + |
1428 | + @Test @OnlyIfNot("isFoo()") |
1429 | + public void notEqualsFoo() { |
1430 | + if ("foo".equals(string)) { |
1431 | + fail("found a foo!"); |
1432 | + } |
1433 | + } |
1434 | + |
1435 | + @Test @OnlyIf("hasThreeChars()") @OnlyIfNot("lastCharR()") |
1436 | + public void threeCharNoTrailingR() { |
1437 | + assertEquals("string length", 3, string.length()); |
1438 | + assertFalse("last char was r! <" + string + '>', string.charAt(2) == 'r'); |
1439 | + } |
1440 | + |
1441 | + @Test |
1442 | + public void stringNotNull() { |
1443 | + assertNotNull("string", string); |
1444 | + } |
1445 | + |
1446 | + public boolean isFoo() { |
1447 | + return "foo".equals(string); |
1448 | + } |
1449 | + |
1450 | + public boolean hasThreeChars() { |
1451 | + return string.length() == 3; |
1452 | + } |
1453 | + |
1454 | + public boolean lastCharR() { |
1455 | + // for simplicity, we'll assume string not null, string.length > 0 |
1456 | + return string.charAt( string.length() - 1) == 'r'; |
1457 | + } |
1458 | +} |
1459 | |
1460 | === added file 'src/test/java/com/motpot/npr/ParameterizationBuilderTest.java' |
1461 | --- src/test/java/com/motpot/npr/ParameterizationBuilderTest.java 1970-01-01 00:00:00 +0000 |
1462 | +++ src/test/java/com/motpot/npr/ParameterizationBuilderTest.java 2011-09-08 16:01:22 +0000 |
1463 | @@ -0,0 +1,153 @@ |
1464 | +package com.motpot.npr; |
1465 | + |
1466 | +import org.junit.Test; |
1467 | + |
1468 | +import java.util.Arrays; |
1469 | +import java.util.List; |
1470 | + |
1471 | +import static junit.framework.Assert.*; |
1472 | + |
1473 | + |
1474 | +public final class ParameterizationBuilderTest |
1475 | +{ |
1476 | + @Test |
1477 | + public void testAdd() |
1478 | + { |
1479 | + ParameterizationBuilder builder = new ParameterizationBuilder(); |
1480 | + builder.add("hello", 1, "someString", 50L); |
1481 | + |
1482 | + List<Parameterization> params = builder.asList(); |
1483 | + assertEquals("params size", 1, params.size()); |
1484 | + assertEquivalent("only one", |
1485 | + Parameterization.create("hello", 1, "someString", 50L), |
1486 | + params.get(0)); |
1487 | + } |
1488 | + |
1489 | + @Test |
1490 | + public void testAddFailing() |
1491 | + { |
1492 | + ParameterizationBuilder builder = new ParameterizationBuilder(); |
1493 | + builder.addFailing("hello", 1, "someString", 50L); |
1494 | + |
1495 | + List<Parameterization> params = builder.asList(); |
1496 | + assertEquals("params size", 1, params.size()); |
1497 | + assertEquivalent("only one", |
1498 | + Parameterization.failing("hello", 1, "someString", 50L), |
1499 | + params.get(0)); |
1500 | + } |
1501 | + |
1502 | + @Test |
1503 | + public void testCreate() |
1504 | + { |
1505 | + ParameterizationBuilder builder = new ParameterizationBuilder(); |
1506 | + builder.create("hello", true, 1, "someString", 50L); |
1507 | + builder.create("hi", false, -1, "anotherString", -50L); |
1508 | + |
1509 | + List<Parameterization> params = builder.asList(); |
1510 | + assertEquals("params size", 2, params.size()); |
1511 | + assertEquivalent("passing", |
1512 | + new Parameterization("hello", true, 1, "someString", 50L), |
1513 | + params.get(0)); |
1514 | + assertEquivalent("failing", |
1515 | + new Parameterization("hi", false, -1, "anotherString", -50L), |
1516 | + params.get(1)); |
1517 | + } |
1518 | + |
1519 | + @Test |
1520 | + public void testAppend() |
1521 | + { |
1522 | + ParameterizationBuilder builder = new ParameterizationBuilder(); |
1523 | + builder.add("ONE", 1, 2, 3); |
1524 | + builder.addFailing("TWO", 4, 5, 6); |
1525 | + builder.multiplyParametersByAppending("-a", 'a', "-b", 'b', "-c", 'c'); |
1526 | + |
1527 | + List<Parameterization> expected = Arrays.asList( |
1528 | + Parameterization.create("ONE-a", 1, 2, 3, 'a'), |
1529 | + Parameterization.create("ONE-b", 1, 2, 3, 'b'), |
1530 | + Parameterization.create("ONE-c", 1, 2, 3, 'c'), |
1531 | + Parameterization.failing("TWO-a", 4, 5, 6, 'a'), |
1532 | + Parameterization.failing("TWO-b", 4, 5, 6, 'b'), |
1533 | + Parameterization.failing("TWO-c", 4, 5, 6, 'c') |
1534 | + ); |
1535 | + List<Parameterization> actual = builder.asList(); |
1536 | + |
1537 | + assertEquals("params size", expected.size(), actual.size()); |
1538 | + |
1539 | + for(int i=0, len=expected.size(); i<len; ++i) |
1540 | + { |
1541 | + assertEquivalent("param " + i, expected.get(i), actual.get(i)); |
1542 | + } |
1543 | + } |
1544 | + |
1545 | + @Test |
1546 | + public void testPrepend() |
1547 | + { |
1548 | + ParameterizationBuilder builder = new ParameterizationBuilder(); |
1549 | + builder.add("ONE", 1, 2, 3); |
1550 | + builder.addFailing("TWO", 4, 5, 6); |
1551 | + builder.multiplyParametersByPrepending("a:", 'a', "b:", 'b', "c:", 'c'); |
1552 | + |
1553 | + List<Parameterization> expected = Arrays.asList( |
1554 | + Parameterization.create("a:ONE", 'a', 1, 2, 3), |
1555 | + Parameterization.create("b:ONE", 'b', 1, 2, 3), |
1556 | + Parameterization.create("c:ONE", 'c', 1, 2, 3), |
1557 | + Parameterization.failing("a:TWO", 'a', 4, 5, 6), |
1558 | + Parameterization.failing("b:TWO", 'b', 4, 5, 6), |
1559 | + Parameterization.failing("c:TWO", 'c', 4, 5, 6) |
1560 | + ); |
1561 | + List<Parameterization> actual = builder.asList(); |
1562 | + |
1563 | + assertEquals("params size", expected.size(), actual.size()); |
1564 | + |
1565 | + for(int i=0, len=expected.size(); i<len; ++i) |
1566 | + { |
1567 | + assertEquivalent("param " + i, expected.get(i), actual.get(i)); |
1568 | + } |
1569 | + } |
1570 | + |
1571 | + @Test |
1572 | + public void testListBackedByBuilder() |
1573 | + { |
1574 | + ParameterizationBuilder builder = new ParameterizationBuilder(); |
1575 | + List<Parameterization> list = builder.asList(); |
1576 | + |
1577 | + assertEquals("list size before building", 0, list.size()); |
1578 | + |
1579 | + builder.add("whatever", 'a'); |
1580 | + assertEquals("list size after building", 1, list.size()); |
1581 | + |
1582 | + assertSame("new list", builder.asList(), list); |
1583 | + } |
1584 | + |
1585 | + @Test(expected=IllegalArgumentException.class) |
1586 | + public void builderMultiplierArgsNotPaired() |
1587 | + { |
1588 | + ParameterizationBuilder builder = new ParameterizationBuilder(); |
1589 | + builder.add("whatever", 1); |
1590 | + |
1591 | + builder.multiplyParametersByAppending("label", 2, 3); |
1592 | + } |
1593 | + |
1594 | + @Test(expected=IllegalArgumentException.class) |
1595 | + public void builderMultiplierArgsLabelNotString() |
1596 | + { |
1597 | + ParameterizationBuilder builder = new ParameterizationBuilder(); |
1598 | + builder.add("whatever", 1); |
1599 | + |
1600 | + builder.multiplyParametersByAppending("label", 2, 3, "this label is in the wrong place"); |
1601 | + } |
1602 | + |
1603 | + @Test(expected=IllegalStateException.class) |
1604 | + public void builderMultipliesByZero() |
1605 | + { |
1606 | + new ParameterizationBuilder().multiplyParametersByAppending(); |
1607 | + } |
1608 | + |
1609 | + private static void assertEquivalent(String message, Parameterization expected, Parameterization actual) |
1610 | + { |
1611 | + if (!expected.equivalent(actual)) |
1612 | + { |
1613 | + fail(message + " expected<" + expected + "> but was <" + actual + ">"); |
1614 | + } |
1615 | + } |
1616 | +} |