From 3214f05f148a3b6779bd2f646890ffb7ac3471c3 Mon Sep 17 00:00:00 2001 From: Yongjun Hong Date: Tue, 12 Nov 2024 23:15:47 +0900 Subject: [PATCH] Add include matching to ClassNamePatternFilterUtils (#4115) Issue: #3717 --------- Signed-off-by: yongjunhong Co-authored-by: Marc Philipp --- .../org/junit/jupiter/engine/Constants.java | 2 +- .../util/ClassNamePatternFilterUtils.java | 53 ++++-- .../platform/launcher/LauncherConstants.java | 2 +- .../ClassNamePatternFilterUtilsTests.java | 151 ++++++++++++++++++ 4 files changed, 195 insertions(+), 13 deletions(-) diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java index 92c6990cb7ed..500f55818923 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java @@ -91,7 +91,7 @@ public final class Constants { * @see #DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME * @see org.junit.jupiter.api.extension.ExecutionCondition */ - public static final String DEACTIVATE_ALL_CONDITIONS_PATTERN = ClassNamePatternFilterUtils.DEACTIVATE_ALL_PATTERN; + public static final String DEACTIVATE_ALL_CONDITIONS_PATTERN = ClassNamePatternFilterUtils.ALL_PATTERN; /** * Property name used to set the default display name generator class name: {@value} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassNamePatternFilterUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassNamePatternFilterUtils.java index 6c713d8c6cb4..f9ec3d257baa 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassNamePatternFilterUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassNamePatternFilterUtils.java @@ -41,7 +41,7 @@ private ClassNamePatternFilterUtils() { /* no-op */ } - public static final String DEACTIVATE_ALL_PATTERN = "*"; + public static final String ALL_PATTERN = "*"; /** * Create a {@link Predicate} that can be used to exclude (i.e., filter out) @@ -51,7 +51,7 @@ private ClassNamePatternFilterUtils() { * @param patterns a comma-separated list of patterns */ public static Predicate excludeMatchingClasses(String patterns) { - return excludeMatchingClasses(patterns, object -> object.getClass().getName()); + return matchingClasses(patterns, object -> object.getClass().getName(), FilterType.EXCLUDE); } /** @@ -61,26 +61,57 @@ public static Predicate excludeMatchingClasses(String patterns) { * @param patterns a comma-separated list of patterns */ public static Predicate excludeMatchingClassNames(String patterns) { - return excludeMatchingClasses(patterns, Function.identity()); + return matchingClasses(patterns, Function.identity(), FilterType.EXCLUDE); } - private static Predicate excludeMatchingClasses(String patterns, Function classNameGetter) { + /** + * Create a {@link Predicate} that can be used to include (i.e., filter in) + * objects of type {@code T} whose fully qualified class names match any of + * the supplied patterns. + * + * @param patterns a comma-separated list of patterns + */ + public static Predicate includeMatchingClasses(String patterns) { + return matchingClasses(patterns, object -> object.getClass().getName(), FilterType.INCLUDE); + } + + /** + * Create a {@link Predicate} that can be used to include (i.e., filter in) + * fully qualified class names matching any of the supplied patterns. + * + * @param patterns a comma-separated list of patterns + */ + public static Predicate includeMatchingClassNames(String patterns) { + return matchingClasses(patterns, Function.identity(), FilterType.INCLUDE); + } + + private enum FilterType { + INCLUDE, EXCLUDE + } + + private static Predicate matchingClasses(String patterns, Function classNameProvider, + FilterType type) { // @formatter:off return Optional.ofNullable(patterns) .filter(StringUtils::isNotBlank) .map(String::trim) - .map(trimmedPatterns -> createPredicateFromPatterns(trimmedPatterns, classNameGetter)) - .orElse(object -> true); + .map(trimmedPatterns -> createPredicateFromPatterns(trimmedPatterns, classNameProvider, type)) + .orElse(type == FilterType.EXCLUDE ? __ -> true : __ -> false); // @formatter:on } - private static Predicate createPredicateFromPatterns(String patterns, - Function classNameProvider) { - if (DEACTIVATE_ALL_PATTERN.equals(patterns)) { - return object -> false; + private static Predicate createPredicateFromPatterns(String patterns, Function classNameProvider, + FilterType mode) { + if (ALL_PATTERN.equals(patterns)) { + return __ -> mode == FilterType.INCLUDE; } + List patternList = convertToRegularExpressions(patterns); - return object -> patternList.stream().noneMatch(it -> it.matcher(classNameProvider.apply(object)).matches()); + return object -> { + boolean isMatchingAnyPattern = patternList.stream().anyMatch( + pattern -> pattern.matcher(classNameProvider.apply(object)).matches()); + return (mode == FilterType.INCLUDE) == isMatchingAnyPattern; + }; } private static List convertToRegularExpressions(String patterns) { diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java index 5cb30bf8e69d..3a7aa079aed1 100644 --- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java +++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java @@ -146,7 +146,7 @@ public class LauncherConstants { * @see #DEACTIVATE_LISTENERS_PATTERN_PROPERTY_NAME * @see org.junit.platform.launcher.TestExecutionListener */ - public static final String DEACTIVATE_ALL_LISTENERS_PATTERN = ClassNamePatternFilterUtils.DEACTIVATE_ALL_PATTERN; + public static final String DEACTIVATE_ALL_LISTENERS_PATTERN = ClassNamePatternFilterUtils.ALL_PATTERN; /** * Property name used to enable support for diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ClassNamePatternFilterUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ClassNamePatternFilterUtilsTests.java index 1d68086db38b..a76f67a31026 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ClassNamePatternFilterUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ClassNamePatternFilterUtilsTests.java @@ -169,4 +169,155 @@ void alwaysExcludedClassName(String pattern) { .isEmpty(); } + //@formatter:off + @ValueSource(strings = { + "org.junit.jupiter.*", + "org.junit.platform.*.NonExistentClass", + "*.NonExistentClass*", + "*NonExistentClass*", + "AExecutionConditionClass, BExecutionConditionClass" + }) + //@formatter:on + @ParameterizedTest + void neverIncludedConditions(String pattern) { + List executionConditions = List.of(new AExecutionConditionClass(), + new BExecutionConditionClass()); + assertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) // + .isEmpty(); + } + + //@formatter:off + @ValueSource(strings = { + "org.junit.platform.*", + "*.platform.*", + "*", + "*AExecutionConditionClass, *BExecutionConditionClass", + "*ExecutionConditionClass" + }) + //@formatter:on + @ParameterizedTest + void alwaysIncludedConditions(String pattern) { + List executionConditions = List.of(new AExecutionConditionClass(), + new BExecutionConditionClass()); + assertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) // + .hasSize(2); + } + + //@formatter:off + @ValueSource(strings = { + "org.junit.jupiter.*", + "org.junit.platform.*.NonExistentClass", + "*.NonExistentClass*", + "*NonExistentClass*", + "ATestExecutionListenerClass, BTestExecutionListenerClass" + }) + //@formatter:on + @ParameterizedTest + void neverIncludedListeners(String pattern) { + List executionConditions = List.of(new ATestExecutionListenerClass(), + new BTestExecutionListenerClass()); + assertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) // + .isEmpty(); + } + + //@formatter:off + @ValueSource(strings = { + "org.junit.platform.*", + "*.platform.*", + "*", + "*ATestExecutionListenerClass, *BTestExecutionListenerClass", + "*TestExecutionListenerClass" + }) + //@formatter:on + @ParameterizedTest + void alwaysIncludedListeners(String pattern) { + List executionConditions = List.of(new ATestExecutionListenerClass(), + new BTestExecutionListenerClass()); + assertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) // + .hasSize(2); + } + + //@formatter:off + @ValueSource(strings = { + "org.junit.jupiter.*", + "org.junit.platform.*.NonExistentClass", + "*.NonExistentClass*", + "*NonExistentClass*", + "AVanillaEmpty, BVanillaEmpty" + }) + //@formatter:on + @ParameterizedTest + void neverIncludedClass(String pattern) { + var executionConditions = List.of(new AVanillaEmpty(), new BVanillaEmpty()); + assertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) // + .isEmpty(); + } + + //@formatter:off + @ValueSource(strings = { + "org.junit.platform.*", + "*.platform.*", + "*", + "*AVanillaEmpty, *BVanillaEmpty", + "*VanillaEmpty" + }) + //@formatter:on + @ParameterizedTest + void alwaysIncludedClass(String pattern) { + var executionConditions = List.of(new AVanillaEmpty(), new BVanillaEmpty()); + assertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) // + .hasSize(2); + } + + //@formatter:off + @ValueSource(strings = { + "org.junit.jupiter.*", + "org.junit.platform.*.NonExistentClass", + "*.NonExistentClass*", + "*NonExistentClass*", + "AVanillaEmpty, BVanillaEmpty" + }) + //@formatter:on + @ParameterizedTest + void neverIncludedClassName(String pattern) { + var executionConditions = List.of("org.junit.platform.commons.util.classes.AVanillaEmpty", + "org.junit.platform.commons.util.classes.BVanillaEmpty"); + assertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClassNames(pattern)) // + .isEmpty(); + } + + //@formatter:off + @ValueSource(strings = { + "org.junit.platform.*", + "*.platform.*", + "*", + "*AVanillaEmpty, *BVanillaEmpty", + "*VanillaEmpty" + }) + //@formatter:on + @ParameterizedTest + void alwaysIncludedClassName(String pattern) { + var executionConditions = List.of("org.junit.platform.commons.util.classes.AVanillaEmpty", + "org.junit.platform.commons.util.classes.BVanillaEmpty"); + assertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClassNames(pattern)) // + .hasSize(2); + } + + //@formatter:off + @ValueSource(strings = { + "org.junit.platform.*", + "*.platform.*", + "*", + "*AVanillaEmpty, *BVanillaEmpty", + "*VanillaEmpty" + }) + //@formatter:on + @ParameterizedTest + void includeAndExcludeSame(String pattern) { + var executionConditions = List.of("org.junit.platform.commons.util.classes.AVanillaEmpty", + "org.junit.platform.commons.util.classes.BVanillaEmpty"); + assertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClassNames(pattern)) // + .hasSize(2); + } + }