Skip to content

Commit

Permalink
Add support for multiple class name patterns to JUnitPlatform runner
Browse files Browse the repository at this point in the history
Issue: #474
  • Loading branch information
marcphilipp committed Oct 18, 2016
1 parent 71e32ac commit eb2be7a
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 21 deletions.
3 changes: 3 additions & 0 deletions documentation/src/docs/asciidoc/release-notes-5.0.0-M3.adoc
Expand Up @@ -47,6 +47,7 @@ on GitHub.
classpath scanning. classpath scanning.
* `ClassNameFilter.includeClassNamePattern` is now deprecated in favor of * `ClassNameFilter.includeClassNamePattern` is now deprecated in favor of
`ClassNameFilter.includeClassNamePatterns`. `ClassNameFilter.includeClassNamePatterns`.
* `@IncludeClassNamePattern` is now deprecated in favor of `@IncludeClassNamePatterns`.
* The `-p` command-line option for configuring additional classpath entries for the * The `-p` command-line option for configuring additional classpath entries for the
`ConsoleLauncher` has been renamed to `-cp` in order to align with the option names for `ConsoleLauncher` has been renamed to `-cp` in order to align with the option names for
the standard `java` executable. In addition, a new `--class-path` alias has been the standard `java` executable. In addition, a new `--class-path` alias has been
Expand Down Expand Up @@ -88,6 +89,8 @@ on GitHub.
that it can be executed as a test on the JUnit Platform). that it can be executed as a test on the JUnit Platform).
* Multiple regular expressions that are combined using OR semantics can now be passed to * Multiple regular expressions that are combined using OR semantics can now be passed to
`ClassNameFilter.includeClassNamePatterns`. `ClassNameFilter.includeClassNamePatterns`.
* Multiple regular expressions that are combined using OR semantics can now be passed via
`@IncludeClassNamePatterns` to the `JUnitPlatform` runner.




[[release-notes-5.0.0-m3-junit-jupiter]] [[release-notes-5.0.0-m3-junit-jupiter]]
Expand Down
Expand Up @@ -11,7 +11,7 @@
package example; package example;


import org.junit.platform.runner.ExcludeTags; import org.junit.platform.runner.ExcludeTags;
import org.junit.platform.runner.IncludeClassNamePattern; import org.junit.platform.runner.IncludeClassNamePatterns;
import org.junit.platform.runner.JUnitPlatform; import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.runner.SelectPackages; import org.junit.platform.runner.SelectPackages;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
Expand All @@ -31,7 +31,7 @@
*/ */
@RunWith(JUnitPlatform.class) @RunWith(JUnitPlatform.class)
@SelectPackages("example") @SelectPackages("example")
@IncludeClassNamePattern(".+(Tests|Demo)$") @IncludeClassNamePatterns(".+(Tests|Demo)$")
@ExcludeTags("exclude") @ExcludeTags("exclude")
public class DocumentationTestSuite { public class DocumentationTestSuite {
} }
Expand Up @@ -10,7 +10,7 @@


package org.junit.jupiter; package org.junit.jupiter;


import org.junit.platform.runner.IncludeClassNamePattern; import org.junit.platform.runner.IncludeClassNamePatterns;
import org.junit.platform.runner.IncludeEngines; import org.junit.platform.runner.IncludeEngines;
import org.junit.platform.runner.JUnitPlatform; import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.runner.SelectPackages; import org.junit.platform.runner.SelectPackages;
Expand All @@ -34,7 +34,7 @@
*/ */
@RunWith(JUnitPlatform.class) @RunWith(JUnitPlatform.class)
@SelectPackages("org.junit.jupiter") @SelectPackages("org.junit.jupiter")
@IncludeClassNamePattern(".*Tests?") @IncludeClassNamePatterns(".*Tests?")
@IncludeEngines("junit-jupiter") @IncludeEngines("junit-jupiter")
public class JupiterTestSuite { public class JupiterTestSuite {
} }
Expand Up @@ -10,7 +10,7 @@


package org.junit.platform.runner; package org.junit.platform.runner;


import static org.junit.platform.commons.meta.API.Usage.Maintained; import static org.junit.platform.commons.meta.API.Usage.Deprecated;


import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
Expand All @@ -33,12 +33,14 @@
* @since 1.0 * @since 1.0
* @see JUnitPlatform * @see JUnitPlatform
* @see org.junit.platform.engine.discovery.ClassNameFilter#includeClassNamePatterns * @see org.junit.platform.engine.discovery.ClassNameFilter#includeClassNamePatterns
* @deprecated Please use {@link IncludeClassNamePatterns}.
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Inherited @Inherited
@Documented @Documented
@API(Maintained) @API(Deprecated)
@Deprecated
public @interface IncludeClassNamePattern { public @interface IncludeClassNamePattern {


/** /**
Expand Down
@@ -0,0 +1,53 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/

package org.junit.platform.runner;

import static org.junit.platform.commons.meta.API.Usage.Maintained;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.platform.commons.meta.API;
import org.junit.platform.engine.discovery.ClassNameFilter;

/**
* {@code @IncludeClassNamePatterns} specifies regular expressions that are used
* to match against fully qualified class names when running a test suite via
* {@code @RunWith(JUnitPlatform.class)}.
*
* <p>The patterns are combined using OR semantics, i.e. if the fully
* qualified name of a class matches against at least one of the patterns,
* the class will be included in the test plan.
*
* @since 1.0
* @see JUnitPlatform
* @see ClassNameFilter#includeClassNamePatterns
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@Documented
@API(Maintained)
public @interface IncludeClassNamePatterns {

/**
* Regular expression used to match against fully qualified class names.
*
* <p>Defaults to {@code "^.*Tests?$"} which matches against class names
* ending in {@code Test} or {@code Tests} (in any package).
*/
String[] value() default { ClassNameFilter.STANDARD_INCLUDE_PATTERN };

}
Expand Up @@ -63,14 +63,14 @@
* in order to be picked up by IDEs and build tools. * in order to be picked up by IDEs and build tools.
* *
* <p>When used on a class that serves as a test suite and the * <p>When used on a class that serves as a test suite and the
* {@link IncludeClassNamePattern} annotation is not present, the default * {@link IncludeClassNamePatterns} annotation is not present, the default
* include pattern {@value org.junit.platform.engine.discovery.ClassNameFilter#STANDARD_INCLUDE_PATTERN} * include pattern {@value org.junit.platform.engine.discovery.ClassNameFilter#STANDARD_INCLUDE_PATTERN}
* is used to avoid loading classes unnecessarily. * is used to avoid loading classes unnecessarily.
* *
* @since 1.0 * @since 1.0
* @see SelectPackages * @see SelectPackages
* @see SelectClasses * @see SelectClasses
* @see IncludeClassNamePattern * @see IncludeClassNamePatterns
* @see IncludeTags * @see IncludeTags
* @see ExcludeTags * @see ExcludeTags
* @see IncludeEngines * @see IncludeEngines
Expand Down Expand Up @@ -158,9 +158,9 @@ private <T> List<DiscoverySelector> transform(T[] sourceElements, Function<T, Di
} }


private void addIncludeClassNamePatternFilter(LauncherDiscoveryRequestBuilder requestBuilder, boolean isSuite) { private void addIncludeClassNamePatternFilter(LauncherDiscoveryRequestBuilder requestBuilder, boolean isSuite) {
String pattern = getIncludeClassNamePattern(isSuite); String[] patterns = getIncludeClassNamePatterns(isSuite);
if (!pattern.isEmpty()) { if (patterns.length > 0) {
requestBuilder.filters(includeClassNamePatterns(pattern)); requestBuilder.filters(includeClassNamePatterns(patterns));
} }
} }


Expand Down Expand Up @@ -216,9 +216,35 @@ private String[] getExcludedEngineIds() {
return getValueFromAnnotation(ExcludeEngines.class, ExcludeEngines::value, EMPTY_STRING_ARRAY); return getValueFromAnnotation(ExcludeEngines.class, ExcludeEngines::value, EMPTY_STRING_ARRAY);
} }


private String getIncludeClassNamePattern(boolean isSuite) { @SuppressWarnings("deprecation")
return getValueFromAnnotation(IncludeClassNamePattern.class, IncludeClassNamePattern::value, private String[] getIncludeClassNamePatterns(boolean isSuite) {
isSuite ? STANDARD_INCLUDE_PATTERN : EMPTY_STRING).trim(); String[] patterns = getIncludeClassNamePatterns();
if (patterns.length == 0 && isSuite) {
return new String[] { STANDARD_INCLUDE_PATTERN };
}
Preconditions.containsNoNullElements(patterns, "IncludeClassNamePatterns must not contain null elements");
trim(patterns);
return patterns;
}

private void trim(String[] patterns) {
for (int i = 0; i < patterns.length; i++) {
patterns[i] = patterns[i].trim();
}
}

@SuppressWarnings("deprecation")
private String[] getIncludeClassNamePatterns() {
String[] patterns = getValueFromAnnotation(IncludeClassNamePatterns.class, IncludeClassNamePatterns::value,
new String[0]);
String patternFromDeprecatedAnnotation = getValueFromAnnotation(IncludeClassNamePattern.class,
IncludeClassNamePattern::value, EMPTY_STRING);
Preconditions.condition(patterns.length == 0 || patternFromDeprecatedAnnotation.isEmpty(),
"You must not specify both @IncludeClassNamePattern and @IncludeClassNamePatterns");
if (patterns.length == 0 && !patternFromDeprecatedAnnotation.isEmpty()) {
patterns = new String[] { patternFromDeprecatedAnnotation };
}
return patterns;
} }


private <A extends Annotation, V> V getValueFromAnnotation(Class<A> annotationClass, Function<A, V> extractor, private <A extends Annotation, V> V getValueFromAnnotation(Class<A> annotationClass, Function<A, V> extractor,
Expand Down
Expand Up @@ -10,7 +10,7 @@


package org.junit.platform.surefire.provider; package org.junit.platform.surefire.provider;


import org.junit.platform.runner.IncludeClassNamePattern; import org.junit.platform.runner.IncludeClassNamePatterns;
import org.junit.platform.runner.IncludeEngines; import org.junit.platform.runner.IncludeEngines;
import org.junit.platform.runner.JUnitPlatform; import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.runner.SelectPackages; import org.junit.platform.runner.SelectPackages;
Expand All @@ -33,7 +33,7 @@
*/ */
@RunWith(JUnitPlatform.class) @RunWith(JUnitPlatform.class)
@SelectPackages("org.junit.platform.surefire.provider") @SelectPackages("org.junit.platform.surefire.provider")
@IncludeClassNamePattern(".*Tests?") @IncludeClassNamePatterns(".*Tests?")
@IncludeEngines("junit-jupiter") @IncludeEngines("junit-jupiter")
public class SurefireProviderTestSuite { public class SurefireProviderTestSuite {
} }
Expand Up @@ -10,7 +10,7 @@


package org.junit.vintage.engine; package org.junit.vintage.engine;


import org.junit.platform.runner.IncludeClassNamePattern; import org.junit.platform.runner.IncludeClassNamePatterns;
import org.junit.platform.runner.IncludeEngines; import org.junit.platform.runner.IncludeEngines;
import org.junit.platform.runner.JUnitPlatform; import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.runner.SelectPackages; import org.junit.platform.runner.SelectPackages;
Expand All @@ -33,7 +33,7 @@
*/ */
@RunWith(JUnitPlatform.class) @RunWith(JUnitPlatform.class)
@SelectPackages("org.junit.vintage.engine") @SelectPackages("org.junit.vintage.engine")
@IncludeClassNamePattern(".*Tests?") @IncludeClassNamePatterns(".*Tests?")
@IncludeEngines("junit-jupiter") @IncludeEngines("junit-jupiter")
public class VintageTestEngineTestSuite { public class VintageTestEngineTestSuite {
} }
Expand Up @@ -10,7 +10,7 @@


package org.junit.platform; package org.junit.platform;


import org.junit.platform.runner.IncludeClassNamePattern; import org.junit.platform.runner.IncludeClassNamePatterns;
import org.junit.platform.runner.IncludeEngines; import org.junit.platform.runner.IncludeEngines;
import org.junit.platform.runner.JUnitPlatform; import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.runner.SelectPackages; import org.junit.platform.runner.SelectPackages;
Expand All @@ -33,7 +33,7 @@
*/ */
@RunWith(JUnitPlatform.class) @RunWith(JUnitPlatform.class)
@SelectPackages("org.junit.platform") @SelectPackages("org.junit.platform")
@IncludeClassNamePattern(".*Tests?") @IncludeClassNamePatterns(".*Tests?")
@IncludeEngines("junit-jupiter") @IncludeEngines("junit-jupiter")
public class JUnitPlatformTestSuite { public class JUnitPlatformTestSuite {
} }
Expand Up @@ -42,6 +42,7 @@


import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.platform.commons.util.PreconditionViolationException;
import org.junit.platform.engine.EngineDiscoveryRequest; import org.junit.platform.engine.EngineDiscoveryRequest;
import org.junit.platform.engine.EngineExecutionListener; import org.junit.platform.engine.EngineExecutionListener;
import org.junit.platform.engine.ExecutionRequest; import org.junit.platform.engine.ExecutionRequest;
Expand Down Expand Up @@ -225,8 +226,10 @@ class TestCase {
} }


@Test @Test
void addsExplicitClassNameFilterToRequestWhenFilterClassNameAnnotationIsPresent() throws Exception { void addsExplicitClassNameFilterToRequestWhenDeprecatedIncludeClassNamePatternAnnotationIsPresent()
throws Exception {


@SuppressWarnings("deprecation")
@IncludeClassNamePattern(".*Foo") @IncludeClassNamePattern(".*Foo")
class TestCase { class TestCase {
} }
Expand All @@ -237,6 +240,76 @@ class TestCase {
assertThat(getOnlyElement(filters).toString()).contains(".*Foo"); assertThat(getOnlyElement(filters).toString()).contains(".*Foo");
} }


@Test
void addsExplicitClassNameFilterToRequestWhenIncludeClassNamePatternsAnnotationIsPresent() throws Exception {

@IncludeClassNamePatterns({ ".*Foo", "Bar.*" })
class TestCase {
}

LauncherDiscoveryRequest request = instantiateRunnerAndCaptureGeneratedRequest(TestCase.class);

List<ClassNameFilter> filters = request.getDiscoveryFiltersByType(ClassNameFilter.class);
assertThat(getOnlyElement(filters).toString()).contains(".*Foo", "Bar.*");
}

@Test
void usesStandardIncludePatternWhenIncludeClassNamePatternsAnnotationIsPresentWithoutArguments()
throws Exception {

@IncludeClassNamePatterns
class TestCase {
}

LauncherDiscoveryRequest request = instantiateRunnerAndCaptureGeneratedRequest(TestCase.class);

List<ClassNameFilter> filters = request.getDiscoveryFiltersByType(ClassNameFilter.class);
assertThat(getOnlyElement(filters).toString()).contains(STANDARD_INCLUDE_PATTERN);
}

@Test
void addsExplicitClassNameFilterWhenIncludeClassNamePatternsAnnotationIsPresentWithEmptyArguments()
throws Exception {

@IncludeClassNamePatterns({})
class TestCase {
}

LauncherDiscoveryRequest request = instantiateRunnerAndCaptureGeneratedRequest(TestCase.class);

List<ClassNameFilter> filters = request.getDiscoveryFiltersByType(ClassNameFilter.class);
assertThat(filters).isEmpty();
}

@Test
void throwsExceptionWhenBothIncludeClassNamePatternsAnnotationsArePresent() throws Exception {

@SuppressWarnings("deprecation")
@IncludeClassNamePattern("foo")
@IncludeClassNamePatterns("foo")
class TestCase {
}

PreconditionViolationException exception = assertThrows(PreconditionViolationException.class,
() -> instantiateRunnerAndCaptureGeneratedRequest(TestCase.class));

assertThat(exception).hasMessage(
"You must not specify both @IncludeClassNamePattern and @IncludeClassNamePatterns");
}

@Test
void trimsArgumentsOfIncludeClassNamePatternsAnnotation() throws Exception {

@IncludeClassNamePatterns({ " foo", "bar " })
class TestCase {
}

LauncherDiscoveryRequest request = instantiateRunnerAndCaptureGeneratedRequest(TestCase.class);

List<ClassNameFilter> filters = request.getDiscoveryFiltersByType(ClassNameFilter.class);
assertThat(getOnlyElement(filters).toString()).contains("'foo'", "'bar'");
}

@Test @Test
void convertsTestIdentifiersIntoDescriptions() throws Exception { void convertsTestIdentifiersIntoDescriptions() throws Exception {


Expand Down

0 comments on commit eb2be7a

Please sign in to comment.