Skip to content

Exceptions thrown by hooks are not completely pruned #4828

@mpkorstanje

Description

@mpkorstanje

Steps to reproduce

Given an arbitrary suite with a failing before suite hook.

@Suite
@SelectPackages("com.example")
public class RunCucumberTest {

    @BeforeSuite
    static void before(){
        throw new RuntimeException("Oops");
    }

    @AfterSuite
    static void after(){

    }
}

The trimmed stacktrace is:

java.lang.RuntimeException: Oops
        at io.cucumber.skeleton.RunCucumberTest.before(RunCucumberTest.java:21)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
        at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
        at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1939)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
        at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)

While I would expect only:

java.lang.RuntimeException: Oops
        at io.cucumber.skeleton.RunCucumberTest.before(RunCucumberTest.java:21)

Likewise

public class ExampleTest {
    
    @BeforeAll
    static void beforeAll(){
        throw new RuntimeException("Oops");
    }
    
    @Test
    void test(){
        
    }
}

Prints a slightly too long stacktrace:

java.lang.RuntimeException: Oops

	at io.cucumber.prettyformatter.ExampleTest.beforeAll(ExampleTest.java:10)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

To support engines whos children are tests, it would also be good to include both self and ancestors when looking for prune limits in StackTracePruningEngineExecutionListener. But I couldn't reproduce the issues mentioned in #4825, with any existing Engine implementation. They all have containers as children.

Solution:

	private static List<String> getTestClassNames(TestDescriptor testDescriptor) {
		var self = Stream.of(testDescriptor);
		var ancestors = testDescriptor.getAncestors().stream();
		return Stream.concat(self, ancestors) //
				.map(TestDescriptor::getSource) //
				.flatMap(Optional::stream) //
				.map(source -> {
					if (source instanceof ClassSource classSource) {
						return classSource.getClassName();
					}
					else if (source instanceof MethodSource methodSource) {
						return methodSource.getClassName();
					}
					else {
						return null;
					}
				}) //
				.filter(Objects::nonNull) //
				.toList();
	}

Context

Did a small investigation following the discussion in #4825 and found this instead.

  • Version: 5.13.4, 6.0.0-SNAPSHOT @ fc94772

Deliverables

  • Slightly more pruned stacktraces
  • Include both self and ancestors in getTestClassNames for test that don't use containers.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions