Skip to content

Commit

Permalink
Class name filters work in a simple way
Browse files Browse the repository at this point in the history
  • Loading branch information
jlink committed Nov 13, 2015
1 parent c924f9c commit 891efa3
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 12 deletions.
@@ -0,0 +1,6 @@
package org.junit.gen5.engine;

public interface ClassFilter extends EngineFilter {

boolean acceptClass(Class<?> clazz);
}
@@ -0,0 +1,19 @@
package org.junit.gen5.engine;

import lombok.Value;

@Value
public class ClassNameFilter implements ClassFilter {

final private String regex;

@Override
public boolean acceptClass(Class<?> clazz) {
return clazz.getSimpleName().matches(regex);
}

@Override
public String getDescription() {
return "Filter class names with regular expression: " + regex;
}
}
@@ -0,0 +1,6 @@
package org.junit.gen5.engine;

public interface EngineFilter {

String getDescription();
}
Expand Up @@ -14,7 +14,9 @@
import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableList;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;


import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
Expand Down Expand Up @@ -43,7 +45,7 @@ public static TestPlanSpecificationElement forUniqueId(String uniqueId) {
return new UniqueIdSpecification(uniqueId); return new UniqueIdSpecification(uniqueId);
} }


public static Predicate<TestDescriptor> filterByTags(String... tagNames) { public static Predicate<TestDescriptor> byTags(String... tagNames) {
List<String> includeTags = Arrays.asList(tagNames); List<String> includeTags = Arrays.asList(tagNames);
// @formatter:off // @formatter:off
return (TestDescriptor descriptor) -> descriptor.getTags().stream() return (TestDescriptor descriptor) -> descriptor.getTags().stream()
Expand All @@ -52,10 +54,14 @@ public static Predicate<TestDescriptor> filterByTags(String... tagNames) {
// @formatter:on // @formatter:on
} }


public static Predicate<TestDescriptor> filterByEngine(String engineId) { public static Predicate<TestDescriptor> byEngine(String engineId) {
return (TestDescriptor descriptor) -> descriptor.getUniqueId().startsWith(engineId); return (TestDescriptor descriptor) -> descriptor.getUniqueId().startsWith(engineId);
} }


public static EngineFilter classNameMatches(String regex) {
return new ClassNameFilter(regex);
}

public static TestPlanSpecification build(TestPlanSpecificationElement... elements) { public static TestPlanSpecification build(TestPlanSpecificationElement... elements) {
return build(Arrays.asList(elements)); return build(Arrays.asList(elements));
} }
Expand All @@ -66,9 +72,13 @@ public static TestPlanSpecification build(List<TestPlanSpecificationElement> ele


private final List<TestPlanSpecificationElement> elements; private final List<TestPlanSpecificationElement> elements;


// Descriptor Filters are evaluated by the launcher itself after engines have done their discovery.
// Begin predicate chain with a predicate that always evaluates to true. // Begin predicate chain with a predicate that always evaluates to true.
private Predicate<TestDescriptor> descriptorFilter = (TestDescriptor descriptor) -> true; private Predicate<TestDescriptor> descriptorFilter = (TestDescriptor descriptor) -> true;


// Engine filters are handed through to all test engines to be applied during discovery
private List<EngineFilter> engineFilters = new ArrayList<>();

public TestPlanSpecification(List<TestPlanSpecificationElement> elements) { public TestPlanSpecification(List<TestPlanSpecificationElement> elements) {
this.elements = elements; this.elements = elements;
} }
Expand All @@ -87,6 +97,15 @@ public void filterWith(Predicate<TestDescriptor> filter) {
this.descriptorFilter = this.descriptorFilter.and(filter); this.descriptorFilter = this.descriptorFilter.and(filter);
} }


public void filterWith(EngineFilter filter) {
Preconditions.notNull(filter, "filter must not be null");
this.engineFilters.add(filter);
}

public List<EngineFilter> getEngineFilters() {
return Collections.unmodifiableList(engineFilters);
}

public boolean acceptDescriptor(TestDescriptor testDescriptor) { public boolean acceptDescriptor(TestDescriptor testDescriptor) {
Preconditions.notNull(testDescriptor, "testDescriptor must not be null"); Preconditions.notNull(testDescriptor, "testDescriptor must not be null");
return this.descriptorFilter.test(testDescriptor); return this.descriptorFilter.test(testDescriptor);
Expand Down
Expand Up @@ -56,10 +56,12 @@ public void execute(TestPlan testPlan) {


testPlanExecutionListener.testPlanExecutionStarted(testPlan); testPlanExecutionListener.testPlanExecutionStarted(testPlan);
for (TestEngine testEngine : lookupAllTestEngines()) { for (TestEngine testEngine : lookupAllTestEngines()) {
testPlanExecutionListener.testPlanExecutionStartedOnEngine(testPlan, testEngine); Optional<EngineDescriptor> engineDescriptorOptional = testPlan.getEngineDescriptorFor(testEngine);
Optional<EngineDescriptor> engineDescriptor = testPlan.getEngineDescriptorFor(testEngine); engineDescriptorOptional.ifPresent(engineDescriptor -> {
testEngine.execute(new EngineExecutionContext(engineDescriptor.get(), testExecutionListener)); testPlanExecutionListener.testPlanExecutionStartedOnEngine(testPlan, testEngine);
testPlanExecutionListener.testPlanExecutionFinishedOnEngine(testPlan, testEngine); testEngine.execute(new EngineExecutionContext(engineDescriptor, testExecutionListener));
testPlanExecutionListener.testPlanExecutionFinishedOnEngine(testPlan, testEngine);
});
} }
testPlanExecutionListener.testPlanExecutionFinished(testPlan); testPlanExecutionListener.testPlanExecutionFinished(testPlan);
} }
Expand Down
Expand Up @@ -28,7 +28,7 @@ static Iterable<TestEngine> lookupAllTestEngines() {


// TODO LOG // TODO LOG
for (TestEngine testEngine : testEngines) { for (TestEngine testEngine : testEngines) {
System.out.println(testEngine.getId()); System.out.println(String.format("Discovered test engine with id: '%s'", testEngine.getId()));
} }


} }
Expand Down
Expand Up @@ -99,7 +99,7 @@ public Set<TestDescriptor> getChildren() {
@Override @Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this, () -> { visitor.visit(this, () -> {
throw new UnsupportedOperationException("It's not possible to remove the whole test plan"); //the test plan itself will never be removed
}); });
new HashSet<>(engineDescriptors).forEach(child -> child.accept(visitor)); new HashSet<>(engineDescriptors).forEach(child -> child.accept(visitor));
} }
Expand Down
Expand Up @@ -14,13 +14,14 @@
import static java.util.stream.Collectors.*; import static java.util.stream.Collectors.*;


import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;


import org.junit.gen5.engine.ClassFilter;
import org.junit.gen5.engine.EngineDescriptor; import org.junit.gen5.engine.EngineDescriptor;
import org.junit.gen5.engine.EngineExecutionContext; import org.junit.gen5.engine.EngineExecutionContext;
import org.junit.gen5.engine.EngineFilter;
import org.junit.gen5.engine.TestDescriptor; import org.junit.gen5.engine.TestDescriptor;
import org.junit.gen5.engine.TestEngine; import org.junit.gen5.engine.TestEngine;
import org.junit.gen5.engine.TestPlanSpecification; import org.junit.gen5.engine.TestPlanSpecification;
Expand All @@ -41,6 +42,27 @@ public String getId() {
public void discoverTests(TestPlanSpecification specification, EngineDescriptor engineDescriptor) { public void discoverTests(TestPlanSpecification specification, EngineDescriptor engineDescriptor) {
JUnit4SpecificationResolver resolver = new JUnit4SpecificationResolver(engineDescriptor); JUnit4SpecificationResolver resolver = new JUnit4SpecificationResolver(engineDescriptor);
specification.accept(resolver); specification.accept(resolver);
applyEngineFilters(specification.getEngineFilters(), engineDescriptor);
}

// More or less copied over from JUnit5TestEngine
private void applyEngineFilters(List<EngineFilter> engineFilters, EngineDescriptor engineDescriptor) {
//Todo: Currently only works with a single ClassFilter
if (engineFilters.isEmpty())
return;
ClassFilter filter = (ClassFilter) engineFilters.get(0);

TestDescriptor.Visitor filteringVisitor = (descriptor, remove) -> {
if (descriptor instanceof DescriptionTestDescriptor) {
DescriptionTestDescriptor descriptionTestDescriptor = (DescriptionTestDescriptor) descriptor;
descriptionTestDescriptor.getTestClass().ifPresent(clazz -> {
if (!filter.acceptClass(clazz))
remove.run();
});
}
};

engineDescriptor.accept(filteringVisitor);
} }


@Override @Override
Expand Down
Expand Up @@ -25,6 +25,7 @@
import java.util.function.Predicate; import java.util.function.Predicate;


import org.junit.gen5.commons.util.StringUtils; import org.junit.gen5.commons.util.StringUtils;
import org.junit.gen5.engine.EngineFilter;
import org.junit.gen5.engine.TestDescriptor; import org.junit.gen5.engine.TestDescriptor;
import org.junit.gen5.engine.TestPlanSpecification; import org.junit.gen5.engine.TestPlanSpecification;
import org.junit.gen5.engine.TestPlanSpecificationElement; import org.junit.gen5.engine.TestPlanSpecificationElement;
Expand Down Expand Up @@ -146,6 +147,29 @@ private static String getAnnotatedOnlyEngine(Class<?> testClass) {
return annotation.value(); return annotation.value();
} }


/**
* The <code>OnlyEngine</code> annotation specifies the engine ID to be filtered when a class
* annotated with <code>@RunWith(JUnit5.class)</code> is run.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface ClassNameMatches {

/**
* @return regex
*/
String value();
}

private static String getAnnotatedClassNameMatches(Class<?> testClass) {
ClassNameMatches annotation = testClass.getAnnotation(ClassNameMatches.class);
if (annotation == null) {
return "";
}
return annotation.value();
}

private final Class<?> testClass; private final Class<?> testClass;
private final TestPlanSpecification specification; private final TestPlanSpecification specification;
private final JUnit5TestTree testTree; private final JUnit5TestTree testTree;
Expand Down Expand Up @@ -192,22 +216,31 @@ private TestPlanSpecification createSpecificationFromAnnotations() {
TestPlanSpecification plan = TestPlanSpecification.build(specs); TestPlanSpecification plan = TestPlanSpecification.build(specs);
addOnlyIncludeTagsFilter(plan); addOnlyIncludeTagsFilter(plan);
addOnlyIncludeEngineFilter(plan); addOnlyIncludeEngineFilter(plan);
addClassNameMatches(plan);


return plan; return plan;
} }


private void addClassNameMatches(TestPlanSpecification plan) {
String regex = getAnnotatedClassNameMatches(testClass).trim();
if (!regex.isEmpty()) {
EngineFilter classNameFilter = TestPlanSpecification.classNameMatches(regex);
plan.filterWith(classNameFilter);
}
}

private void addOnlyIncludeTagsFilter(TestPlanSpecification plan) { private void addOnlyIncludeTagsFilter(TestPlanSpecification plan) {
String[] onlyIncludeTags = getAnnotatedOnlyIncludeTags(testClass); String[] onlyIncludeTags = getAnnotatedOnlyIncludeTags(testClass);
if (onlyIncludeTags.length > 0) { if (onlyIncludeTags.length > 0) {
Predicate<TestDescriptor> tagNamesFilter = TestPlanSpecification.filterByTags(onlyIncludeTags); Predicate<TestDescriptor> tagNamesFilter = TestPlanSpecification.byTags(onlyIncludeTags);
plan.filterWith(tagNamesFilter); plan.filterWith(tagNamesFilter);
} }
} }


private void addOnlyIncludeEngineFilter(TestPlanSpecification plan) { private void addOnlyIncludeEngineFilter(TestPlanSpecification plan) {
String onlyIncludeEngine = getAnnotatedOnlyEngine(testClass); String onlyIncludeEngine = getAnnotatedOnlyEngine(testClass);
if (StringUtils.isNotBlank(onlyIncludeEngine)) { if (StringUtils.isNotBlank(onlyIncludeEngine)) {
Predicate<TestDescriptor> engineFilter = TestPlanSpecification.filterByEngine(onlyIncludeEngine); Predicate<TestDescriptor> engineFilter = TestPlanSpecification.byEngine(onlyIncludeEngine);
plan.filterWith(engineFilter); plan.filterWith(engineFilter);
} }
} }
Expand Down
Expand Up @@ -22,12 +22,15 @@
import java.util.Set; import java.util.Set;


import org.junit.gen5.commons.util.Preconditions; import org.junit.gen5.commons.util.Preconditions;
import org.junit.gen5.engine.ClassFilter;
import org.junit.gen5.engine.EngineDescriptor; import org.junit.gen5.engine.EngineDescriptor;
import org.junit.gen5.engine.EngineExecutionContext; import org.junit.gen5.engine.EngineExecutionContext;
import org.junit.gen5.engine.EngineFilter;
import org.junit.gen5.engine.TestDescriptor; import org.junit.gen5.engine.TestDescriptor;
import org.junit.gen5.engine.TestEngine; import org.junit.gen5.engine.TestEngine;
import org.junit.gen5.engine.TestPlanSpecification; import org.junit.gen5.engine.TestPlanSpecification;
import org.junit.gen5.engine.TestPlanSpecificationElement; import org.junit.gen5.engine.TestPlanSpecificationElement;
import org.junit.gen5.engine.junit5.descriptor.ClassTestDescriptor;
import org.junit.gen5.engine.junit5.descriptor.SpecificationResolver; import org.junit.gen5.engine.junit5.descriptor.SpecificationResolver;
import org.junit.gen5.engine.junit5.execution.TestExecutionNode; import org.junit.gen5.engine.junit5.execution.TestExecutionNode;
import org.junit.gen5.engine.junit5.execution.TestExecutionNodeResolver; import org.junit.gen5.engine.junit5.execution.TestExecutionNodeResolver;
Expand All @@ -53,6 +56,26 @@ private void resolveSpecification(TestPlanSpecification specification, EngineDes
for (TestPlanSpecificationElement element : specification) { for (TestPlanSpecificationElement element : specification) {
resolver.resolveElement(element); resolver.resolveElement(element);
} }
applyEngineFilters(specification.getEngineFilters(), engineDescriptor);
}

private void applyEngineFilters(List<EngineFilter> engineFilters, EngineDescriptor engineDescriptor) {
//Todo: Currently only works with a single ClassFilter
if (engineFilters.isEmpty())
return;
ClassFilter filter = (ClassFilter) engineFilters.get(0);
TestDescriptor.Visitor filteringVisitor = new TestDescriptor.Visitor() {

@Override
public void visit(TestDescriptor descriptor, Runnable remove) {
if (descriptor instanceof ClassTestDescriptor) {
ClassTestDescriptor classTestDescriptor = (ClassTestDescriptor) descriptor;
if (!filter.acceptClass(classTestDescriptor.getTestClass()))
remove.run();
}
}
};
engineDescriptor.accept(filteringVisitor);
} }


@Override @Override
Expand Down
Expand Up @@ -22,8 +22,9 @@
"junit5:com.example.SampleTestCase#assertAllFailingTest()", "junit5:com.example.SampleTestCase#assertAllFailingTest()",
"junit5:com.example.SampleTestCase@AnInnerTestContext" }) "junit5:com.example.SampleTestCase@AnInnerTestContext" })
@Packages({ "com.example.subpackage" }) @Packages({ "com.example.subpackage" })
//@ClassNameMatches(".*TestCase.")
//@OnlyIncludeTags({ "fast" }) //@OnlyIncludeTags({ "fast" })
//@JUnit5.OnlyEngine("junit5") //@OnlyEngine("junit5")
public class JUnit4SamplesSuite { public class JUnit4SamplesSuite {


// When you have the following method, it overrides all annotations // When you have the following method, it overrides all annotations
Expand Down

0 comments on commit 891efa3

Please sign in to comment.