Merge lp:~yshavit/namedparameterizedrunner/init_dump into lp:namedparameterizedrunner

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
Reviewer Review Type Date Requested Status
Yuval Shavit Pending
Review via email: mp+74631@code.launchpad.net

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+}

Subscribers

People subscribed via source and target branches

to all changes: