Skip to content

Commit 23f8fa5

Browse files
authored
report skipped spock features when setupSpec fails (fixes #1151, via #1347)
1 parent c7a8906 commit 23f8fa5

6 files changed

Lines changed: 502 additions & 43 deletions

File tree

allure-spock2/src/main/java/io/qameta/allure/spock2/AllureSpock2.java

Lines changed: 142 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
import org.spockframework.runtime.model.ErrorInfo;
4545
import org.spockframework.runtime.model.FeatureInfo;
4646
import org.spockframework.runtime.model.IterationInfo;
47-
import org.spockframework.runtime.model.MethodInfo;
4847
import org.spockframework.runtime.model.MethodKind;
4948
import org.spockframework.runtime.model.SpecInfo;
5049
import org.spockframework.runtime.model.TestTag;
@@ -65,6 +64,7 @@
6564
import java.util.stream.Collectors;
6665
import java.util.stream.IntStream;
6766

67+
import static io.qameta.allure.util.ResultsUtils.ALLURE_ID_LABEL_NAME;
6868
import static io.qameta.allure.util.ResultsUtils.createFrameworkLabel;
6969
import static io.qameta.allure.util.ResultsUtils.createHostLabel;
7070
import static io.qameta.allure.util.ResultsUtils.createLanguageLabel;
@@ -89,7 +89,7 @@
8989
*
9090
* <p>Register this extension with Spock to convert specification, feature, iteration, fixture, and error events into Allure results. The constructor accepting a test plan enables Allure test plan filtering before execution.</p>
9191
*/
92-
@SuppressWarnings("PMD.GodClass")
92+
@SuppressWarnings({"PMD.GodClass", "PMD.TooManyMethods"})
9393
public class AllureSpock2 extends AbstractRunListener implements IGlobalExtension, IBlockListener {
9494

9595
private static final String BLOCK = "block";
@@ -173,44 +173,62 @@ public void visitSpec(final SpecInfo spec) {
173173
*/
174174
@Override
175175
public void beforeIteration(final IterationInfo iteration) {
176-
final String uuid = UUID.randomUUID().toString();
177-
178176
final FeatureInfo feature = iteration.getFeature();
179-
final MethodInfo methodInfo = feature.getFeatureMethod();
180-
final Method method = methodInfo.getReflection();
177+
final List<Parameter> parameters = getParameters(feature.getDataVariables(), iteration.getDataValues());
178+
final TestResult result = createTestResult(
179+
feature,
180+
iteration.getName(),
181+
iteration.getDisplayName(),
182+
parameters
183+
);
184+
final String uuid = result.getUuid();
185+
186+
testResults.set(uuid);
187+
final AllureExternalKey testKey = testKey(uuid);
188+
getLifecycle().scheduleTest(testKey, result);
189+
getLifecycle().addDefaultLabels(testKey, getDefaultLabels(feature.getSpec()));
190+
getLifecycle().startTest(testKey);
191+
blockSteps.set(new IdentityHashMap<>());
192+
activeBlockStep.remove();
193+
194+
}
195+
196+
private TestResult createTestResult(final FeatureInfo feature,
197+
final String testCaseName,
198+
final String displayName,
199+
final List<Parameter> parameters) {
200+
return createTestResult(
201+
feature.getSpec(),
202+
feature.getFeatureMethod().getReflection(),
203+
feature.getTestTags(),
204+
feature.getName(),
205+
testCaseName,
206+
displayName,
207+
parameters
208+
);
209+
}
210+
211+
private TestResult createTestResult(final SpecInfo specInfo,
212+
final Method method,
213+
final Set<TestTag> testTags,
214+
final String testMethodName,
215+
final String testCaseName,
216+
final String displayName,
217+
final List<Parameter> parameters) {
181218
final Set<Label> featureLabels = AnnotationUtils.getLabels(method);
182219
final Set<Link> featureLinks = AnnotationUtils.getLinks(method);
183-
final SpecInfo specInfo = feature.getSpec();
184220
final Class<?> clazz = specInfo.getReflection();
185221
final Set<Label> specLabels = AnnotationUtils.getLabels(clazz);
186222
final Set<Link> specLinks = AnnotationUtils.getLinks(clazz);
187223
final boolean flaky = AnnotationUtils.isFlaky(method) || AnnotationUtils.isFlaky(clazz);
188224
final boolean muted = AnnotationUtils.isMuted(method) || AnnotationUtils.isMuted(clazz);
189225

190-
final List<Parameter> parameters = getParameters(feature.getDataVariables(), iteration.getDataValues());
191226
final SpecInfo subSpec = specInfo.getSubSpec();
192227
final SpecInfo superSpec = specInfo.getSuperSpec();
193228
final String packageName = specInfo.getPackage();
194-
final String specName = specInfo.getName();
195-
final String testClassName = feature.getSpec().getReflection().getName();
196-
// the declared feature name is the source-level identity of the feature method,
197-
// stable across data-driven iterations, unlike the resolved iteration display name
198-
final String testMethodName = feature.getName();
199-
final String displayName = iteration.getDisplayName();
200-
201-
final List<Label> defaultLabels = new ArrayList<>(
202-
Collections.singletonList(createSuiteLabel(specName))
203-
);
229+
final String testClassName = specInfo.getReflection().getName();
204230

205-
if (Objects.nonNull(subSpec)) {
206-
defaultLabels.add(createSubSuiteLabel(subSpec.getName()));
207-
}
208-
209-
if (Objects.nonNull(superSpec)) {
210-
defaultLabels.add(createParentSuiteLabel(superSpec.getName()));
211-
}
212-
213-
final List<Label> testTags = feature.getTestTags().stream()
231+
final List<Label> tagLabels = testTags.stream()
214232
.map(TestTag::getValue)
215233
.map(ResultsUtils::createTagLabel)
216234
.collect(Collectors.toList());
@@ -226,7 +244,7 @@ public void beforeIteration(final IterationInfo iteration) {
226244
createLanguageLabel("java")
227245
)
228246
);
229-
labels.addAll(testTags);
247+
labels.addAll(tagLabels);
230248
labels.addAll(featureLabels);
231249
labels.addAll(specLabels);
232250
AnnotationUtils.getSeverity(method)
@@ -239,23 +257,23 @@ public void beforeIteration(final IterationInfo iteration) {
239257
final List<Link> links = new ArrayList<>(featureLinks);
240258
links.addAll(specLinks);
241259

242-
final String qualifiedName = getQualifiedName(iteration);
260+
final String qualifiedName = getQualifiedName(specInfo.getReflection().getName(), testCaseName);
243261
final List<String> titlePath = new ArrayList<>(createTitlePathFromPackage(packageName));
244262
titlePath.addAll(
245263
createTitlePath(
246264
Objects.nonNull(superSpec) ? superSpec.getName() : null,
247-
specName,
265+
specInfo.getName(),
248266
Objects.nonNull(subSpec) ? subSpec.getName() : null
249267
)
250268
);
251269
final TestResult result = new TestResult()
252-
.setUuid(uuid)
253-
.setTestCaseName(iteration.getName())
270+
.setUuid(UUID.randomUUID().toString())
271+
.setTestCaseName(testCaseName)
254272
.setTestCaseId(md5(qualifiedName))
255273
.setFullName(qualifiedName)
256274
.setTitlePath(titlePath)
257275
.setName(
258-
firstNonEmpty(displayName, iteration.getName(), qualifiedName)
276+
firstNonEmpty(displayName, testCaseName, qualifiedName)
259277
.orElse("Unknown")
260278
)
261279
.setStatusDetails(
@@ -274,18 +292,91 @@ public void beforeIteration(final IterationInfo iteration) {
274292
result::setDescriptionHtml
275293
);
276294

277-
testResults.set(uuid);
278-
final AllureExternalKey testKey = testKey(uuid);
279-
getLifecycle().scheduleTest(testKey, result);
280-
getLifecycle().addDefaultLabels(testKey, defaultLabels);
281-
getLifecycle().startTest(testKey);
282-
blockSteps.set(new IdentityHashMap<>());
283-
activeBlockStep.remove();
295+
return result;
296+
}
297+
298+
private List<Label> getDefaultLabels(final SpecInfo specInfo) {
299+
final List<Label> defaultLabels = new ArrayList<>(
300+
Collections.singletonList(createSuiteLabel(specInfo.getName()))
301+
);
284302

303+
final SpecInfo subSpec = specInfo.getSubSpec();
304+
if (Objects.nonNull(subSpec)) {
305+
defaultLabels.add(createSubSuiteLabel(subSpec.getName()));
306+
}
307+
308+
final SpecInfo superSpec = specInfo.getSuperSpec();
309+
if (Objects.nonNull(superSpec)) {
310+
defaultLabels.add(createParentSuiteLabel(superSpec.getName()));
311+
}
312+
313+
return defaultLabels;
314+
}
315+
316+
private void reportSetupSpecFailure(final IMethodInvocation invocation,
317+
final String containerUuid,
318+
final String fixtureName,
319+
final Throwable throwable) {
320+
final StatusDetails failureDetails = getStatusDetails(throwable).orElse(null);
321+
final SpecInfo fixtureSpec = invocation.getSpec();
322+
final TestResult configurationResult = createTestResult(
323+
fixtureSpec,
324+
invocation.getMethod().getReflection(),
325+
Collections.emptySet(),
326+
fixtureName,
327+
fixtureName,
328+
fixtureName,
329+
Collections.emptyList()
330+
).setStatus(getStatus(throwable).orElse(Status.BROKEN));
331+
configurationResult.getLabels().add(
332+
new Label().setName(ALLURE_ID_LABEL_NAME).setValue("-1")
333+
);
334+
copyFailureDetails(failureDetails, configurationResult.getStatusDetails());
335+
writeCompletedTest(containerUuid, configurationResult, fixtureSpec);
336+
337+
fixtureSpec.getBottomSpec().getAllFeaturesInExecutionOrder().stream()
338+
.filter(feature -> !feature.isExcluded())
339+
.filter(feature -> !feature.isSkipped())
340+
.forEach(feature -> {
341+
final TestResult skippedResult = createTestResult(
342+
feature,
343+
feature.getName(),
344+
feature.getDisplayName(),
345+
Collections.emptyList()
346+
).setStatus(Status.SKIPPED);
347+
copyFailureDetails(failureDetails, skippedResult.getStatusDetails());
348+
final String failureMessage = skippedResult.getStatusDetails().getMessage();
349+
skippedResult.getStatusDetails().setMessage(
350+
Objects.isNull(failureMessage)
351+
? "Skipped because setup spec failed"
352+
: "Skipped because setup spec failed: " + failureMessage
353+
);
354+
writeCompletedTest(containerUuid, skippedResult, feature.getSpec());
355+
});
285356
}
286357

287-
private String getQualifiedName(final IterationInfo iteration) {
288-
return this.getQualifiedName(iteration.getFeature().getSpec().getReflection().getName(), iteration.getName());
358+
private void copyFailureDetails(final StatusDetails source, final StatusDetails target) {
359+
if (Objects.nonNull(source)) {
360+
target.setMessage(source.getMessage())
361+
.setTrace(source.getTrace())
362+
.setActual(source.getActual())
363+
.setExpected(source.getExpected());
364+
}
365+
}
366+
367+
private void writeCompletedTest(final String containerUuid,
368+
final TestResult result,
369+
final SpecInfo specInfo) {
370+
final AllureExternalKey resultKey = testKey(result.getUuid());
371+
getLifecycle().scheduleTest(
372+
Collections.singletonList(scopeKey(containerUuid)),
373+
resultKey,
374+
result
375+
);
376+
getLifecycle().addDefaultLabels(resultKey, getDefaultLabels(specInfo));
377+
getLifecycle().startTest(resultKey);
378+
getLifecycle().stopTest(resultKey);
379+
getLifecycle().writeTest(resultKey);
289380
}
290381

291382
private String getQualifiedName(final FeatureInfo featureInfo) {
@@ -645,6 +736,7 @@ public void intercept(final IMethodInvocation invocation) throws Throwable {
645736
);
646737
}
647738

739+
Throwable failure = null;
648740
try {
649741
invocation.proceed();
650742
getLifecycle().updateFixture(
@@ -658,10 +750,17 @@ public void intercept(final IMethodInvocation invocation) throws Throwable {
658750
.setStatus(getStatus(throwable).orElse(Status.BROKEN))
659751
.setStatusDetails(getStatusDetails(throwable).orElse(null))
660752
);
661-
throw ExceptionUtils.sneakyThrow(throwable);
753+
failure = throwable;
662754
} finally {
663755
getLifecycle().stopFixture(fixtureKey);
664756
}
757+
758+
if (Objects.nonNull(failure)) {
759+
if (MethodKind.SETUP_SPEC.equals(kind)) {
760+
reportSetupSpecFailure(invocation, containerUuid, fixtureName, failure);
761+
}
762+
throw ExceptionUtils.sneakyThrow(failure);
763+
}
665764
}
666765

667766
}

0 commit comments

Comments
 (0)