Skip to content

Commit 63c5129

Browse files
RenatIsinArtem Eroshenko
authored andcommitted
add filter for junit4 and testng
1 parent a143bfe commit 63c5129

12 files changed

Lines changed: 750 additions & 5 deletions

File tree

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-junit4/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ val junitVersion = "4.12"
77
dependencies {
88
agent("org.aspectj:aspectjweaver")
99
api(project(":allure-java-commons"))
10+
implementation("org.aspectj:aspectjrt")
1011
implementation("junit:junit:$junitVersion")
1112
testImplementation("org.assertj:assertj-core")
1213
testImplementation("org.junit.jupiter:junit-jupiter-api")
1314
testImplementation("org.mockito:mockito-core")
1415
testImplementation("org.slf4j:slf4j-simple")
1516
testImplementation(project(":allure-java-commons-test"))
1617
testImplementation(project(":allure-junit-platform"))
18+
implementation(project(":allure-test-filter"))
1719
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
1820
}
1921

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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.junit4;
17+
18+
import io.qameta.allure.AllureId;
19+
import io.qameta.allure.testfilter.TestPlan;
20+
import io.qameta.allure.testfilter.TestPlanV1_0;
21+
22+
import java.util.Objects;
23+
import java.util.Optional;
24+
import java.util.stream.Collectors;
25+
26+
import org.junit.Ignore;
27+
import org.junit.runner.Description;
28+
import org.junit.runner.manipulation.Filter;
29+
30+
/**
31+
* @author eroshenkoam (Artem Eroshenko).
32+
*/
33+
public class AllureFilter extends Filter {
34+
35+
private final TestPlan testPlan;
36+
37+
public AllureFilter(final TestPlan testPlan) {
38+
this.testPlan = testPlan;
39+
}
40+
41+
@Override
42+
public String describe() {
43+
if (testPlan instanceof TestPlanV1_0) {
44+
return "include ids: " + ((TestPlanV1_0) testPlan).getTests().stream()
45+
.map(TestPlanV1_0.TestCase::getId).collect(Collectors.joining(" "));
46+
}
47+
return "Unknown test plan version";
48+
}
49+
50+
@Override
51+
public boolean shouldRun(final Description description) {
52+
if (Objects.isNull(description.getMethodName())) {
53+
return Objects.isNull(description.getAnnotation(Ignore.class));
54+
}
55+
if (testPlan instanceof TestPlanV1_0) {
56+
final String selector = getSelector(description.getClassName(), description.getMethodName());
57+
58+
final String allureId = Optional
59+
.ofNullable(description.getAnnotation(AllureId.class))
60+
.map(AllureId::value).orElse(null);
61+
62+
return ((TestPlanV1_0) testPlan).isSelected(allureId, selector);
63+
} else {
64+
return true;
65+
}
66+
}
67+
68+
private String getSelector(final String className, final String methodName) {
69+
return String.format("%s.%s",
70+
className,
71+
methodName);
72+
}
73+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
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.junit4;
17+
18+
import io.qameta.allure.testfilter.FileTestPlanSupplier;
19+
import io.qameta.allure.testfilter.TestPlan;
20+
21+
import java.io.File;
22+
import java.io.IOException;
23+
import java.lang.reflect.Modifier;
24+
import java.nio.file.Files;
25+
import java.nio.file.Path;
26+
import java.nio.file.Paths;
27+
import java.util.ArrayList;
28+
import java.util.Arrays;
29+
import java.util.List;
30+
import java.util.Optional;
31+
import java.util.stream.Collectors;
32+
33+
import org.junit.Test;
34+
import org.junit.runner.manipulation.Filter;
35+
import org.junit.runner.manipulation.NoTestsRemainException;
36+
import org.junit.runners.Suite;
37+
import org.junit.runners.model.InitializationError;
38+
import org.junit.runners.model.RunnerBuilder;
39+
40+
/**
41+
* @author eroshenkoam (Artem Eroshenko).
42+
*/
43+
public class AllureSuite extends Suite {
44+
45+
private static final char UNIX_SEPARATOR = '/';
46+
private static final char DOT_SYMBOL = '.';
47+
48+
private static final String CLASS_SUFFIX = ".class";
49+
private static final String FALLBACK_CLASSPATH_PROPERTY = "java.class.path";
50+
51+
public AllureSuite(final Class<?> klass, final RunnerBuilder builder) throws InitializationError {
52+
this(klass, builder, new FileTestPlanSupplier().supply());
53+
}
54+
55+
public AllureSuite(final Class<?> klass,
56+
final RunnerBuilder builder,
57+
final Optional<TestPlan> testPlan) throws InitializationError {
58+
super(builder, klass, findAllTestClasses());
59+
if (testPlan.isPresent()) {
60+
final Filter filter = new AllureFilter(testPlan.get());
61+
try {
62+
filter(filter);
63+
} catch (NoTestsRemainException e) {
64+
throw new InitializationError(e);
65+
}
66+
}
67+
}
68+
69+
private static Class<?>[] findAllTestClasses() {
70+
final List<Path> classRoots = splitClassPath(getClasspath()).stream()
71+
.map(Paths::get)
72+
.filter(AllureSuite::isNotJar)
73+
.collect(Collectors.toList());
74+
final List<String> classFiles = new ArrayList<>();
75+
76+
for (final Path classRoot : classRoots) {
77+
classFiles.addAll(getRelativeClassFiles(classRoot));
78+
}
79+
80+
final List<String> classNames = classFiles.stream()
81+
.map(AllureSuite::classNameFromFile)
82+
.collect(Collectors.toList());
83+
84+
final List<Class<?>> classes = classNames.stream()
85+
.map(AllureSuite::readClass)
86+
.filter(AllureSuite::isTestClass)
87+
.collect(Collectors.toList());
88+
89+
return classes.toArray(new Class[]{});
90+
}
91+
92+
private static List<String> getRelativeClassFiles(final Path root) {
93+
try {
94+
return Files.walk(root)
95+
.filter(Files::isRegularFile)
96+
.filter(Files::isReadable)
97+
.filter(AllureSuite::isNotInnerClass)
98+
.filter(AllureSuite::isClassFile)
99+
.map(root::relativize)
100+
.map(Path::toString)
101+
.collect(Collectors.toList());
102+
} catch (IOException e) {
103+
return new ArrayList<>();
104+
}
105+
}
106+
107+
private static String classNameFromFile(final String classFileName) {
108+
final String result = replaceFileSeparators(cutOffExtension(classFileName));
109+
return result.charAt(0) == '.' ? result.substring(1) : result;
110+
}
111+
112+
private static boolean isTestClass(final Class<?> clazz) {
113+
if (isAbstractClass(clazz)) {
114+
return false;
115+
}
116+
return hasInheritanceTestMethods(clazz);
117+
}
118+
119+
private static boolean hasInheritanceTestMethods(final Class<?> clazz) {
120+
Class<?> possibleClass = clazz;
121+
while (possibleClass != null) {
122+
if (hasTestMethods(possibleClass)) {
123+
return true;
124+
}
125+
possibleClass = possibleClass.getSuperclass();
126+
}
127+
return false;
128+
}
129+
130+
private static boolean hasTestMethods(final Class<?> clazz) {
131+
return Arrays.stream(clazz.getMethods())
132+
.anyMatch(method -> method.isAnnotationPresent(Test.class));
133+
}
134+
135+
private static boolean isAbstractClass(final Class<?> clazz) {
136+
return (clazz.getModifiers() & Modifier.ABSTRACT) != 0;
137+
}
138+
139+
private static Class<?> readClass(final String className) {
140+
try {
141+
return Class.forName(className);
142+
} catch (ClassNotFoundException e) {
143+
return null;
144+
}
145+
}
146+
147+
private static boolean isNotInnerClass(final Path classFilePath) {
148+
return !classFilePath.getFileName().toString().contains("$");
149+
}
150+
151+
private static boolean isClassFile(final Path classFilePath) {
152+
return classFilePath.getFileName().toString().endsWith(CLASS_SUFFIX);
153+
}
154+
155+
private static boolean isNotJar(final Path classRoot) {
156+
final String rootName = classRoot.getFileName().toString();
157+
return !rootName.endsWith(".jar") && !rootName.endsWith(".JAR");
158+
}
159+
160+
private static String getClasspath() {
161+
return System.getProperty(FALLBACK_CLASSPATH_PROPERTY);
162+
}
163+
164+
private static List<String> splitClassPath(final String classPath) {
165+
final String separator = System.getProperty("path.separator");
166+
return Arrays.asList(classPath.split(separator));
167+
}
168+
169+
private static String replaceFileSeparators(final String s) {
170+
String result = s.replace(File.separatorChar, DOT_SYMBOL);
171+
if (File.separatorChar != UNIX_SEPARATOR) {
172+
result = result.replace(UNIX_SEPARATOR, DOT_SYMBOL);
173+
}
174+
return result;
175+
}
176+
177+
private static String cutOffExtension(final String classFileName) {
178+
return classFileName.substring(0, classFileName.length() - CLASS_SUFFIX.length());
179+
}
180+
181+
}

0 commit comments

Comments
 (0)