Skip to content

Commit 8f433e6

Browse files
author
Artem Eroshenko
authored
add test filter module (via allure-framework#474)
1 parent dd16e83 commit 8f433e6

24 files changed

Lines changed: 1142 additions & 105 deletions

File tree

.github/workflows/labels-verify.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ jobs:
1010
steps:
1111
- uses: zwaldowski/match-label-action@v2
1212
with:
13-
allowed: 'type:bug, type:enhancement, type:improvement, type:dependencies, type:internal, type:invalid'
13+
allowed: 'type:bug, type:new feature, type:improvement, type:dependencies, type:internal, type:invalid'

allure-java-commons-test/src/main/java/io/qameta/allure/test/AllureFeatures.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,12 @@
222222
@Feature("Timeline")
223223
@interface Severity {
224224
}
225+
226+
@Documented
227+
@Inherited
228+
@Retention(RetentionPolicy.RUNTIME)
229+
@Target({ElementType.METHOD, ElementType.TYPE})
230+
@Feature("Filtration")
231+
@interface Filtration {
232+
}
225233
}

allure-junit-platform/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ dependencies {
77
api(project(":allure-java-commons"))
88
implementation("org.junit.jupiter:junit-jupiter-api")
99
implementation("org.junit.platform:junit-platform-launcher")
10+
implementation(project(":allure-test-filter"))
1011
testAnnotationProcessor("org.slf4j:slf4j-simple")
1112
testAnnotationProcessor(project(":allure-descriptions-javadoc"))
1213
testImplementation("io.github.glytching:junit-extensions")

allure-junit-platform/src/main/java/io/qameta/allure/junitplatform/AllureJunitPlatform.java

Lines changed: 5 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,10 @@ private void startTestCase(final TestIdentifier testIdentifier) {
359359
final String uuid = tests.getOrCreate(testIdentifier);
360360

361361
final Optional<TestSource> testSource = testIdentifier.getSource();
362-
final Optional<Method> testMethod = testSource.flatMap(this::getTestMethod);
363-
final Optional<Class<?>> testClass = testSource.flatMap(this::getTestClass);
362+
final Optional<Method> testMethod = testSource
363+
.flatMap(AllureJunitPlatformUtils::getTestMethod);
364+
final Optional<Class<?>> testClass = testSource
365+
.flatMap(AllureJunitPlatformUtils::getTestClass);
364366

365367
final TestResult result = new TestResult()
366368
.setUuid(uuid)
@@ -384,7 +386,7 @@ private void startTestCase(final TestIdentifier testIdentifier) {
384386
createLanguageLabel("java")
385387
));
386388

387-
testSource.flatMap(this::getFullName).ifPresent(result::setFullName);
389+
testSource.flatMap(AllureJunitPlatformUtils::getFullName).ifPresent(result::setFullName);
388390
testSource.map(this::getSourceLabels).ifPresent(result.getLabels()::addAll);
389391
testClass.ifPresent(aClass -> {
390392
final String suiteName = getDisplayName(aClass).orElse(aClass.getCanonicalName());
@@ -508,56 +510,6 @@ private List<Label> getSourceLabels(final TestSource source) {
508510
return Collections.emptyList();
509511
}
510512

511-
private Optional<String> getFullName(final TestSource source) {
512-
if (source instanceof MethodSource) {
513-
final MethodSource ms = (MethodSource) source;
514-
return Optional.of(String.format("%s.%s", ms.getClassName(), ms.getMethodName()));
515-
}
516-
if (source instanceof ClassSource) {
517-
final ClassSource cs = (ClassSource) source;
518-
return Optional.ofNullable(cs.getClassName());
519-
}
520-
return Optional.empty();
521-
}
522-
523-
private Optional<Class<?>> getTestClass(final TestSource source) {
524-
if (source instanceof MethodSource) {
525-
return getTestClass(((MethodSource) source).getClassName());
526-
}
527-
if (source instanceof ClassSource) {
528-
return Optional.of(((ClassSource) source).getJavaClass());
529-
}
530-
return Optional.empty();
531-
}
532-
533-
private Optional<Class<?>> getTestClass(final String className) {
534-
try {
535-
return Optional.of(Class.forName(className));
536-
} catch (ClassNotFoundException e) {
537-
LOGGER.trace("Could not get test class from test source {}", className, e);
538-
}
539-
return Optional.empty();
540-
}
541-
542-
private Optional<Method> getTestMethod(final TestSource source) {
543-
if (source instanceof MethodSource) {
544-
return getTestMethod((MethodSource) source);
545-
}
546-
return Optional.empty();
547-
}
548-
549-
private Optional<Method> getTestMethod(final MethodSource source) {
550-
try {
551-
final Class<?> aClass = Class.forName(source.getClassName());
552-
return Stream.of(aClass.getDeclaredMethods())
553-
.filter(method -> MethodSource.from(method).equals(source))
554-
.findAny();
555-
} catch (ClassNotFoundException e) {
556-
LOGGER.trace("Could not get test method from method source {}", source, e);
557-
}
558-
return Optional.empty();
559-
}
560-
561513
private static class Uuids {
562514

563515
private final Map<TestIdentifier, String> storage = new ConcurrentHashMap<>();
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2020 Qameta Software OÜ
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.qameta.allure.junitplatform;
17+
18+
import org.junit.platform.engine.TestSource;
19+
import org.junit.platform.engine.support.descriptor.ClassSource;
20+
import org.junit.platform.engine.support.descriptor.MethodSource;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
24+
import java.lang.reflect.Method;
25+
import java.util.Optional;
26+
import java.util.stream.Stream;
27+
28+
/**
29+
* @author charlie (Dmitry Baev).
30+
*/
31+
/* package-private */ final class AllureJunitPlatformUtils {
32+
33+
private static final Logger LOGGER = LoggerFactory.getLogger(AllureJunitPlatformUtils.class);
34+
35+
private AllureJunitPlatformUtils() {
36+
throw new IllegalStateException("do not instance");
37+
}
38+
39+
public static Optional<String> getFullName(final TestSource source) {
40+
if (source instanceof MethodSource) {
41+
final MethodSource ms = (MethodSource) source;
42+
return Optional.of(String.format("%s.%s", ms.getClassName(), ms.getMethodName()));
43+
}
44+
if (source instanceof ClassSource) {
45+
final ClassSource cs = (ClassSource) source;
46+
return Optional.ofNullable(cs.getClassName());
47+
}
48+
return Optional.empty();
49+
}
50+
51+
public static Optional<Class<?>> getTestClass(final TestSource source) {
52+
if (source instanceof MethodSource) {
53+
return getTestClass(((MethodSource) source).getClassName());
54+
}
55+
if (source instanceof ClassSource) {
56+
return Optional.of(((ClassSource) source).getJavaClass());
57+
}
58+
return Optional.empty();
59+
}
60+
61+
public static Optional<Class<?>> getTestClass(final String className) {
62+
try {
63+
return Optional.of(Class.forName(className));
64+
} catch (ClassNotFoundException e) {
65+
LOGGER.trace("Could not get test class from test source {}", className, e);
66+
}
67+
return Optional.empty();
68+
}
69+
70+
public static Optional<Method> getTestMethod(final TestSource source) {
71+
if (source instanceof MethodSource) {
72+
return getTestMethod((MethodSource) source);
73+
}
74+
return Optional.empty();
75+
}
76+
77+
public static Optional<Method> getTestMethod(final MethodSource source) {
78+
try {
79+
final Class<?> aClass = Class.forName(source.getClassName());
80+
return Stream.of(aClass.getDeclaredMethods())
81+
.filter(method -> MethodSource.from(method).equals(source))
82+
.findAny();
83+
} catch (ClassNotFoundException e) {
84+
LOGGER.trace("Could not get test method from method source {}", source, e);
85+
}
86+
return Optional.empty();
87+
}
88+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2020 Qameta Software OÜ
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.qameta.allure.junitplatform;
17+
18+
import io.qameta.allure.model.Label;
19+
import io.qameta.allure.testfilter.FileTestPlanSupplier;
20+
import io.qameta.allure.testfilter.TestPlan;
21+
import io.qameta.allure.testfilter.TestPlanV1_0;
22+
import io.qameta.allure.util.AnnotationUtils;
23+
import org.junit.platform.engine.FilterResult;
24+
import org.junit.platform.engine.TestDescriptor;
25+
import org.junit.platform.engine.TestTag;
26+
import org.junit.platform.launcher.PostDiscoveryFilter;
27+
28+
import java.lang.reflect.Method;
29+
import java.util.Collection;
30+
import java.util.Objects;
31+
import java.util.Optional;
32+
import java.util.regex.Matcher;
33+
import java.util.regex.Pattern;
34+
35+
import static io.qameta.allure.util.ResultsUtils.ALLURE_ID_LABEL_NAME;
36+
37+
/**
38+
* @author charlie (Dmitry Baev).
39+
*/
40+
public class AllurePostDiscoveryFilter implements PostDiscoveryFilter {
41+
42+
private static final Pattern ID_TAG = Pattern.compile("^@?allure\\.id[:=](?<id>.+)$");
43+
44+
private final TestPlan testPlan;
45+
46+
public AllurePostDiscoveryFilter() {
47+
this(new FileTestPlanSupplier().supply().orElse(null));
48+
}
49+
50+
public AllurePostDiscoveryFilter(final TestPlan testPlan) {
51+
this.testPlan = testPlan;
52+
}
53+
54+
@Override
55+
public FilterResult apply(final TestDescriptor object) {
56+
if (Objects.isNull(testPlan)) {
57+
return FilterResult.included("test plan is empty");
58+
}
59+
if (!object.isTest()) {
60+
return FilterResult.included("filter only applied for tests");
61+
}
62+
63+
final String allureId = findAllureId(object);
64+
final String uniqueId = object.getUniqueId().toString();
65+
final String fullName = object.getSource()
66+
.flatMap(AllureJunitPlatformUtils::getFullName)
67+
.orElse(null);
68+
69+
return FilterResult.includedIf(
70+
isIncluded(testPlan, allureId, uniqueId, fullName)
71+
);
72+
}
73+
74+
private boolean isIncluded(final TestPlan testPlan,
75+
final String allureId,
76+
final String uniqueId,
77+
final String fullName) {
78+
if (testPlan instanceof TestPlanV1_0) {
79+
final TestPlanV1_0 tp = (TestPlanV1_0) testPlan;
80+
return Objects.isNull(tp.getTests()) || tp.getTests()
81+
.stream()
82+
.filter(Objects::nonNull)
83+
.anyMatch(tc -> match(tc, allureId, uniqueId, fullName));
84+
}
85+
return true;
86+
}
87+
88+
@SuppressWarnings("BooleanExpressionComplexity")
89+
private boolean match(final TestPlanV1_0.TestCase tc,
90+
final String allureId,
91+
final String uniqueId,
92+
final String fullName) {
93+
return Objects.nonNull(tc.getId()) && tc.getId().equals(allureId)
94+
|| Objects.nonNull(tc.getSelector()) && tc.getSelector().equals(uniqueId)
95+
|| Objects.nonNull(tc.getSelector()) && tc.getSelector().equals(fullName);
96+
}
97+
98+
private String findAllureId(final TestDescriptor object) {
99+
return object.getSource()
100+
.flatMap(AllureJunitPlatformUtils::getTestMethod)
101+
.flatMap(this::findAllureId)
102+
.orElseGet(() -> findAllureId(object.getTags()));
103+
}
104+
105+
private String findAllureId(final Collection<TestTag> tags) {
106+
return tags.stream()
107+
.map(TestTag::getName)
108+
.map(t -> {
109+
final Matcher matcher = ID_TAG.matcher(t);
110+
return matcher.matches()
111+
? Optional.ofNullable(matcher.group("id"))
112+
: Optional.<String>empty();
113+
})
114+
.filter(Optional::isPresent)
115+
.map(Optional::get)
116+
.filter(Objects::nonNull)
117+
.findAny()
118+
.orElse(null);
119+
}
120+
121+
private Optional<String> findAllureId(final Method value) {
122+
return AnnotationUtils.getLabels(value)
123+
.stream()
124+
.filter(l -> ALLURE_ID_LABEL_NAME.equals(l.getName()))
125+
.map(Label::getValue)
126+
.findAny();
127+
}
128+
129+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.qameta.allure.junitplatform.AllurePostDiscoveryFilter

0 commit comments

Comments
 (0)