Permalink
Browse files

alpha-ready implementation of SuiteBuilder

  • Loading branch information...
dsaff committed May 12, 2010
1 parent 3b7ab4a commit f09cff79b941a525271f3f2838a9742b4c5c8d36
View
@@ -5,7 +5,7 @@
<property name="src" value="src/main/java" />
<property name="target" location="target" />
<property name="bin" location="${target}/main" />
<property name="version-base" value="4.8.2" />
<property name="version-base" value="4.9-SNAPSHOT-20100512-0041" />
<property name="version-status" value="" />
<property name="version" value="${version-base}${version-status}" />
<property name="dist" value="junit${version}" />
@@ -0,0 +1,4 @@
Can't open /Users/saff/Documents/git-repos/junit/doc/ReleaseNotes4.9-SNAPSHOT-20100512-0041.txt: No such file or directory at build/Markdown.pl line 218.
Use of uninitialized value in substitution (s///) at build/Markdown.pl line 245.
Use of uninitialized value in substitution (s///) at build/Markdown.pl line 246.
View
@@ -0,0 +1,28 @@
<h2>Summary of Changes in version 4.9</h2>
<h3>SuiteBuilder</h3>
<p>A new way of declaring suites to run. SuiteBuilder allows for flexible
specification of where to find the classes containing tests, and how
to filter the resulting runners. This suite class lists the
explicit test classes to consider running, and then filters it down
to only those tests or classes annotated with <code>@Category(Yes.class)</code>:</p>
<pre><code>@RunWith(SuiteBuilder.class)
public static class OnlyYes {

This comment has been minimized.

Show comment
Hide comment
@marcphilipp

marcphilipp May 12, 2010

Member

Should not be 'static', should it?

@marcphilipp

marcphilipp May 12, 2010

Member

Should not be 'static', should it?

This comment has been minimized.

Show comment
Hide comment
@dsaff

dsaff May 13, 2010

Member

Nope, you're right. Good catch

@dsaff

dsaff May 13, 2010

Member

Nope, you're right. Good catch

@Classes
public Listed classes= new Listed(Yes1.class, Yes2.class, No1.class);
@RunnerFilter
public CategoryFilter filter= CategoryFilter.include(Yes.class);
}
</code></pre>
<p>We hope to soon include other implementations for the @Classes annotation,
including a classpath-searching test gatherer.</p>
<h3>Bug fixes</h3>
<ul>
<li>github#98: assumeTrue() does not work with expected exceptions</li>
</ul>
View
@@ -1,5 +1,25 @@
## Summary of Changes in version 4.9 ##
### SuiteBuilder ###
A new way of declaring suites to run. SuiteBuilder allows for flexible
specification of where to find the classes containing tests, and how
to filter the resulting runners. This suite class lists the
explicit test classes to consider running, and then filters it down
to only those tests or classes annotated with `@Category(Yes.class)`:
@RunWith(SuiteBuilder.class)
public static class OnlyYes {
@Classes
public Listed classes= new Listed(Yes1.class, Yes2.class, No1.class);
@RunnerFilter
public CategoryFilter filter= CategoryFilter.include(Yes.class);
}
We hope to soon include other implementations for the @Classes annotation,
including a classpath-searching test gatherer.
### Bug fixes ###
- github#98: assumeTrue() does not work with expected exceptions
@@ -9,7 +9,7 @@ private Version() {
}
public static String id() {
return "4.8.2";
return "4.9-SNAPSHOT-20100512-0041";
}
public static void main(String[] args) {
@@ -8,10 +8,10 @@
import java.util.Arrays;
import java.util.List;
import org.junit.experimental.runners.SuiteBuilder;
import org.junit.runner.Runner;
import org.junit.tests.experimental.categories.SuiteBuilderTest.RunnerFilter;
public class CategoryFilter implements RunnerFilter.Value {
public class CategoryFilter implements SuiteBuilder.RunnerFilter.Value {
private final Class<?> fIncluded;
public CategoryFilter(Class<?> included) {
@@ -0,0 +1,19 @@
/**
*
*/
package org.junit.experimental.runners;
import java.util.Arrays;
import java.util.List;
public class Listed implements SuiteBuilder.Classes.Value {
private final Class<?>[] fClasses;
public Listed(Class<?>... classes) {
fClasses= classes;
}
public List<? extends Class<?>> get() {
return Arrays.asList(fClasses);
}
}
@@ -0,0 +1,99 @@
/**
*
*/
package org.junit.experimental.runners;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.TestClass;
public class SuiteBuilder extends Runner {
@Retention(RetentionPolicy.RUNTIME)
public @interface RunnerFilter {
public static interface Value {
public abstract List<Runner> matchingRunners(
List<Runner> allPossibleRunners);
}
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Classes {
public static interface Value {
public abstract Collection<? extends Class<?>> get();
}
}
private final TestClass fTestClass;
private final Object fInstance;
private final List<Runner> fRunners;
public SuiteBuilder(Class<?> testClass) throws InitializationError {
fTestClass= new TestClass(testClass);
fInstance = createInstance();
fRunners= computeRunners();
}
private Object createInstance() throws InitializationError {
try {
return fTestClass.getOnlyConstructor().newInstance();
} catch (Exception e) {
throw new InitializationError(e);
}
}
@Override
public Description getDescription() {
Description description= Description.createSuiteDescription(fTestClass.getJavaClass());
for (Runner each : fRunners) {
description.addChild(each.getDescription());
}
return description;
}
@Override
public void run(RunNotifier notifier) {
for (Runner each : fRunners)
each.run(notifier);
}
private List<Runner> computeRunners() throws InitializationError {
List<Class<?>> allPossibleClasses= gatherClasses();
List<Runner> allPossibleRunners= runnersForClasses(allPossibleClasses);
return filterRunners(allPossibleRunners);
}
private List<Runner> filterRunners(List<Runner> allPossibleRunners) {
List<Runner> result= allPossibleRunners;
for (SuiteBuilder.RunnerFilter.Value each : getFilters())
result= each.matchingRunners(result);
return result;
}
private List<SuiteBuilder.RunnerFilter.Value> getFilters() {
return fTestClass.getAnnotatedFieldValues(fInstance,
SuiteBuilder.RunnerFilter.class, SuiteBuilder.RunnerFilter.Value.class);
}
private List<Runner> runnersForClasses(List<Class<?>> allPossibleClasses) throws InitializationError {
return new AllDefaultPossibilitiesBuilder(true).runners(fTestClass
.getJavaClass(), allPossibleClasses);
}
public List<Class<?>> gatherClasses() {
ArrayList<Class<?>> result= new ArrayList<Class<?>>();
List<SuiteBuilder.Classes.Value> classeses= fTestClass.getAnnotatedFieldValues(
fInstance, SuiteBuilder.Classes.class, SuiteBuilder.Classes.Value.class);
for (SuiteBuilder.Classes.Value each : classeses)
result.addAll(each.get());
return result;
}
}
@@ -87,6 +87,11 @@ void removeParent(Class<?> klass) {
}
}
public List<Runner> runners(Class<?> parent, List<Class<?>> children)
throws InitializationError {
return runners(parent, children.toArray(new Class<?>[0]));
}
private List<Runner> runners(Class<?>[] children) {
ArrayList<Runner> runners= new ArrayList<Runner>();
for (Class<?> each : children) {
@@ -5,138 +5,32 @@
import static org.junit.experimental.results.PrintableResult.testResult;
import static org.junit.experimental.results.ResultMatchers.isSuccessful;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.experimental.categories.CategoryFilter;
import org.junit.experimental.runners.Listed;
import org.junit.experimental.runners.SuiteBuilder;
import org.junit.experimental.runners.SuiteBuilder.Classes;
import org.junit.experimental.runners.SuiteBuilder.RunnerFilter;
import org.junit.runner.Description;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.RunWith;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.TestClass;
public class SuiteBuilderTest {
@Retention(RetentionPolicy.RUNTIME)
public @interface RunnerFilter {
public static interface Value {
public abstract List<Runner> matchingRunners(
List<Runner> allPossibleRunners);
}
}
public static class Listed implements Classes.Value {
private final Class<?>[] fClasses;
public Listed(Class<?>... classes) {
fClasses= classes;
}
public List<? extends Class<?>> get() {
return Arrays.asList(fClasses);
}
}
// Classes -> RunnerBuilder
@Retention(RetentionPolicy.RUNTIME)
public @interface Classes {
public static interface Value {
public abstract Collection<? extends Class<?>> get();
}
}
public static class SuiteBuilder extends Runner {
private final TestClass fTestClass;
private final Object fInstance;
private final List<Runner> fRunners;
public SuiteBuilder(Class<?> testClass) throws InitializationError {
fTestClass= new TestClass(testClass);
// TODO: extract complexity
try {
fInstance= fTestClass.getOnlyConstructor().newInstance();
} catch (Exception e) {
throw new InitializationError(e);
}
fRunners= computeRunners();
}
@Override
public Description getDescription() {
Description description= Description.createSuiteDescription(fTestClass.getJavaClass());
for (Runner each : fRunners) {
description.addChild(each.getDescription());
}
return description;
}
// TODO: require an instance?
@Override
public void run(RunNotifier notifier) {
for (Runner each : fRunners)
each.run(notifier);
}
private List<Runner> computeRunners() {
List<Class<?>> allPossibleClasses= gatherClasses();
List<Runner> allPossibleRunners= runnersForClasses(allPossibleClasses);
return filterRunners(allPossibleRunners);
}
private List<Runner> filterRunners(List<Runner> allPossibleRunners) {
List<Runner> result= allPossibleRunners;
for (RunnerFilter.Value each : getFilters())
result= each.matchingRunners(result);
return result;
}
private List<RunnerFilter.Value> getFilters() {
return fTestClass.getAnnotatedFieldValues(fInstance,
RunnerFilter.class, RunnerFilter.Value.class);
}
private List<Runner> runnersForClasses(List<Class<?>> allPossibleClasses) {
// TODO: cheating
ArrayList<Runner> result= new ArrayList<Runner>();
for (Class<?> each : allPossibleClasses) {
try {
result.add(new BlockJUnit4ClassRunner(each));
} catch (InitializationError e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return result;
}
private List<Class<?>> gatherClasses() {
ArrayList<Class<?>> result= new ArrayList<Class<?>>();
List<Classes.Value> classeses= fTestClass.getAnnotatedFieldValues(
fInstance, Classes.class, Classes.Value.class);
for (Classes.Value each : classeses)
result.addAll(each.get());
return result;
}
}
static class Yes {
}
static class No {
}
// TODO: test multiple filters, multiple class sources
@Category(Yes.class)
public static class Yes1 {
@Test
@@ -168,7 +62,7 @@ public void no1() {
}
@RunWith(SuiteBuilder.class)
public static class OnlyYesMaybeTwo {
public static class OnlyYes {
@Classes
public Listed classes= new Listed(Yes1.class, Yes2.class, No1.class);
@@ -212,9 +106,9 @@ public void onlyRunOne() {
@Test
public void runTwo() {
Result result= new JUnitCore().run(OnlyYesMaybeTwo.class);
Result result= new JUnitCore().run(OnlyYes.class);
assertEquals(2, result.getRunCount());
assertThat(testResult(OnlyYesMaybeTwo.class), isSuccessful());
assertThat(testResult(OnlyYes.class), isSuccessful());
}
@Test

0 comments on commit f09cff7

Please sign in to comment.