Skip to content

Commit 8a8dbaa

Browse files
authored
1 parent 76a6717 commit 8a8dbaa

24 files changed

Lines changed: 1361 additions & 5 deletions

allure-java-commons/src/main/java/io/qameta/allure/util/AnnotationUtils.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
*/
1616
package io.qameta.allure.util;
1717

18+
import io.qameta.allure.Flaky;
1819
import io.qameta.allure.LabelAnnotation;
1920
import io.qameta.allure.LinkAnnotation;
21+
import io.qameta.allure.Muted;
2022
import io.qameta.allure.model.Label;
2123
import io.qameta.allure.model.Link;
2224
import org.slf4j.Logger;
@@ -55,6 +57,26 @@ private AnnotationUtils() {
5557
throw new IllegalStateException("Do not instance");
5658
}
5759

60+
/**
61+
* Returns true if {@link io.qameta.allure.Flaky} annotation is present.
62+
*
63+
* @param annotatedElement the element to search annotations on.
64+
* @return true if {@link io.qameta.allure.Flaky} annotation is present, false otherwise.
65+
*/
66+
public static boolean isFlaky(final AnnotatedElement annotatedElement) {
67+
return annotatedElement.isAnnotationPresent(Flaky.class);
68+
}
69+
70+
/**
71+
* Returns true if {@link io.qameta.allure.Muted} annotation is present.
72+
*
73+
* @param annotatedElement the element to search annotations on.
74+
* @return true if {@link io.qameta.allure.Muted} annotation is present, false otherwise.
75+
*/
76+
public static boolean isMuted(final AnnotatedElement annotatedElement) {
77+
return annotatedElement.isAnnotationPresent(Muted.class);
78+
}
79+
5880
/**
5981
* Returns links created from Allure meta annotations specified on annotated element.
6082
*

allure-java-commons/src/main/java/io/qameta/allure/util/ResultsUtils.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,12 @@ public static Optional<Status> getStatus(final Throwable throwable) {
286286
public static Optional<StatusDetails> getStatusDetails(final Throwable e) {
287287
return Optional.ofNullable(e)
288288
.map(throwable -> new StatusDetails()
289-
.setMessage(Optional.ofNullable(throwable.getMessage()).orElse(throwable.getClass().getName()))
290-
.setTrace(getStackTraceAsString(throwable)));
289+
.setMessage(Optional
290+
.ofNullable(throwable.getMessage())
291+
.orElse(throwable.getClass().getName())
292+
)
293+
.setTrace(getStackTraceAsString(throwable))
294+
);
291295
}
292296

293297
public static Optional<String> getJavadocDescription(final ClassLoader classLoader,

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,8 @@ private Map<String, String> unwrap(final Map<String, String> data) {
242242
final Map<String, String> res = new HashMap<>();
243243
data.forEach((key, value) -> {
244244
if (Objects.nonNull(value)
245-
&& value.trim().isEmpty()
246-
&& value.startsWith(ALLURE_REPORT_ENTRY_BLANK_PREFIX)) {
245+
&& value.trim().isEmpty()
246+
&& value.startsWith(ALLURE_REPORT_ENTRY_BLANK_PREFIX)) {
247247
res.put(key, value.substring(ALLURE_REPORT_ENTRY_BLANK_PREFIX.length()));
248248
} else {
249249
res.put(key, value);
@@ -544,7 +544,23 @@ private void stopTestCase(final TestIdentifier testIdentifier,
544544
}
545545
result.setStage(Stage.FINISHED);
546546
result.setStatus(status);
547-
result.setStatusDetails(statusDetails);
547+
548+
final StatusDetails currentSd = result.getStatusDetails();
549+
if (Objects.isNull(currentSd)) {
550+
result.setStatusDetails(statusDetails);
551+
} else if (Objects.nonNull(statusDetails)) {
552+
Optional.of(statusDetails)
553+
.map(StatusDetails::getMessage)
554+
.ifPresent(currentSd::setMessage);
555+
556+
Optional.of(statusDetails)
557+
.map(StatusDetails::getTrace)
558+
.ifPresent(currentSd::setTrace);
559+
560+
currentSd.setMuted(currentSd.isMuted() || statusDetails.isMuted());
561+
currentSd.setFlaky(currentSd.isFlaky() || statusDetails.isFlaky());
562+
currentSd.setKnown(currentSd.isKnown() || statusDetails.isKnown());
563+
}
548564
});
549565
getLifecycle().stopTestCase(uuid);
550566
getLifecycle().writeTestCase(uuid);

allure-spock2/build.gradle.kts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
description = "Allure Spock 2 Framework Integration"
2+
3+
plugins {
4+
groovy
5+
}
6+
7+
val spockFrameworkVersion = "2.3-groovy-3.0"
8+
val groovyVersion = "3.0.13"
9+
10+
dependencies {
11+
api(project(":allure-java-commons"))
12+
implementation("org.spockframework:spock-core:$spockFrameworkVersion")
13+
compileOnly("org.aspectj:aspectjrt")
14+
implementation(project(":allure-test-filter"))
15+
testAnnotationProcessor("org.slf4j:slf4j-simple")
16+
testAnnotationProcessor(project(":allure-descriptions-javadoc"))
17+
testImplementation("io.github.glytching:junit-extensions")
18+
testImplementation("org.assertj:assertj-core")
19+
testImplementation("org.codehaus.groovy:groovy-all:${groovyVersion}")
20+
testImplementation("org.junit.jupiter:junit-jupiter-api")
21+
testImplementation("org.junit.jupiter:junit-jupiter-params")
22+
testImplementation("org.junit.platform:junit-platform-launcher")
23+
testImplementation("org.mockito:mockito-core")
24+
testImplementation("org.slf4j:slf4j-simple")
25+
testImplementation(project(":allure-assertj"))
26+
testImplementation(project(":allure-java-commons-test"))
27+
testImplementation(project(":allure-junit-platform"))
28+
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
29+
}
30+
31+
tasks.jar {
32+
manifest {
33+
attributes(mapOf(
34+
"Automatic-Module-Name" to "io.qameta.allure.spock2"
35+
))
36+
}
37+
38+
from("src/main/services") {
39+
into("META-INF/services")
40+
}
41+
}
42+
43+
tasks.test {
44+
useJUnitPlatform()
45+
exclude("**/samples/*")
46+
}
47+
48+
val spiOffJar: Jar by tasks.creating(Jar::class) {
49+
from(sourceSets.getByName("main").output)
50+
archiveClassifier.set("spi-off")
51+
}
52+
53+
publishing {
54+
publications {
55+
named<MavenPublication>("maven") {
56+
artifact(spiOffJar)
57+
}
58+
}
59+
}
60+
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
/*
2+
* Copyright 2019 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.spock2;
17+
18+
import io.qameta.allure.Allure;
19+
import io.qameta.allure.AllureLifecycle;
20+
import io.qameta.allure.model.Label;
21+
import io.qameta.allure.model.Link;
22+
import io.qameta.allure.model.Parameter;
23+
import io.qameta.allure.model.Status;
24+
import io.qameta.allure.model.StatusDetails;
25+
import io.qameta.allure.model.TestResult;
26+
import io.qameta.allure.util.AnnotationUtils;
27+
import io.qameta.allure.util.ResultsUtils;
28+
import org.spockframework.runtime.AbstractRunListener;
29+
import org.spockframework.runtime.extension.IGlobalExtension;
30+
import org.spockframework.runtime.model.ErrorInfo;
31+
import org.spockframework.runtime.model.FeatureInfo;
32+
import org.spockframework.runtime.model.IterationInfo;
33+
import org.spockframework.runtime.model.MethodInfo;
34+
import org.spockframework.runtime.model.SpecInfo;
35+
36+
import java.lang.reflect.Method;
37+
import java.security.MessageDigest;
38+
import java.util.ArrayList;
39+
import java.util.Arrays;
40+
import java.util.List;
41+
import java.util.Objects;
42+
import java.util.Set;
43+
import java.util.UUID;
44+
import java.util.stream.Collectors;
45+
import java.util.stream.IntStream;
46+
47+
import static io.qameta.allure.util.ResultsUtils.bytesToHex;
48+
import static io.qameta.allure.util.ResultsUtils.createFrameworkLabel;
49+
import static io.qameta.allure.util.ResultsUtils.createHostLabel;
50+
import static io.qameta.allure.util.ResultsUtils.createLanguageLabel;
51+
import static io.qameta.allure.util.ResultsUtils.createPackageLabel;
52+
import static io.qameta.allure.util.ResultsUtils.createParameter;
53+
import static io.qameta.allure.util.ResultsUtils.createParentSuiteLabel;
54+
import static io.qameta.allure.util.ResultsUtils.createSubSuiteLabel;
55+
import static io.qameta.allure.util.ResultsUtils.createSuiteLabel;
56+
import static io.qameta.allure.util.ResultsUtils.createTestClassLabel;
57+
import static io.qameta.allure.util.ResultsUtils.createTestMethodLabel;
58+
import static io.qameta.allure.util.ResultsUtils.createThreadLabel;
59+
import static io.qameta.allure.util.ResultsUtils.firstNonEmpty;
60+
import static io.qameta.allure.util.ResultsUtils.getMd5Digest;
61+
import static io.qameta.allure.util.ResultsUtils.getProvidedLabels;
62+
import static io.qameta.allure.util.ResultsUtils.getStatus;
63+
import static io.qameta.allure.util.ResultsUtils.getStatusDetails;
64+
import static io.qameta.allure.util.ResultsUtils.md5;
65+
import static java.nio.charset.StandardCharsets.UTF_8;
66+
import static java.util.Comparator.comparing;
67+
68+
/**
69+
* @author charlie (Dmitry Baev).
70+
*/
71+
@SuppressWarnings({
72+
"PMD.NcssCount"
73+
})
74+
public class AllureSpock2 extends AbstractRunListener implements IGlobalExtension {
75+
76+
private final ThreadLocal<String> testResults = new InheritableThreadLocal<String>() {
77+
@Override
78+
protected String initialValue() {
79+
return UUID.randomUUID().toString();
80+
}
81+
};
82+
83+
private final AllureLifecycle lifecycle;
84+
85+
@SuppressWarnings("unused")
86+
public AllureSpock2() {
87+
this(Allure.getLifecycle());
88+
}
89+
90+
public AllureSpock2(final AllureLifecycle lifecycle) {
91+
this.lifecycle = lifecycle;
92+
}
93+
94+
@Override
95+
public void start() {
96+
//do nothing at this point
97+
}
98+
99+
@Override
100+
public void visitSpec(final SpecInfo spec) {
101+
spec.addListener(this);
102+
}
103+
104+
@Override
105+
public void stop() {
106+
//do nothing at this point
107+
}
108+
109+
@Override
110+
public void beforeIteration(final IterationInfo iteration) {
111+
final String uuid = testResults.get();
112+
113+
final FeatureInfo feature = iteration.getFeature();
114+
final MethodInfo methodInfo = feature.getFeatureMethod();
115+
final Method method = methodInfo.getReflection();
116+
final Set<Label> featureLabels = AnnotationUtils.getLabels(method);
117+
final Set<Link> featureLinks = AnnotationUtils.getLinks(method);
118+
final SpecInfo specInfo = feature.getSpec();
119+
final Class<?> clazz = specInfo.getReflection();
120+
final Set<Label> specLabels = AnnotationUtils.getLabels(clazz);
121+
final Set<Link> specLinks = AnnotationUtils.getLinks(clazz);
122+
final boolean flaky = AnnotationUtils.isFlaky(method) || AnnotationUtils.isFlaky(clazz);
123+
final boolean muted = AnnotationUtils.isMuted(method) || AnnotationUtils.isMuted(clazz);
124+
125+
final List<Parameter> parameters = getParameters(feature.getDataVariables(), iteration.getDataValues());
126+
final SpecInfo subSpec = specInfo.getSubSpec();
127+
final SpecInfo superSpec = specInfo.getSuperSpec();
128+
final String packageName = specInfo.getPackage();
129+
final String specName = specInfo.getName();
130+
final String testClassName = feature.getSpec().getReflection().getName();
131+
final String testMethodName = iteration.getDisplayName();
132+
133+
final List<Label> labels = new ArrayList<>(Arrays.asList(
134+
createPackageLabel(packageName),
135+
createTestClassLabel(testClassName),
136+
createTestMethodLabel(testMethodName),
137+
createSuiteLabel(specName),
138+
createHostLabel(),
139+
createThreadLabel(),
140+
createFrameworkLabel("spock"),
141+
createLanguageLabel("java")
142+
));
143+
144+
if (Objects.nonNull(subSpec)) {
145+
labels.add(createSubSuiteLabel(subSpec.getName()));
146+
}
147+
148+
if (Objects.nonNull(superSpec)) {
149+
labels.add(createParentSuiteLabel(superSpec.getName()));
150+
}
151+
152+
labels.addAll(featureLabels);
153+
labels.addAll(specLabels);
154+
labels.addAll(getProvidedLabels());
155+
156+
final List<Link> links = new ArrayList<>(featureLinks);
157+
links.addAll(specLinks);
158+
159+
final String qualifiedName = getQualifiedName(iteration);
160+
final TestResult result = new TestResult()
161+
.setUuid(uuid)
162+
.setHistoryId(getHistoryId(qualifiedName, parameters))
163+
.setTestCaseName(iteration.getName())
164+
.setTestCaseId(md5(qualifiedName))
165+
.setFullName(qualifiedName)
166+
.setName(
167+
firstNonEmpty(testMethodName, iteration.getName(), qualifiedName)
168+
.orElse("Unknown")
169+
)
170+
.setStatusDetails(new StatusDetails()
171+
.setFlaky(flaky)
172+
.setMuted(muted)
173+
)
174+
.setParameters(parameters)
175+
.setLinks(links)
176+
.setLabels(labels);
177+
178+
ResultsUtils.processDescription(
179+
getClass().getClassLoader(),
180+
method,
181+
result::setDescription,
182+
result::setDescriptionHtml
183+
);
184+
185+
getLifecycle().scheduleTestCase(result);
186+
getLifecycle().startTestCase(uuid);
187+
}
188+
189+
private String getQualifiedName(final IterationInfo iteration) {
190+
return iteration.getFeature().getSpec().getReflection().getName() + "." + iteration.getName();
191+
}
192+
193+
private String getHistoryId(final String name, final List<Parameter> parameters) {
194+
final MessageDigest digest = getMd5Digest();
195+
digest.update(name.getBytes(UTF_8));
196+
parameters.stream()
197+
.sorted(comparing(Parameter::getName).thenComparing(Parameter::getValue))
198+
.forEachOrdered(parameter -> {
199+
digest.update(parameter.getName().getBytes(UTF_8));
200+
digest.update(parameter.getValue().getBytes(UTF_8));
201+
});
202+
final byte[] bytes = digest.digest();
203+
return bytesToHex(bytes);
204+
}
205+
206+
@Override
207+
public void error(final ErrorInfo error) {
208+
final String uuid = testResults.get();
209+
getLifecycle().updateTestCase(uuid, testResult -> testResult
210+
.setStatus(getStatus(error.getException()).orElse(null))
211+
.setStatusDetails(getStatusDetails(error.getException()).orElse(null))
212+
);
213+
}
214+
215+
@Override
216+
public void afterIteration(final IterationInfo iteration) {
217+
final String uuid = testResults.get();
218+
testResults.remove();
219+
220+
getLifecycle().updateTestCase(uuid, testResult -> {
221+
if (Objects.isNull(testResult.getStatus())) {
222+
testResult.setStatus(Status.PASSED);
223+
}
224+
});
225+
getLifecycle().stopTestCase(uuid);
226+
getLifecycle().writeTestCase(uuid);
227+
}
228+
229+
private List<Parameter> getParameters(final List<String> names, final Object... values) {
230+
return IntStream.range(0, Math.min(names.size(), values.length))
231+
.mapToObj(index -> createParameter(names.get(index), values[index]))
232+
.collect(Collectors.toList());
233+
}
234+
235+
public AllureLifecycle getLifecycle() {
236+
return lifecycle;
237+
}
238+
}

0 commit comments

Comments
 (0)