From 44a409579237deabd5eb7f30f52ae042d4c66023 Mon Sep 17 00:00:00 2001 From: "Y.H. Kuo" Date: Sat, 25 Feb 2023 02:51:30 +0800 Subject: [PATCH] Introduce method filter support in the ConsoleLauncher Issue: #3107 --- .../console/options/AvailableOptions.java | 22 + .../console/options/CommandLineOptions.java | 18 + .../tasks/DiscoveryRequestCreator.java | 10 + .../launcher/AbstractMethodFilter.java | 58 ++ .../launcher/ExcludeMethodFilter.java | 59 ++ .../launcher/IncludeMethodFilter.java | 58 ++ .../junit/platform/launcher/MethodFilter.java | 107 ++++ .../ConsoleLauncherIntegrationTests.java | 16 + .../PicocliCommandLineOptionsParserTests.java | 531 ++++++++++-------- .../tasks/DiscoveryRequestCreatorTests.java | 37 +- .../platform/launcher/MethodFilterTests.java | 166 ++++++ 11 files changed, 834 insertions(+), 248 deletions(-) create mode 100644 junit-platform-launcher/src/main/java/org/junit/platform/launcher/AbstractMethodFilter.java create mode 100644 junit-platform-launcher/src/main/java/org/junit/platform/launcher/ExcludeMethodFilter.java create mode 100644 junit-platform-launcher/src/main/java/org/junit/platform/launcher/IncludeMethodFilter.java create mode 100644 junit-platform-launcher/src/main/java/org/junit/platform/launcher/MethodFilter.java create mode 100644 platform-tests/src/test/java/org/junit/platform/launcher/MethodFilterTests.java diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/AvailableOptions.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/AvailableOptions.java index fce8ad509aff..e1674c1f5469 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/AvailableOptions.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/AvailableOptions.java @@ -236,6 +236,24 @@ static class FilterOptions { @Option(names = { "-exclude-package" }, arity = "1", hidden = true) private List excludePackages2 = new ArrayList<>(); + @Option(names = { + "--include-methodname" }, paramLabel = "PATTERN", arity = "1", description = "Provide a regular expression to include only methods whose fully qualified names match. " + + // + "When this option is repeated, all patterns will be combined using OR semantics.") + private List includeMethodNamePatterns = new ArrayList<>(); + + @Option(names = { "-include-methodname" }, arity = "1", hidden = true) + private List includeMethodNamePatterns2 = new ArrayList<>(); + + @Option(names = { + "--exclude-methodname" }, paramLabel = "PATTERN", arity = "1", description = "Provide a regular expression to exclude those methods whose fully qualified names match. " + + // + "When this option is repeated, all patterns will be combined using OR semantics.") + private List excludeMethodNamePatterns = new ArrayList<>(); + + @Option(names = { "-exclude-methodname" }, arity = "1", hidden = true) + private List excludeMethodNamePatterns2 = new ArrayList<>(); + @Option(names = { "-t", "--include-tag" }, paramLabel = "TAG", arity = "1", description = "Provide a tag or tag expression to include only tests whose tags match. " + // @@ -273,6 +291,10 @@ private void applyTo(CommandLineOptions result) { result.setExcludedClassNamePatterns(merge(this.excludeClassNamePatterns, this.excludeClassNamePatterns2)); result.setIncludedPackages(merge(this.includePackages, this.includePackages2)); result.setExcludedPackages(merge(this.excludePackages, this.excludePackages2)); + result.setIncludedMethodNamePatterns( + merge(this.includeMethodNamePatterns, this.includeMethodNamePatterns2)); + result.setExcludedMethodNamePatterns( + merge(this.excludeMethodNamePatterns, this.excludeMethodNamePatterns2)); result.setIncludedTagExpressions(merge(this.includedTags, this.includedTags2)); result.setExcludedTagExpressions(merge(this.excludedTags, this.excludedTags2)); result.setIncludedEngines(merge(this.includedEngines, this.includedEngines2)); diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandLineOptions.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandLineOptions.java index 4321acbcd598..bd84d86752ac 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandLineOptions.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandLineOptions.java @@ -77,6 +77,8 @@ public class CommandLineOptions { private List excludedClassNamePatterns = emptyList(); private List includedPackages = emptyList(); private List excludedPackages = emptyList(); + private List includedMethodNamePatterns = emptyList(); + private List excludedMethodNamePatterns = emptyList(); private List includedEngines = emptyList(); private List excludedEngines = emptyList(); private List includedTagExpressions = emptyList(); @@ -312,6 +314,22 @@ public void setExcludedPackages(List excludedPackages) { this.excludedPackages = excludedPackages; } + public List getIncludedMethodNamePatterns() { + return includedMethodNamePatterns; + } + + public void setIncludedMethodNamePatterns(List includedMethodNamePatterns) { + this.includedMethodNamePatterns = includedMethodNamePatterns; + } + + public List getExcludedMethodNamePatterns() { + return excludedMethodNamePatterns; + } + + public void setExcludedMethodNamePatterns(List excludedMethodNamePatterns) { + this.excludedMethodNamePatterns = excludedMethodNamePatterns; + } + public List getIncludedEngines() { return this.includedEngines; } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java index 2fb1ea03a74b..df9fa5757254 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/tasks/DiscoveryRequestCreator.java @@ -18,6 +18,8 @@ import static org.junit.platform.engine.discovery.PackageNameFilter.includePackageNames; import static org.junit.platform.launcher.EngineFilter.excludeEngines; import static org.junit.platform.launcher.EngineFilter.includeEngines; +import static org.junit.platform.launcher.MethodFilter.excludeMethodNamePatterns; +import static org.junit.platform.launcher.MethodFilter.includeMethodNamePatterns; import static org.junit.platform.launcher.TagFilter.excludeTags; import static org.junit.platform.launcher.TagFilter.includeTags; import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; @@ -103,6 +105,14 @@ private void addFilters(LauncherDiscoveryRequestBuilder requestBuilder, CommandL requestBuilder.filters(excludePackageNames(options.getExcludedPackages())); } + if (!options.getIncludedMethodNamePatterns().isEmpty()) { + requestBuilder.filters(includeMethodNamePatterns(options.getIncludedMethodNamePatterns())); + } + + if (!options.getExcludedMethodNamePatterns().isEmpty()) { + requestBuilder.filters(excludeMethodNamePatterns(options.getExcludedMethodNamePatterns())); + } + if (!options.getIncludedTagExpressions().isEmpty()) { requestBuilder.filters(includeTags(options.getIncludedTagExpressions())); } diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/AbstractMethodFilter.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/AbstractMethodFilter.java new file mode 100644 index 000000000000..48b0bf856ebe --- /dev/null +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/AbstractMethodFilter.java @@ -0,0 +1,58 @@ +/* + * Copyright 2015-2023 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 v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.launcher; + +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toList; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.regex.Pattern; + +import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.support.descriptor.MethodSource; + +/** + * Abstract {@link MethodFilter} that servers as a superclass + * for filters including or excluding fully qualified method names + * based on pattern-matching. + * + * @since 1.10 + */ +abstract class AbstractMethodFilter implements MethodFilter { + + protected final List patterns; + protected final String patternDescription; + + AbstractMethodFilter(String... patterns) { + Preconditions.notEmpty(patterns, "patterns array must not be null or empty"); + Preconditions.containsNoNullElements(patterns, "patterns array must not contain null elements"); + this.patterns = Arrays.stream(patterns).map(Pattern::compile).collect(toList()); + this.patternDescription = Arrays.stream(patterns).collect(joining("' OR '", "'", "'")); + } + + protected Optional findMatchingPattern(String methodName) { + if (methodName == null) { + return Optional.empty(); + } + return this.patterns.stream().filter(pattern -> pattern.matcher(methodName).matches()).findAny(); + } + + protected String getFullyQualifiedMethodNameFromDescriptor(TestDescriptor descriptor) { + return descriptor.getSource() // + .filter(source -> source instanceof MethodSource) // + .map(methodSource -> ((MethodSource) methodSource)) // + .map(methodSource -> methodSource.getJavaClass().getSimpleName() + "#" + methodSource.getMethodName()) // + .orElse(null); + } +} diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/ExcludeMethodFilter.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/ExcludeMethodFilter.java new file mode 100644 index 000000000000..a33a6dafb83b --- /dev/null +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/ExcludeMethodFilter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2015-2023 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 v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.launcher; + +import static org.junit.platform.engine.FilterResult.excluded; +import static org.junit.platform.engine.FilterResult.included; + +import java.util.regex.Pattern; + +import org.junit.platform.engine.FilterResult; +import org.junit.platform.engine.TestDescriptor; + +/** + * {@link MethodFilter} that matches fully qualified method names against + * patterns in the form of regular expressions. + * + *

If the fully qualified name of a method matches against at least one + * pattern, the class will be excluded. + * + * @since 1.10 + */ +class ExcludeMethodFilter extends AbstractMethodFilter { + + ExcludeMethodFilter(String... patterns) { + super(patterns); + } + + @Override + public FilterResult apply(TestDescriptor descriptor) { + String methodName = getFullyQualifiedMethodNameFromDescriptor(descriptor); + return findMatchingPattern(methodName) // + .map(pattern -> excluded(formatExclusionReason(methodName, pattern))) // + .orElseGet(() -> included(formatInclusionReason(methodName))); + } + + private String formatInclusionReason(String methodName) { + return String.format("Method name [%s] does not match any excluded pattern: %s", methodName, + patternDescription); + } + + private String formatExclusionReason(String methodName, Pattern pattern) { + return String.format("Method name [%s] matches excluded pattern: '%s'", methodName, pattern); + } + + @Override + public String toString() { + return String.format("%s that excludes method names that match one of the following regular expressions: %s", + getClass().getSimpleName(), patternDescription); + } + +} diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/IncludeMethodFilter.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/IncludeMethodFilter.java new file mode 100644 index 000000000000..05782b06d751 --- /dev/null +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/IncludeMethodFilter.java @@ -0,0 +1,58 @@ +/* + * Copyright 2015-2023 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 v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.launcher; + +import static org.junit.platform.engine.FilterResult.excluded; +import static org.junit.platform.engine.FilterResult.included; + +import java.util.regex.Pattern; + +import org.junit.platform.engine.FilterResult; +import org.junit.platform.engine.TestDescriptor; + +/** + * {@link MethodFilter} that matches fully qualified method names against + * patterns in the form of regular expressions. + * + *

If the fully qualified name of a method matches against at least one + * pattern, the method will be included. + * + * @since 1.10 + */ +class IncludeMethodFilter extends AbstractMethodFilter { + + IncludeMethodFilter(String... patterns) { + super(patterns); + } + + @Override + public FilterResult apply(TestDescriptor descriptor) { + String methodName = getFullyQualifiedMethodNameFromDescriptor(descriptor); + return findMatchingPattern(methodName) // + .map(pattern -> included(formatInclusionReason(methodName, pattern))) // + .orElseGet(() -> excluded(formatExclusionReason(methodName))); + } + + private String formatInclusionReason(String methodName, Pattern pattern) { + return String.format("Method name [%s] matches included pattern: '%s'", methodName, pattern); + } + + private String formatExclusionReason(String methodName) { + return String.format("Method name [%s] does not match any included pattern: %s", methodName, + patternDescription); + } + + @Override + public String toString() { + return String.format("%s that includes method names that match one of the following regular expressions: %s", + getClass().getSimpleName(), patternDescription); + } +} diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/MethodFilter.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/MethodFilter.java new file mode 100644 index 000000000000..2ebf74695eb1 --- /dev/null +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/MethodFilter.java @@ -0,0 +1,107 @@ +/* + * Copyright 2015-2023 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 v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.launcher; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.reflect.Method; +import java.util.List; + +import org.apiguardian.api.API; + +/** + * {@link PostDiscoveryFilter} that is applied to the fully qualified + * name of a {@link Method}. + * + * @since 1.10 + * @see #includeMethodNamePatterns(String...) + * @see #excludeMethodNamePatterns(String...) + */ +@API(status = EXPERIMENTAL, since = "1.10") +public interface MethodFilter extends PostDiscoveryFilter { + + /** + * Create a new include {@link MethodFilter} based on the + * supplied patterns. + * + *

The patterns are combined using OR semantics, i.e. if the fully + * qualified name of a method matches against at least one of the patterns, + * the method will be included in the result set. + * + * @param patterns regular expressions to match against fully qualified + * method names; never {@code null}, empty, or containing {@code null} + * @see Class#getSimpleName() + * @see Method#getName() + * @see #includeMethodNamePatterns(List) + * @see #excludeMethodNamePatterns(String...) + */ + static MethodFilter includeMethodNamePatterns(String... patterns) { + return new IncludeMethodFilter(patterns); + } + + /** + * Create a new include {@link MethodFilter} based on the + * supplied patterns. + * + *

The patterns are combined using OR semantics, i.e. if the fully + * qualified name of a method matches against at least one of the patterns, + * the method will be included in the result set. + * + * @param patterns regular expressions to match against fully qualified + * method names; never {@code null}, empty, or containing {@code null} + * @see Class#getSimpleName() + * @see Method#getName() + * @see #includeMethodNamePatterns(String...) + * @see #excludeMethodNamePatterns(String...) + */ + static MethodFilter includeMethodNamePatterns(List patterns) { + return includeMethodNamePatterns(patterns.toArray(new String[0])); + } + + /** + * Create a new exclude {@link MethodFilter} based on the + * supplied patterns. + * + *

The patterns are combined using OR semantics, i.e. if the fully + * qualified name of a method matches against at least one of the patterns, + * the method will be excluded from the result set. + * + * @param patterns regular expressions to match against fully qualified + * method names; never {@code null}, empty, or containing {@code null} + * @see Class#getSimpleName() + * @see Method#getName() + * @see #excludeMethodNamePatterns(List) + * @see #includeMethodNamePatterns(String...) + */ + static MethodFilter excludeMethodNamePatterns(String... patterns) { + return new ExcludeMethodFilter(patterns); + } + + /** + * Create a new exclude {@link MethodFilter} based on the + * supplied patterns. + * + *

The patterns are combined using OR semantics, i.e. if the fully + * qualified name of a method matches against at least one of the patterns, + * the method will be excluded from the result set. + * + * @param patterns regular expressions to match against fully qualified + * method names; never {@code null}, empty, or containing {@code null} + * @see Class#getSimpleName() + * @see Method#getName() + * @see #excludeMethodNamePatterns(String...) + * @see #includeMethodNamePatterns(String...) + */ + static MethodFilter excludeMethodNamePatterns(List patterns) { + return excludeMethodNamePatterns(patterns.toArray(new String[0])); + } + +} diff --git a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherIntegrationTests.java b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherIntegrationTests.java index b8ccb37f3582..5613a787f6c5 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherIntegrationTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherIntegrationTests.java @@ -38,6 +38,22 @@ void executeWithoutExcludeClassnameOptionDoesNotExcludeClassesAndMustIncludeAllC assertEquals(9, new ConsoleLauncherWrapper().execute(args).getTestsFoundCount()); } + /** + * @since 1.10 + */ + @Test + void executeWithExcludeMethodNameOptionExcludesMethods() { + String[] args = { "-e", "junit-jupiter", "-p", "org.junit.platform.console.subpackage", "--exclude-methodname", + ".+#test" }; + var result = new ConsoleLauncherWrapper().execute(args); + assertAll("all subpackage test methods are excluded by the method name filter", // + () -> assertArrayEquals(args, result.args), // + () -> assertEquals(0, result.code), // + () -> assertEquals(0, result.getTestsFoundCount()), // + () -> assertTrue(isBlank(result.err)) // + ); + } + @Test void executeWithExcludeClassnameOptionExcludesClasses() { String[] args = { "-e", "junit-jupiter", "-p", "org.junit.platform.console.subpackage", "--exclude-classname", diff --git a/platform-tests/src/test/java/org/junit/platform/console/options/PicocliCommandLineOptionsParserTests.java b/platform-tests/src/test/java/org/junit/platform/console/options/PicocliCommandLineOptionsParserTests.java index ed793dcf6159..5a9b4a74d284 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/options/PicocliCommandLineOptionsParserTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/options/PicocliCommandLineOptionsParserTests.java @@ -60,55 +60,57 @@ void parseNoArguments() { var options = createParser().parse(noArguments); // @formatter:off - assertAll( - () -> assertFalse(options.isAnsiColorOutputDisabled()), - () -> assertFalse(options.isDisplayHelp()), - () -> assertEquals(CommandLineOptions.DEFAULT_DETAILS, options.getDetails()), - () -> assertFalse(options.isScanClasspath()), - () -> assertEquals(List.of(STANDARD_INCLUDE_PATTERN), options.getIncludedClassNamePatterns()), - () -> assertEquals(List.of(), options.getExcludedClassNamePatterns()), - () -> assertEquals(List.of(), options.getIncludedPackages()), - () -> assertEquals(List.of(), options.getExcludedPackages()), - () -> assertEquals(List.of(), options.getIncludedTagExpressions()), - () -> assertEquals(List.of(), options.getExcludedTagExpressions()), - () -> assertEquals(List.of(), options.getAdditionalClasspathEntries()), - () -> assertEquals(Optional.empty(), options.getReportsDir()), - () -> assertEquals(List.of(), options.getSelectedUris()), - () -> assertEquals(List.of(), options.getSelectedFiles()), - () -> assertEquals(List.of(), options.getSelectedDirectories()), - () -> assertEquals(List.of(), options.getSelectedModules()), - () -> assertEquals(List.of(), options.getSelectedPackages()), - () -> assertEquals(List.of(), options.getSelectedMethods()), - () -> assertEquals(List.of(), options.getSelectedClasspathEntries()), - () -> assertEquals(Map.of(), options.getConfigurationParameters()) - ); - // @formatter:on + assertAll( + () -> assertFalse(options.isAnsiColorOutputDisabled()), + () -> assertFalse(options.isDisplayHelp()), + () -> assertEquals(CommandLineOptions.DEFAULT_DETAILS, options.getDetails()), + () -> assertFalse(options.isScanClasspath()), + () -> assertEquals(List.of(STANDARD_INCLUDE_PATTERN), options.getIncludedClassNamePatterns()), + () -> assertEquals(List.of(), options.getExcludedClassNamePatterns()), + () -> assertEquals(List.of(), options.getIncludedPackages()), + () -> assertEquals(List.of(), options.getExcludedPackages()), + () -> assertEquals(List.of(), options.getIncludedMethodNamePatterns()), + () -> assertEquals(List.of(), options.getExcludedMethodNamePatterns()), + () -> assertEquals(List.of(), options.getIncludedTagExpressions()), + () -> assertEquals(List.of(), options.getExcludedTagExpressions()), + () -> assertEquals(List.of(), options.getAdditionalClasspathEntries()), + () -> assertEquals(Optional.empty(), options.getReportsDir()), + () -> assertEquals(List.of(), options.getSelectedUris()), + () -> assertEquals(List.of(), options.getSelectedFiles()), + () -> assertEquals(List.of(), options.getSelectedDirectories()), + () -> assertEquals(List.of(), options.getSelectedModules()), + () -> assertEquals(List.of(), options.getSelectedPackages()), + () -> assertEquals(List.of(), options.getSelectedMethods()), + () -> assertEquals(List.of(), options.getSelectedClasspathEntries()), + () -> assertEquals(Map.of(), options.getConfigurationParameters()) + ); + // @formatter:on } @Test void parseSwitches() { // @formatter:off - assertAll( - () -> assertParses("disable ansi", CommandLineOptions::isAnsiColorOutputDisabled, "--disable-ansi-colors"), - () -> assertParses("help", CommandLineOptions::isDisplayHelp, "-h", "--help"), - () -> assertParses("scan class path", CommandLineOptions::isScanClasspath, "--scan-class-path") - ); - // @formatter:on + assertAll( + () -> assertParses("disable ansi", CommandLineOptions::isAnsiColorOutputDisabled, "--disable-ansi-colors"), + () -> assertParses("help", CommandLineOptions::isDisplayHelp, "-h", "--help"), + () -> assertParses("scan class path", CommandLineOptions::isScanClasspath, "--scan-class-path") + ); + // @formatter:on } @ParameterizedTest @EnumSource void parseValidDetails(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(Details.VERBOSE, type.parseArgLine("--details verbose").getDetails()), - () -> assertEquals(Details.TREE, type.parseArgLine("--details tree").getDetails()), - () -> assertEquals(Details.FLAT, type.parseArgLine("--details flat").getDetails()), - () -> assertEquals(Details.NONE, type.parseArgLine("--details NONE").getDetails()), - () -> assertEquals(Details.NONE, type.parseArgLine("--details none").getDetails()), - () -> assertEquals(Details.NONE, type.parseArgLine("--details None").getDetails()) - ); - // @formatter:on + assertAll( + () -> assertEquals(Details.VERBOSE, type.parseArgLine("--details verbose").getDetails()), + () -> assertEquals(Details.TREE, type.parseArgLine("--details tree").getDetails()), + () -> assertEquals(Details.FLAT, type.parseArgLine("--details flat").getDetails()), + () -> assertEquals(Details.NONE, type.parseArgLine("--details NONE").getDetails()), + () -> assertEquals(Details.NONE, type.parseArgLine("--details none").getDetails()), + () -> assertEquals(Details.NONE, type.parseArgLine("--details None").getDetails()) + ); + // @formatter:on } @Test @@ -120,14 +122,14 @@ void parseInvalidDetails() { @EnumSource void parseValidDetailsTheme(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(Theme.ASCII, type.parseArgLine("--details-theme ascii").getTheme()), - () -> assertEquals(Theme.ASCII, type.parseArgLine("--details-theme ASCII").getTheme()), - () -> assertEquals(Theme.UNICODE, type.parseArgLine("--details-theme unicode").getTheme()), - () -> assertEquals(Theme.UNICODE, type.parseArgLine("--details-theme UNICODE").getTheme()), - () -> assertEquals(Theme.UNICODE, type.parseArgLine("--details-theme uniCode").getTheme()) - ); - // @formatter:on + assertAll( + () -> assertEquals(Theme.ASCII, type.parseArgLine("--details-theme ascii").getTheme()), + () -> assertEquals(Theme.ASCII, type.parseArgLine("--details-theme ASCII").getTheme()), + () -> assertEquals(Theme.UNICODE, type.parseArgLine("--details-theme unicode").getTheme()), + () -> assertEquals(Theme.UNICODE, type.parseArgLine("--details-theme UNICODE").getTheme()), + () -> assertEquals(Theme.UNICODE, type.parseArgLine("--details-theme uniCode").getTheme()) + ); + // @formatter:on } @Test @@ -139,24 +141,24 @@ void parseInvalidDetailsTheme() { @EnumSource void parseValidIncludeClassNamePatterns(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of(".*Test"), type.parseArgLine("-n .*Test").getIncludedClassNamePatterns()), - () -> assertEquals(List.of(".*Test", ".*Tests"), type.parseArgLine("--include-classname .*Test --include-classname .*Tests").getIncludedClassNamePatterns()), - () -> assertEquals(List.of(".*Test"), type.parseArgLine("--include-classname=.*Test").getIncludedClassNamePatterns()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(".*Test"), type.parseArgLine("-n .*Test").getIncludedClassNamePatterns()), + () -> assertEquals(List.of(".*Test", ".*Tests"), type.parseArgLine("--include-classname .*Test --include-classname .*Tests").getIncludedClassNamePatterns()), + () -> assertEquals(List.of(".*Test"), type.parseArgLine("--include-classname=.*Test").getIncludedClassNamePatterns()) + ); + // @formatter:on } @ParameterizedTest @EnumSource void parseValidExcludeClassNamePatterns(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of(".*Test"), type.parseArgLine("-N .*Test").getExcludedClassNamePatterns()), - () -> assertEquals(List.of(".*Test", ".*Tests"), type.parseArgLine("--exclude-classname .*Test --exclude-classname .*Tests").getExcludedClassNamePatterns()), - () -> assertEquals(List.of(".*Test"), type.parseArgLine("--exclude-classname=.*Test").getExcludedClassNamePatterns()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(".*Test"), type.parseArgLine("-N .*Test").getExcludedClassNamePatterns()), + () -> assertEquals(List.of(".*Test", ".*Tests"), type.parseArgLine("--exclude-classname .*Test --exclude-classname .*Tests").getExcludedClassNamePatterns()), + () -> assertEquals(List.of(".*Test"), type.parseArgLine("--exclude-classname=.*Test").getExcludedClassNamePatterns()) + ); + // @formatter:on } @Test @@ -178,43 +180,95 @@ void parseInvalidExcludeClassNamePatterns() { @EnumSource void parseValidIncludedPackages(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of("org.junit.included"), - type.parseArgLine("--include-package org.junit.included").getIncludedPackages()), - () -> assertEquals(List.of("org.junit.included"), - type.parseArgLine("--include-package=org.junit.included").getIncludedPackages()), - () -> assertEquals(List.of("org.junit.included1", "org.junit.included2"), - type.parseArgLine("--include-package org.junit.included1 --include-package org.junit.included2").getIncludedPackages()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of("org.junit.included"), + type.parseArgLine("--include-package org.junit.included").getIncludedPackages()), + () -> assertEquals(List.of("org.junit.included"), + type.parseArgLine("--include-package=org.junit.included").getIncludedPackages()), + () -> assertEquals(List.of("org.junit.included1", "org.junit.included2"), + type.parseArgLine("--include-package org.junit.included1 --include-package org.junit.included2").getIncludedPackages()) + ); + // @formatter:on } @ParameterizedTest @EnumSource void parseValidExcludedPackages(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of("org.junit.excluded"), - type.parseArgLine("--exclude-package org.junit.excluded").getExcludedPackages()), - () -> assertEquals(List.of("org.junit.excluded"), - type.parseArgLine("--exclude-package=org.junit.excluded").getExcludedPackages()), - () -> assertEquals(List.of("org.junit.excluded1", "org.junit.excluded2"), - type.parseArgLine("--exclude-package org.junit.excluded1 --exclude-package org.junit.excluded2").getExcludedPackages()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of("org.junit.excluded"), + type.parseArgLine("--exclude-package org.junit.excluded").getExcludedPackages()), + () -> assertEquals(List.of("org.junit.excluded"), + type.parseArgLine("--exclude-package=org.junit.excluded").getExcludedPackages()), + () -> assertEquals(List.of("org.junit.excluded1", "org.junit.excluded2"), + type.parseArgLine("--exclude-package org.junit.excluded1 --exclude-package org.junit.excluded2").getExcludedPackages()) + ); + // @formatter:on + } + + /** + * @since 1.10 + */ + @ParameterizedTest + @EnumSource + void parseValidIncludeMethodNamePatterns(ArgsType type) { + // @formatter:off + assertAll( + () -> assertEquals(List.of(".+#method.*"), + type.parseArgLine("--include-methodname .+#method.*").getIncludedMethodNamePatterns()), + () -> assertEquals(List.of(".+#methodA.*", ".+#methodB.*"), + type.parseArgLine("--include-methodname .+#methodA.* --include-methodname .+#methodB.*").getIncludedMethodNamePatterns()), + () -> assertEquals(List.of(".+#method.*"), + type.parseArgLine("--include-methodname=.+#method.*").getIncludedMethodNamePatterns()) + ); + // @formatter:on + } + + /** + * @since 1.10 + */ + @ParameterizedTest + @EnumSource + void parseValidExcludeMethodNamePatterns(ArgsType type) { + // @formatter:off + assertAll( + () -> assertEquals(List.of(".+#method.*"), + type.parseArgLine("--exclude-methodname .+#method.*").getExcludedMethodNamePatterns()), + () -> assertEquals(List.of(".+#methodA.*", ".+#methodB.*"), + type.parseArgLine("--exclude-methodname .+#methodA.* --exclude-methodname .+#methodB.*").getExcludedMethodNamePatterns()), + () -> assertEquals(List.of(".+#method.*"), + type.parseArgLine("--exclude-methodname=.+#method.*").getExcludedMethodNamePatterns()) + ); + // @formatter:on + } + + /** + * @since 1.10 + */ + @Test + void parseInvalidIncludeMethodNamePatterns() { + assertOptionWithMissingRequiredArgumentThrowsException("--include-methodname"); + } + + /** + * @since 1.10 + */ + @Test + void parseInvalidExcludeMethodNamePatterns() { + assertOptionWithMissingRequiredArgumentThrowsException("--exclude-methodname"); } @ParameterizedTest @EnumSource void parseValidIncludedTags(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of("fast"), type.parseArgLine("-t fast").getIncludedTagExpressions()), - () -> assertEquals(List.of("fast"), type.parseArgLine("--include-tag fast").getIncludedTagExpressions()), - () -> assertEquals(List.of("fast"), type.parseArgLine("--include-tag=fast").getIncludedTagExpressions()), - () -> assertEquals(List.of("fast", "slow"), type.parseArgLine("-t fast -t slow").getIncludedTagExpressions()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of("fast"), type.parseArgLine("-t fast").getIncludedTagExpressions()), + () -> assertEquals(List.of("fast"), type.parseArgLine("--include-tag fast").getIncludedTagExpressions()), + () -> assertEquals(List.of("fast"), type.parseArgLine("--include-tag=fast").getIncludedTagExpressions()), + () -> assertEquals(List.of("fast", "slow"), type.parseArgLine("-t fast -t slow").getIncludedTagExpressions()) + ); + // @formatter:on } @Test @@ -226,13 +280,13 @@ void parseInvalidIncludedTags() { @EnumSource void parseValidExcludedTags(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of("fast"), type.parseArgLine("-T fast").getExcludedTagExpressions()), - () -> assertEquals(List.of("fast"), type.parseArgLine("--exclude-tag fast").getExcludedTagExpressions()), - () -> assertEquals(List.of("fast"), type.parseArgLine("--exclude-tag=fast").getExcludedTagExpressions()), - () -> assertEquals(List.of("fast", "slow"), type.parseArgLine("-T fast -T slow").getExcludedTagExpressions()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of("fast"), type.parseArgLine("-T fast").getExcludedTagExpressions()), + () -> assertEquals(List.of("fast"), type.parseArgLine("--exclude-tag fast").getExcludedTagExpressions()), + () -> assertEquals(List.of("fast"), type.parseArgLine("--exclude-tag=fast").getExcludedTagExpressions()), + () -> assertEquals(List.of("fast", "slow"), type.parseArgLine("-T fast -T slow").getExcludedTagExpressions()) + ); + // @formatter:on } @Test @@ -244,12 +298,12 @@ void parseInvalidExcludedTags() { @EnumSource void parseValidIncludedEngines(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of("junit-jupiter"), type.parseArgLine("-e junit-jupiter").getIncludedEngines()), - () -> assertEquals(List.of("junit-vintage"), type.parseArgLine("--include-engine junit-vintage").getIncludedEngines()), - () -> assertEquals(List.of(), type.parseArgLine("").getIncludedEngines()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of("junit-jupiter"), type.parseArgLine("-e junit-jupiter").getIncludedEngines()), + () -> assertEquals(List.of("junit-vintage"), type.parseArgLine("--include-engine junit-vintage").getIncludedEngines()), + () -> assertEquals(List.of(), type.parseArgLine("").getIncludedEngines()) + ); + // @formatter:on } @Test @@ -261,12 +315,12 @@ void parseInvalidIncludedEngines() { @EnumSource void parseValidExcludedEngines(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of("junit-jupiter"), type.parseArgLine("-E junit-jupiter").getExcludedEngines()), - () -> assertEquals(List.of("junit-vintage"), type.parseArgLine("--exclude-engine junit-vintage").getExcludedEngines()), - () -> assertEquals(List.of(), type.parseArgLine("").getExcludedEngines()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of("junit-jupiter"), type.parseArgLine("-E junit-jupiter").getExcludedEngines()), + () -> assertEquals(List.of("junit-vintage"), type.parseArgLine("--exclude-engine junit-vintage").getExcludedEngines()), + () -> assertEquals(List.of(), type.parseArgLine("").getExcludedEngines()) + ); + // @formatter:on } @Test @@ -279,19 +333,19 @@ void parseInvalidExcludedEngines() { void parseValidAdditionalClasspathEntries(ArgsType type) { var dir = Paths.get("."); // @formatter:off - assertAll( - () -> assertEquals(List.of(dir), type.parseArgLine("-cp .").getAdditionalClasspathEntries()), - () -> assertEquals(List.of(dir), type.parseArgLine("--cp .").getAdditionalClasspathEntries()), - () -> assertEquals(List.of(dir), type.parseArgLine("-classpath .").getAdditionalClasspathEntries()), - () -> assertEquals(List.of(dir), type.parseArgLine("-classpath=.").getAdditionalClasspathEntries()), - () -> assertEquals(List.of(dir), type.parseArgLine("--classpath .").getAdditionalClasspathEntries()), - () -> assertEquals(List.of(dir), type.parseArgLine("--classpath=.").getAdditionalClasspathEntries()), - () -> assertEquals(List.of(dir), type.parseArgLine("--class-path .").getAdditionalClasspathEntries()), - () -> assertEquals(List.of(dir), type.parseArgLine("--class-path=.").getAdditionalClasspathEntries()), - () -> assertEquals(List.of(dir, Path.of("lib/some.jar")), type.parseArgLine("-cp . -cp lib/some.jar").getAdditionalClasspathEntries()), - () -> assertEquals(List.of(dir, Path.of("lib/some.jar")), type.parseArgLine("-cp ." + File.pathSeparator + "lib/some.jar").getAdditionalClasspathEntries()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(dir), type.parseArgLine("-cp .").getAdditionalClasspathEntries()), + () -> assertEquals(List.of(dir), type.parseArgLine("--cp .").getAdditionalClasspathEntries()), + () -> assertEquals(List.of(dir), type.parseArgLine("-classpath .").getAdditionalClasspathEntries()), + () -> assertEquals(List.of(dir), type.parseArgLine("-classpath=.").getAdditionalClasspathEntries()), + () -> assertEquals(List.of(dir), type.parseArgLine("--classpath .").getAdditionalClasspathEntries()), + () -> assertEquals(List.of(dir), type.parseArgLine("--classpath=.").getAdditionalClasspathEntries()), + () -> assertEquals(List.of(dir), type.parseArgLine("--class-path .").getAdditionalClasspathEntries()), + () -> assertEquals(List.of(dir), type.parseArgLine("--class-path=.").getAdditionalClasspathEntries()), + () -> assertEquals(List.of(dir, Path.of("lib/some.jar")), type.parseArgLine("-cp . -cp lib/some.jar").getAdditionalClasspathEntries()), + () -> assertEquals(List.of(dir, Path.of("lib/some.jar")), type.parseArgLine("-cp ." + File.pathSeparator + "lib/some.jar").getAdditionalClasspathEntries()) + ); + // @formatter:on } @Test @@ -313,11 +367,11 @@ void parseInvalidAdditionalClasspathEntries() { void parseValidXmlReportsDirs(ArgsType type) { var dir = Paths.get("build", "test-results"); // @formatter:off - assertAll( - () -> assertEquals(Optional.of(dir), type.parseArgLine("--reports-dir build/test-results").getReportsDir()), - () -> assertEquals(Optional.of(dir), type.parseArgLine("--reports-dir=build/test-results").getReportsDir()) - ); - // @formatter:on + assertAll( + () -> assertEquals(Optional.of(dir), type.parseArgLine("--reports-dir build/test-results").getReportsDir()), + () -> assertEquals(Optional.of(dir), type.parseArgLine("--reports-dir=build/test-results").getReportsDir()) + ); + // @formatter:on } @Test @@ -329,16 +383,16 @@ void parseInvalidXmlReportsDirs() { @EnumSource void parseValidUriSelectors(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("-u file:///foo.txt").getSelectedUris()), - () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("--u file:///foo.txt").getSelectedUris()), - () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("-select-uri file:///foo.txt").getSelectedUris()), - () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("-select-uri=file:///foo.txt").getSelectedUris()), - () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("--select-uri file:///foo.txt").getSelectedUris()), - () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("--select-uri=file:///foo.txt").getSelectedUris()), - () -> assertEquals(List.of(selectUri("file:///foo.txt"), selectUri("https://example")), type.parseArgLine("-u file:///foo.txt -u https://example").getSelectedUris()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("-u file:///foo.txt").getSelectedUris()), + () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("--u file:///foo.txt").getSelectedUris()), + () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("-select-uri file:///foo.txt").getSelectedUris()), + () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("-select-uri=file:///foo.txt").getSelectedUris()), + () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("--select-uri file:///foo.txt").getSelectedUris()), + () -> assertEquals(List.of(selectUri("file:///foo.txt")), type.parseArgLine("--select-uri=file:///foo.txt").getSelectedUris()), + () -> assertEquals(List.of(selectUri("file:///foo.txt"), selectUri("https://example")), type.parseArgLine("-u file:///foo.txt -u https://example").getSelectedUris()) + ); + // @formatter:on } @Test @@ -350,16 +404,16 @@ void parseInvalidUriSelectors() { @EnumSource void parseValidFileSelectors(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("-f foo.txt").getSelectedFiles()), - () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("--f foo.txt").getSelectedFiles()), - () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("-select-file foo.txt").getSelectedFiles()), - () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("-select-file=foo.txt").getSelectedFiles()), - () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("--select-file foo.txt").getSelectedFiles()), - () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("--select-file=foo.txt").getSelectedFiles()), - () -> assertEquals(List.of(selectFile("foo.txt"), selectFile("bar.csv")), type.parseArgLine("-f foo.txt -f bar.csv").getSelectedFiles()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("-f foo.txt").getSelectedFiles()), + () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("--f foo.txt").getSelectedFiles()), + () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("-select-file foo.txt").getSelectedFiles()), + () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("-select-file=foo.txt").getSelectedFiles()), + () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("--select-file foo.txt").getSelectedFiles()), + () -> assertEquals(List.of(selectFile("foo.txt")), type.parseArgLine("--select-file=foo.txt").getSelectedFiles()), + () -> assertEquals(List.of(selectFile("foo.txt"), selectFile("bar.csv")), type.parseArgLine("-f foo.txt -f bar.csv").getSelectedFiles()) + ); + // @formatter:on } @Test @@ -371,16 +425,16 @@ void parseInvalidFileSelectors() { @EnumSource void parseValidDirectorySelectors(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("-d foo/bar").getSelectedDirectories()), - () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("--d foo/bar").getSelectedDirectories()), - () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("-select-directory foo/bar").getSelectedDirectories()), - () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("-select-directory=foo/bar").getSelectedDirectories()), - () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("--select-directory foo/bar").getSelectedDirectories()), - () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("--select-directory=foo/bar").getSelectedDirectories()), - () -> assertEquals(List.of(selectDirectory("foo/bar"), selectDirectory("bar/qux")), type.parseArgLine("-d foo/bar -d bar/qux").getSelectedDirectories()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("-d foo/bar").getSelectedDirectories()), + () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("--d foo/bar").getSelectedDirectories()), + () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("-select-directory foo/bar").getSelectedDirectories()), + () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("-select-directory=foo/bar").getSelectedDirectories()), + () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("--select-directory foo/bar").getSelectedDirectories()), + () -> assertEquals(List.of(selectDirectory("foo/bar")), type.parseArgLine("--select-directory=foo/bar").getSelectedDirectories()), + () -> assertEquals(List.of(selectDirectory("foo/bar"), selectDirectory("bar/qux")), type.parseArgLine("-d foo/bar -d bar/qux").getSelectedDirectories()) + ); + // @formatter:on } @Test @@ -392,16 +446,16 @@ void parseInvalidDirectorySelectors() { @EnumSource void parseValidModuleSelectors(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("-o com.acme.foo").getSelectedModules()), - () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("--o com.acme.foo").getSelectedModules()), - () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("-select-module com.acme.foo").getSelectedModules()), - () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("-select-module=com.acme.foo").getSelectedModules()), - () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("--select-module com.acme.foo").getSelectedModules()), - () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("--select-module=com.acme.foo").getSelectedModules()), - () -> assertEquals(List.of(selectModule("com.acme.foo"), selectModule("com.example.bar")), type.parseArgLine("-o com.acme.foo -o com.example.bar").getSelectedModules()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("-o com.acme.foo").getSelectedModules()), + () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("--o com.acme.foo").getSelectedModules()), + () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("-select-module com.acme.foo").getSelectedModules()), + () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("-select-module=com.acme.foo").getSelectedModules()), + () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("--select-module com.acme.foo").getSelectedModules()), + () -> assertEquals(List.of(selectModule("com.acme.foo")), type.parseArgLine("--select-module=com.acme.foo").getSelectedModules()), + () -> assertEquals(List.of(selectModule("com.acme.foo"), selectModule("com.example.bar")), type.parseArgLine("-o com.acme.foo -o com.example.bar").getSelectedModules()) + ); + // @formatter:on } @Test @@ -413,16 +467,16 @@ void parseInvalidModuleSelectors() { @EnumSource void parseValidPackageSelectors(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("-p com.acme.foo").getSelectedPackages()), - () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("--p com.acme.foo").getSelectedPackages()), - () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("-select-package com.acme.foo").getSelectedPackages()), - () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("-select-package=com.acme.foo").getSelectedPackages()), - () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("--select-package com.acme.foo").getSelectedPackages()), - () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("--select-package=com.acme.foo").getSelectedPackages()), - () -> assertEquals(List.of(selectPackage("com.acme.foo"), selectPackage("com.example.bar")), type.parseArgLine("-p com.acme.foo -p com.example.bar").getSelectedPackages()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("-p com.acme.foo").getSelectedPackages()), + () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("--p com.acme.foo").getSelectedPackages()), + () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("-select-package com.acme.foo").getSelectedPackages()), + () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("-select-package=com.acme.foo").getSelectedPackages()), + () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("--select-package com.acme.foo").getSelectedPackages()), + () -> assertEquals(List.of(selectPackage("com.acme.foo")), type.parseArgLine("--select-package=com.acme.foo").getSelectedPackages()), + () -> assertEquals(List.of(selectPackage("com.acme.foo"), selectPackage("com.example.bar")), type.parseArgLine("-p com.acme.foo -p com.example.bar").getSelectedPackages()) + ); + // @formatter:on } @Test @@ -434,16 +488,16 @@ void parseInvalidPackageSelectors() { @EnumSource void parseValidClassSelectors(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("-c com.acme.Foo").getSelectedClasses()), - () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("--c com.acme.Foo").getSelectedClasses()), - () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("-select-class com.acme.Foo").getSelectedClasses()), - () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("-select-class=com.acme.Foo").getSelectedClasses()), - () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("--select-class com.acme.Foo").getSelectedClasses()), - () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("--select-class=com.acme.Foo").getSelectedClasses()), - () -> assertEquals(List.of(selectClass("com.acme.Foo"), selectClass("com.example.Bar")), type.parseArgLine("-c com.acme.Foo -c com.example.Bar").getSelectedClasses()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("-c com.acme.Foo").getSelectedClasses()), + () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("--c com.acme.Foo").getSelectedClasses()), + () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("-select-class com.acme.Foo").getSelectedClasses()), + () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("-select-class=com.acme.Foo").getSelectedClasses()), + () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("--select-class com.acme.Foo").getSelectedClasses()), + () -> assertEquals(List.of(selectClass("com.acme.Foo")), type.parseArgLine("--select-class=com.acme.Foo").getSelectedClasses()), + () -> assertEquals(List.of(selectClass("com.acme.Foo"), selectClass("com.example.Bar")), type.parseArgLine("-c com.acme.Foo -c com.example.Bar").getSelectedClasses()) + ); + // @formatter:on } @Test @@ -455,17 +509,17 @@ void parseInvalidClassSelectors() { @EnumSource void parseValidMethodSelectors(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("-m com.acme.Foo#m()").getSelectedMethods()), - () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("--m com.acme.Foo#m()").getSelectedMethods()), - () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("-select-method com.acme.Foo#m()").getSelectedMethods()), - () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("-select-method=com.acme.Foo#m()").getSelectedMethods()), - () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("--select-method com.acme.Foo#m()").getSelectedMethods()), - () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("--select-method=com.acme.Foo#m()").getSelectedMethods()), - () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()"), selectMethod("com.example.Bar#method(java.lang.Object)")), - type.parseArgLine("-m com.acme.Foo#m() -m com.example.Bar#method(java.lang.Object)").getSelectedMethods()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("-m com.acme.Foo#m()").getSelectedMethods()), + () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("--m com.acme.Foo#m()").getSelectedMethods()), + () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("-select-method com.acme.Foo#m()").getSelectedMethods()), + () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("-select-method=com.acme.Foo#m()").getSelectedMethods()), + () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("--select-method com.acme.Foo#m()").getSelectedMethods()), + () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()")), type.parseArgLine("--select-method=com.acme.Foo#m()").getSelectedMethods()), + () -> assertEquals(List.of(selectMethod("com.acme.Foo#m()"), selectMethod("com.example.Bar#method(java.lang.Object)")), + type.parseArgLine("-m com.acme.Foo#m() -m com.example.Bar#method(java.lang.Object)").getSelectedMethods()) + ); + // @formatter:on } @Test @@ -477,16 +531,16 @@ void parseInvalidMethodSelectors() { @EnumSource void parseValidClasspathResourceSelectors(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("-r /foo.csv").getSelectedClasspathResources()), - () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("--r /foo.csv").getSelectedClasspathResources()), - () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("-select-resource /foo.csv").getSelectedClasspathResources()), - () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("-select-resource=/foo.csv").getSelectedClasspathResources()), - () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("--select-resource /foo.csv").getSelectedClasspathResources()), - () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("--select-resource=/foo.csv").getSelectedClasspathResources()), - () -> assertEquals(List.of(selectClasspathResource("/foo.csv"), selectClasspathResource("bar.json")), type.parseArgLine("-r /foo.csv -r bar.json").getSelectedClasspathResources()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("-r /foo.csv").getSelectedClasspathResources()), + () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("--r /foo.csv").getSelectedClasspathResources()), + () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("-select-resource /foo.csv").getSelectedClasspathResources()), + () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("-select-resource=/foo.csv").getSelectedClasspathResources()), + () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("--select-resource /foo.csv").getSelectedClasspathResources()), + () -> assertEquals(List.of(selectClasspathResource("/foo.csv")), type.parseArgLine("--select-resource=/foo.csv").getSelectedClasspathResources()), + () -> assertEquals(List.of(selectClasspathResource("/foo.csv"), selectClasspathResource("bar.json")), type.parseArgLine("-r /foo.csv -r bar.json").getSelectedClasspathResources()) + ); + // @formatter:on } @Test @@ -498,16 +552,16 @@ void parseInvalidClasspathResourceSelectors() { @EnumSource void parseValidIterationSelectors(ArgsType type) { // @formatter:off - assertAll( - () -> assertEquals(List.of(selectIteration(selectClasspathResource("/foo.csv"), 0)), type.parseArgLine("-i resource:/foo.csv[0]").getSelectedIterations()), - () -> assertEquals(List.of(selectIteration(selectMethod("com.acme.Foo#m()"), 1, 2)), type.parseArgLine("--i method:com.acme.Foo#m()[1..2]").getSelectedIterations()), - () -> assertEquals(List.of(selectIteration(selectClass("com.acme.Foo"), 0, 2)), type.parseArgLine("-select-iteration class:com.acme.Foo[0,2]").getSelectedIterations()), - () -> assertEquals(List.of(selectIteration(selectPackage("com.acme.foo"), 3)), type.parseArgLine("-select-iteration=package:com.acme.foo[3]").getSelectedIterations()), - () -> assertEquals(List.of(selectIteration(selectModule("com.acme.foo"), 0, 1, 2, 4, 5, 6)), type.parseArgLine("--select-iteration module:com.acme.foo[0..2,4..6]").getSelectedIterations()), - () -> assertEquals(List.of(selectIteration(selectDirectory("foo/bar"), 1, 5)), type.parseArgLine("--select-iteration=directory:foo/bar[1,5]").getSelectedIterations()), - () -> assertEquals(List.of(selectIteration(selectFile("foo.txt"), 6), selectIteration(selectUri("file:///foo.txt"), 7)), type.parseArgLine("-i file:foo.txt[6] -i uri:file:///foo.txt[7]").getSelectedIterations()) - ); - // @formatter:on + assertAll( + () -> assertEquals(List.of(selectIteration(selectClasspathResource("/foo.csv"), 0)), type.parseArgLine("-i resource:/foo.csv[0]").getSelectedIterations()), + () -> assertEquals(List.of(selectIteration(selectMethod("com.acme.Foo#m()"), 1, 2)), type.parseArgLine("--i method:com.acme.Foo#m()[1..2]").getSelectedIterations()), + () -> assertEquals(List.of(selectIteration(selectClass("com.acme.Foo"), 0, 2)), type.parseArgLine("-select-iteration class:com.acme.Foo[0,2]").getSelectedIterations()), + () -> assertEquals(List.of(selectIteration(selectPackage("com.acme.foo"), 3)), type.parseArgLine("-select-iteration=package:com.acme.foo[3]").getSelectedIterations()), + () -> assertEquals(List.of(selectIteration(selectModule("com.acme.foo"), 0, 1, 2, 4, 5, 6)), type.parseArgLine("--select-iteration module:com.acme.foo[0..2,4..6]").getSelectedIterations()), + () -> assertEquals(List.of(selectIteration(selectDirectory("foo/bar"), 1, 5)), type.parseArgLine("--select-iteration=directory:foo/bar[1,5]").getSelectedIterations()), + () -> assertEquals(List.of(selectIteration(selectFile("foo.txt"), 6), selectIteration(selectUri("file:///foo.txt"), 7)), type.parseArgLine("-i file:foo.txt[6] -i uri:file:///foo.txt[7]").getSelectedIterations()) + ); + // @formatter:on } @Test @@ -520,39 +574,39 @@ void parseInvalidIterationSelectors() { void parseClasspathScanningEntries(ArgsType type) { var dir = Paths.get("."); // @formatter:off - assertAll( - () -> assertTrue(type.parseArgLine("--scan-class-path").isScanClasspath()), - () -> assertEquals(List.of(), type.parseArgLine("--scan-class-path").getSelectedClasspathEntries()), - () -> assertTrue(type.parseArgLine("--scan-classpath").isScanClasspath()), - () -> assertEquals(List.of(), type.parseArgLine("--scan-classpath").getSelectedClasspathEntries()), - () -> assertTrue(type.parseArgLine("--scan-class-path .").isScanClasspath()), - () -> assertEquals(List.of(dir), type.parseArgLine("--scan-class-path .").getSelectedClasspathEntries()), - () -> assertEquals(List.of(dir), type.parseArgLine("--scan-class-path=.").getSelectedClasspathEntries()), - () -> assertEquals(List.of(dir), type.parseArgLine("-scan-class-path .").getSelectedClasspathEntries()), - () -> assertEquals(List.of(dir), type.parseArgLine("-scan-class-path=.").getSelectedClasspathEntries()), - () -> assertEquals(List.of(dir, Paths.get("src/test/java")), type.parseArgLine("--scan-class-path . --scan-class-path src/test/java").getSelectedClasspathEntries()), - () -> assertEquals(List.of(dir, Paths.get("src/test/java")), type.parseArgLine("--scan-class-path ." + File.pathSeparator + "src/test/java").getSelectedClasspathEntries()) - ); - // @formatter:on + assertAll( + () -> assertTrue(type.parseArgLine("--scan-class-path").isScanClasspath()), + () -> assertEquals(List.of(), type.parseArgLine("--scan-class-path").getSelectedClasspathEntries()), + () -> assertTrue(type.parseArgLine("--scan-classpath").isScanClasspath()), + () -> assertEquals(List.of(), type.parseArgLine("--scan-classpath").getSelectedClasspathEntries()), + () -> assertTrue(type.parseArgLine("--scan-class-path .").isScanClasspath()), + () -> assertEquals(List.of(dir), type.parseArgLine("--scan-class-path .").getSelectedClasspathEntries()), + () -> assertEquals(List.of(dir), type.parseArgLine("--scan-class-path=.").getSelectedClasspathEntries()), + () -> assertEquals(List.of(dir), type.parseArgLine("-scan-class-path .").getSelectedClasspathEntries()), + () -> assertEquals(List.of(dir), type.parseArgLine("-scan-class-path=.").getSelectedClasspathEntries()), + () -> assertEquals(List.of(dir, Paths.get("src/test/java")), type.parseArgLine("--scan-class-path . --scan-class-path src/test/java").getSelectedClasspathEntries()), + () -> assertEquals(List.of(dir, Paths.get("src/test/java")), type.parseArgLine("--scan-class-path ." + File.pathSeparator + "src/test/java").getSelectedClasspathEntries()) + ); + // @formatter:on } @ParameterizedTest @EnumSource void parseValidConfigurationParameters(ArgsType type) { // @formatter:off - assertAll( - () -> assertThat(type.parseArgLine("-config foo=bar").getConfigurationParameters()) - .containsOnly(entry("foo", "bar")), - () -> assertThat(type.parseArgLine("-config=foo=bar").getConfigurationParameters()) - .containsOnly(entry("foo", "bar")), - () -> assertThat(type.parseArgLine("--config foo=bar").getConfigurationParameters()) - .containsOnly(entry("foo", "bar")), - () -> assertThat(type.parseArgLine("--config=foo=bar").getConfigurationParameters()) - .containsOnly(entry("foo", "bar")), - () -> assertThat(type.parseArgLine("--config foo=bar --config baz=qux").getConfigurationParameters()) - .containsExactly(entry("foo", "bar"), entry("baz", "qux")) - ); - // @formatter:on + assertAll( + () -> assertThat(type.parseArgLine("-config foo=bar").getConfigurationParameters()) + .containsOnly(entry("foo", "bar")), + () -> assertThat(type.parseArgLine("-config=foo=bar").getConfigurationParameters()) + .containsOnly(entry("foo", "bar")), + () -> assertThat(type.parseArgLine("--config foo=bar").getConfigurationParameters()) + .containsOnly(entry("foo", "bar")), + () -> assertThat(type.parseArgLine("--config=foo=bar").getConfigurationParameters()) + .containsOnly(entry("foo", "bar")), + () -> assertThat(type.parseArgLine("--config foo=bar --config baz=qux").getConfigurationParameters()) + .containsExactly(entry("foo", "bar"), entry("baz", "qux")) + ); + // @formatter:on } @Test @@ -641,6 +695,7 @@ CommandLineOptions parseArgLine(String argLine) throws IOException { } } }; + abstract CommandLineOptions parseArgLine(String argLine) throws IOException; private static String[] split(String argLine) { diff --git a/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java b/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java index 3456eb8f2068..df0929acdb57 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/tasks/DiscoveryRequestCreatorTests.java @@ -64,10 +64,10 @@ void convertsScanClasspathOptionWithoutExplicitRootDirectories() { var classpathRootSelectors = request.getSelectorsByType(ClasspathRootSelector.class); // @formatter:off - assertThat(classpathRootSelectors).extracting(ClasspathRootSelector::getClasspathRoot) - .hasAtLeastOneElementOfType(URI.class) - .doesNotContainNull(); - // @formatter:on + assertThat(classpathRootSelectors).extracting(ClasspathRootSelector::getClasspathRoot) + .hasAtLeastOneElementOfType(URI.class) + .doesNotContainNull(); + // @formatter:on } @Test @@ -79,9 +79,9 @@ void convertsScanClasspathOptionWithExplicitRootDirectories() { var classpathRootSelectors = request.getSelectorsByType(ClasspathRootSelector.class); // @formatter:off - assertThat(classpathRootSelectors).extracting(ClasspathRootSelector::getClasspathRoot) - .containsExactly(new File(".").toURI(), new File("..").toURI()); - // @formatter:on + assertThat(classpathRootSelectors).extracting(ClasspathRootSelector::getClasspathRoot) + .containsExactly(new File(".").toURI(), new File("..").toURI()); + // @formatter:on } @Test @@ -93,9 +93,9 @@ void convertsScanClasspathOptionWithAdditionalClasspathEntries() { var classpathRootSelectors = request.getSelectorsByType(ClasspathRootSelector.class); // @formatter:off - assertThat(classpathRootSelectors).extracting(ClasspathRootSelector::getClasspathRoot) - .contains(new File(".").toURI(), new File("..").toURI()); - // @formatter:on + assertThat(classpathRootSelectors).extracting(ClasspathRootSelector::getClasspathRoot) + .contains(new File(".").toURI(), new File("..").toURI()); + // @formatter:on } @Test @@ -178,6 +178,23 @@ void convertsPackageOptions() { assertExcludes(packageNameFilters.get(1), "org.junit.excluded1"); } + /** + * @since 1.10 + */ + @Test + void convertsMethodNamePatternOptions() { + options.setScanClasspath(true); + options.setIncludedMethodNamePatterns(List.of(".+#foo.*Bar", ".+#toString", ".+#method.*")); + options.setExcludedMethodNamePatterns(List.of(".+#bar.*Foo")); + + var request = convert(); + var methodNameFilters = request.getPostDiscoveryFilters(); + + assertThat(methodNameFilters).hasSize(2); + assertThat(methodNameFilters.get(0).toString()).contains(".+#foo.*Bar", ".+#toString", ".+#method.*"); + assertThat(methodNameFilters.get(1).toString()).contains(".+#bar.*Foo"); + } + @Test void convertsTagOptions() { options.setScanClasspath(true); diff --git a/platform-tests/src/test/java/org/junit/platform/launcher/MethodFilterTests.java b/platform-tests/src/test/java/org/junit/platform/launcher/MethodFilterTests.java new file mode 100644 index 000000000000..a7553644315e --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/launcher/MethodFilterTests.java @@ -0,0 +1,166 @@ +/* + * Copyright 2015-2023 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 v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.launcher; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.platform.launcher.MethodFilter.excludeMethodNamePatterns; +import static org.junit.platform.launcher.MethodFilter.includeMethodNamePatterns; + +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.commons.util.ReflectionUtils; +import org.junit.platform.engine.FilterResult; +import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.UniqueId; +import org.junit.platform.engine.support.descriptor.DemoMethodTestDescriptor; + +/** + * Unit tests for {@link MethodFilter}. + * + * @since 1.10 + */ +class MethodFilterTests { + private static final TestDescriptor class1test1 = methodTestDescriptor("class1", Class1.class, "test1"); + private static final TestDescriptor class1test2 = methodTestDescriptor("class1", Class1.class, "test2"); + private static final TestDescriptor class2test1 = methodTestDescriptor("class2", Class2.class, "test1"); + private static final TestDescriptor class2test2 = methodTestDescriptor("class2", Class2.class, "test2"); + + @Test + void includeMethodNamePatternsChecksPreconditions() { + assertThatThrownBy(() -> includeMethodNamePatterns((String[]) null)) // + .isInstanceOf(PreconditionViolationException.class) // + .hasMessage("patterns array must not be null or empty"); + assertThatThrownBy(() -> includeMethodNamePatterns(new String[0])) // + .isInstanceOf(PreconditionViolationException.class) // + .hasMessage("patterns array must not be null or empty"); + assertThatThrownBy(() -> includeMethodNamePatterns(new String[] { null })) // + .isInstanceOf(PreconditionViolationException.class) // + .hasMessage("patterns array must not contain null elements"); + } + + @Test + void includeSingleMethodNamePattern() { + var regex = "Class1#test.*"; + var filter = includeMethodNamePatterns(regex); + + assertIncluded(filter.apply(class1test1), + "Method name [Class1#test1] matches included pattern: '" + regex + "'"); + assertIncluded(filter.apply(class1test2), + "Method name [Class1#test2] matches included pattern: '" + regex + "'"); + + assertExcluded(filter.apply(class2test1), + "Method name [Class2#test1] does not match any included pattern: '" + regex + "'"); + assertExcluded(filter.apply(class2test2), + "Method name [Class2#test2] does not match any included pattern: '" + regex + "'"); + } + + @Test + void includeMultipleMethodNamePatterns() { + var firstRegex = "Class1#test.*"; + var secondRegex = "Class.+#test1"; + var filter = includeMethodNamePatterns(firstRegex, secondRegex); + + assertIncluded(filter.apply(class1test1), + "Method name [Class1#test1] matches included pattern: '" + firstRegex + "'"); + assertIncluded(filter.apply(class1test2), + "Method name [Class1#test2] matches included pattern: '" + firstRegex + "'"); + assertIncluded(filter.apply(class2test1), + "Method name [Class2#test1] matches included pattern: '" + secondRegex + "'"); + + assertExcluded(filter.apply(class2test2), "Method name [Class2#test2] does not match any included pattern: '" + + firstRegex + "' OR '" + secondRegex + "'"); + } + + @Test + void excludeMethodNamePatternsChecksPreconditions() { + assertThatThrownBy(() -> excludeMethodNamePatterns((String[]) null)) // + .isInstanceOf(PreconditionViolationException.class) // + .hasMessage("patterns array must not be null or empty"); + assertThatThrownBy(() -> excludeMethodNamePatterns(new String[0])) // + .isInstanceOf(PreconditionViolationException.class) // + .hasMessage("patterns array must not be null or empty"); + assertThatThrownBy(() -> excludeMethodNamePatterns(new String[] { null })) // + .isInstanceOf(PreconditionViolationException.class) // + .hasMessage("patterns array must not contain null elements"); + } + + @Test + void excludeSingleMethodNamePattern() { + var regex = "Class1#test.*"; + var filter = excludeMethodNamePatterns(regex); + + assertExcluded(filter.apply(class1test1), + "Method name [Class1#test1] matches excluded pattern: '" + regex + "'"); + assertExcluded(filter.apply(class1test2), + "Method name [Class1#test2] matches excluded pattern: '" + regex + "'"); + + assertIncluded(filter.apply(class2test1), + "Method name [Class2#test1] does not match any excluded pattern: '" + regex + "'"); + assertIncluded(filter.apply(class2test2), + "Method name [Class2#test2] does not match any excluded pattern: '" + regex + "'"); + } + + @Test + void excludeMultipleMethodNamePatterns() { + var firstRegex = "Class1#test.*"; + var secondRegex = "Class.+#test1"; + var filter = excludeMethodNamePatterns(firstRegex, secondRegex); + + assertExcluded(filter.apply(class1test1), + "Method name [Class1#test1] matches excluded pattern: '" + firstRegex + "'"); + assertExcluded(filter.apply(class1test2), + "Method name [Class1#test2] matches excluded pattern: '" + firstRegex + "'"); + assertExcluded(filter.apply(class2test1), + "Method name [Class2#test1] matches excluded pattern: '" + secondRegex + "'"); + + assertIncluded(filter.apply(class2test2), "Method name [Class2#test2] does not match any excluded pattern: '" + + firstRegex + "' OR '" + secondRegex + "'"); + } + + private void assertIncluded(FilterResult filterResult, String expectedReason) { + assertTrue(filterResult.included()); + assertThat(filterResult.getReason()).isPresent().contains(expectedReason); + } + + private void assertExcluded(FilterResult filterResult, String expectedReason) { + assertTrue(filterResult.excluded()); + assertThat(filterResult.getReason()).isPresent().contains(expectedReason); + } + + private static TestDescriptor methodTestDescriptor(String uniqueId, Class testClass, String methodName) { + var method = ReflectionUtils.findMethod(testClass, methodName, new Class[0]).orElseThrow(); + return new DemoMethodTestDescriptor(UniqueId.root("method", uniqueId), testClass, method); + } + + // ------------------------------------------------------------------------- + + private static class Class1 { + @Test + void test1() { + } + + @Test + void test2() { + } + } + + private static class Class2 { + @Test + void test1() { + } + + @Test + void test2() { + } + } +}