Skip to content

Commit

Permalink
Fix test filter greediness for method names (#93)
Browse files Browse the repository at this point in the history
The present change aims at preventing the `MyTest#testSomething` pattern
from matching the `testSomethingElse` method in the following example:
```java
public class MyTest {
  @test
  void testSomething() {
  }

  @test
  void testSomethingElse() {
  }
```

With `asPredicate`, the test filter matches test methods _starting with_
the given method name:
> Creates a predicate that tests if this pattern is found in a given
> input string.

Unless the pattern doesn't encompass a method name, appending a `$` to
the input pattern makes sure only exact method names are considered.

Note: think about filter greediness for class names.
  • Loading branch information
rdesgroppes authored Oct 27, 2022
1 parent d315d8f commit 06311c6
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.platform.engine.FilterResult;
import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.engine.TestSource;
Expand Down Expand Up @@ -59,10 +61,19 @@ public FilterResult apply(TestDescriptor object) {
return FilterResult.excluded("Did not match " + rawPattern);
}

/**
* Converts comma-separated selections in patterns like:
*
* <ul>
* <li>classes: "path.to.SomeTest,path.to.AnotherTest" -> "path.to.SomeTest|path.to.AnotherTest"
* <li>methods: "path.to.SomeTest#testSomething,testSomethingElse" ->
* "path.to.SomeTest#testSomething$|path.to.SomeTest#testSomethingElse$"
* </ul>
*/
private static String convertCommaSeparatedSelections(String pattern) {
var selections = pattern.split(",");
if (selections.length == 1) {
return pattern;
return ensureExactMethodName(pattern);
}
var precedingClassSelection = selections[0];
var precedingHashIndex = precedingClassSelection.indexOf('#');
Expand All @@ -76,6 +87,13 @@ private static String convertCommaSeparatedSelections(String pattern) {
selections[i] = precedingClassSelection.substring(0, precedingHashIndex + 1) + selection;
}
}
return String.join("|", selections);
return Stream.of(selections)
.map(PatternFilter::ensureExactMethodName)
.collect(Collectors.joining("|"));
}

/** Appends '$' to patterns like "class#method" or "#method", unless already done. */
private static String ensureExactMethodName(String pattern) {
return pattern.matches(".*#.*[^$]$") ? pattern + '$' : pattern;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public void shouldIncludeATestMethodIfTheFilterIsJustTheClassName() {
assertTrue(siblingTestResult.included());

FilterResult nestedTestResult = filter.apply(nestedTestMethodTestDescriptor);
assertFalse(nestedTestResult.included(), "nested class should not be matched");
assertFalse(nestedTestResult.included(), "method in nested class should not be matched");
}

@Test
Expand All @@ -143,10 +143,10 @@ public void shouldIncludeANestedTestMethodIfTheFilterIsJustTheNestedClassName()
new PatternFilter(JUnit5StyleTest.NestedTest.class.getName().replace("$", "\\$") + "#");

FilterResult testResult = filter.apply(testMethodTestDescriptor);
assertFalse(testResult.included(), "enclosing class should not be matched");
assertFalse(testResult.included(), "method in enclosing class should not be matched");

FilterResult siblingTestResult = filter.apply(siblingTestMethodTestDescriptor);
assertFalse(siblingTestResult.included(), "enclosing class should not be matched");
assertFalse(siblingTestResult.included(), "method in enclosing class should not be matched");

FilterResult nestedTestResult = filter.apply(nestedTestMethodTestDescriptor);
assertTrue(nestedTestResult.included());
Expand Down Expand Up @@ -186,11 +186,25 @@ public void shouldNotIncludeATestMethodIfTheFilterDoesNotMatchTheMethodName() {
}

@Test
public void shouldIncludeATestMethodIfTheFilterMatchesTheMethodName() {
public void shouldIncludeATestMethodIfTheFilterMatchesTheExactShortMethodName() {
PatternFilter filter = new PatternFilter("#alwaysPasses");

FilterResult testResult = filter.apply(testMethodTestDescriptor);
assertTrue(testResult.included());

FilterResult siblingTestResult = filter.apply(siblingTestMethodTestDescriptor);
assertFalse(siblingTestResult.included(), "longer method name should not be matched");

FilterResult nestedTestResult = filter.apply(nestedTestMethodTestDescriptor);
assertFalse(nestedTestResult.included(), "longer method name should not be matched");
}

@Test
public void shouldIncludeATestMethodIfTheFilterMatchesTheExactLongMethodName() {
PatternFilter filter = new PatternFilter("#alwaysPassesToo");

FilterResult testResult = filter.apply(testMethodTestDescriptor);
assertFalse(testResult.included(), "different method name should not be matched");
assertFalse(testResult.included(), "shorter method name should not be matched");

FilterResult siblingTestResult = filter.apply(siblingTestMethodTestDescriptor);
assertTrue(siblingTestResult.included());
Expand Down

0 comments on commit 06311c6

Please sign in to comment.