Skip to content

Commit 32c41f6

Browse files
committed
Print stacktraces in a better way
Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
1 parent e6b3a66 commit 32c41f6

File tree

2 files changed

+54
-55
lines changed

2 files changed

+54
-55
lines changed

microprofile/testing/junit5/src/main/java/io/helidon/microprofile/testing/junit5/HelidonPinnedThreadValidationJunitExtension.java

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,79 +16,77 @@
1616

1717
package io.helidon.microprofile.testing.junit5;
1818

19-
import java.util.ArrayList;
20-
import java.util.List;
21-
2219
import jdk.jfr.consumer.RecordedEvent;
23-
import jdk.jfr.consumer.RecordedFrame;
2420
import jdk.jfr.consumer.RecordingStream;
2521
import org.junit.jupiter.api.extension.AfterAllCallback;
2622
import org.junit.jupiter.api.extension.BeforeAllCallback;
2723
import org.junit.jupiter.api.extension.ExtensionContext;
2824

29-
import static org.junit.jupiter.api.Assertions.fail;
30-
3125
/**
3226
* JUnit5 extension to support pinned threads validation.
3327
*/
3428
class HelidonPinnedThreadValidationJunitExtension implements BeforeAllCallback, AfterAllCallback {
3529

36-
private List<EventWrapper> jfrVTPinned;
3730
private RecordingStream recordingStream;
3831
private boolean pinnedThreadValidation;
32+
private PinningException pinningException;
3933

4034
@Override
4135
public void beforeAll(ExtensionContext context) throws Exception {
4236
Class<?> testClass = context.getRequiredTestClass();
4337
pinnedThreadValidation = testClass.getAnnotation(PinnedThreadValidation.class) != null;
4438
if (pinnedThreadValidation) {
45-
jfrVTPinned = new ArrayList<>();
4639
recordingStream = new RecordingStream();
4740
recordingStream.enable("jdk.VirtualThreadPinned").withStackTrace();
48-
recordingStream.onEvent("jdk.VirtualThreadPinned", event -> {
49-
jfrVTPinned.add(new EventWrapper(event));
50-
});
41+
recordingStream.onEvent("jdk.VirtualThreadPinned", this::record);
5142
recordingStream.startAsync();
5243
}
5344
}
5445

46+
void record(RecordedEvent event) {
47+
PinningException e = new PinningException(event);
48+
if (pinningException == null) {
49+
pinningException = e;
50+
} else {
51+
pinningException.addSuppressed(e);
52+
}
53+
}
54+
5555
@Override
5656
public void afterAll(ExtensionContext context) {
5757
if (pinnedThreadValidation) {
5858
try {
5959
// Flush ending events
6060
recordingStream.stop();
61-
if (!jfrVTPinned.isEmpty()) {
62-
fail("Some pinned virtual threads were detected:\n" + jfrVTPinned);
61+
if (pinningException != null) {
62+
throw pinningException;
6363
}
6464
} finally {
6565
recordingStream.close();
6666
}
6767
}
6868
}
6969

70-
private static class EventWrapper {
71-
70+
private static class PinningException extends AssertionError {
7271
private final RecordedEvent recordedEvent;
7372

74-
private EventWrapper(RecordedEvent recordedEvent) {
73+
PinningException(RecordedEvent recordedEvent) {
7574
this.recordedEvent = recordedEvent;
75+
if (recordedEvent.getStackTrace() != null) {
76+
StackTraceElement[] stackTraceElements = recordedEvent.getStackTrace().getFrames().stream()
77+
.map(f -> new StackTraceElement(f.getMethod().getType().getName(),
78+
f.getMethod().getName(),
79+
f.getMethod().getType().getName() + ".java",
80+
f.getLineNumber()))
81+
.toArray(StackTraceElement[]::new);
82+
super.setStackTrace(stackTraceElements);
83+
}
7684
}
7785

7886
@Override
79-
public String toString() {
80-
StringBuilder builder = new StringBuilder(recordedEvent.toString());
81-
if (recordedEvent.getStackTrace() != null) {
82-
builder.append("full-stackTrace = [");
83-
List<RecordedFrame> frames = recordedEvent.getStackTrace().getFrames();
84-
for (RecordedFrame frame : frames) {
85-
builder.append("\n\t").append(frame.getMethod().getType().getName())
86-
.append("#").append(frame.getMethod().getName())
87-
.append("(").append(frame.getLineNumber()).append(")");
88-
}
89-
builder.append("\n]");
90-
}
91-
return builder.toString();
87+
public String getMessage() {
88+
return "Pinned virtual threads were detected:\n"
89+
+ recordedEvent.toString();
9290
}
9391
}
9492
}

microprofile/testing/testng/src/main/java/io/helidon/microprofile/testing/testng/HelidonTestNgListener.java

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
import jakarta.inject.Singleton;
6161
import jakarta.ws.rs.client.ClientBuilder;
6262
import jdk.jfr.consumer.RecordedEvent;
63-
import jdk.jfr.consumer.RecordedFrame;
6463
import jdk.jfr.consumer.RecordingStream;
6564
import org.eclipse.microprofile.config.Config;
6665
import org.eclipse.microprofile.config.spi.ConfigBuilder;
@@ -73,8 +72,6 @@
7372
import org.testng.ITestResult;
7473
import org.testng.annotations.Test;
7574

76-
import static org.testng.Assert.fail;
77-
7875
/**
7976
* TestNG extension to support Helidon CDI container in tests.
8077
*/
@@ -93,7 +90,6 @@ public class HelidonTestNgListener implements IClassListener, ITestListener {
9390
private List<AddExtension> classLevelExtensions = new ArrayList<>();
9491
private List<AddBean> classLevelBeans = new ArrayList<>();
9592
private ConfigMeta classLevelConfigMeta = new ConfigMeta();
96-
private List<EventWrapper> jfrVTPinned;
9793
private RecordingStream recordingStream;
9894
private boolean classLevelDisableDiscovery = false;
9995
private boolean resetPerTest;
@@ -104,6 +100,7 @@ public class HelidonTestNgListener implements IClassListener, ITestListener {
104100
private ConfigProviderResolver configProviderResolver;
105101
private Config config;
106102
private SeContainer container;
103+
private PinningException pinningException;
107104

108105
@Override
109106
public void onBeforeClass(ITestClass iTestClass) {
@@ -371,12 +368,9 @@ private <T extends Annotation> T getAnnotation(Class<?> testClass, Class<T> anno
371368

372369
private void startRecordingStream() {
373370
if (pinnedThreadValidation) {
374-
jfrVTPinned = new ArrayList<>();
375371
recordingStream = new RecordingStream();
376372
recordingStream.enable("jdk.VirtualThreadPinned").withStackTrace();
377-
recordingStream.onEvent("jdk.VirtualThreadPinned", event -> {
378-
jfrVTPinned.add(new EventWrapper(event));
379-
});
373+
recordingStream.onEvent("jdk.VirtualThreadPinned", this::record);
380374
recordingStream.startAsync();
381375
}
382376
}
@@ -386,8 +380,8 @@ private void closeRecordingStream() {
386380
try {
387381
// Flush ending events
388382
recordingStream.stop();
389-
if (!jfrVTPinned.isEmpty()) {
390-
fail("Some pinned virtual threads were detected:\n" + jfrVTPinned);
383+
if (pinningException != null) {
384+
throw pinningException;
391385
}
392386
} finally {
393387
recordingStream.close();
@@ -468,6 +462,15 @@ private static boolean hasAnnotation(AnnotatedElement element, Set<Class<? exten
468462
return false;
469463
}
470464

465+
void record(RecordedEvent event) {
466+
PinningException e = new PinningException(event);
467+
if (pinningException == null) {
468+
pinningException = e;
469+
} else {
470+
pinningException.addSuppressed(e);
471+
}
472+
}
473+
471474
@SuppressWarnings("CdiManagedBeanInconsistencyInspection")
472475
private record TestInstanceExtension(Object testInstance, Class<?> testClass) implements Extension {
473476

@@ -683,28 +686,26 @@ private static final class SingletonLiteral extends AnnotationLiteral<Singleton>
683686
static final SingletonLiteral INSTANCE = new SingletonLiteral();
684687
}
685688

686-
private static class EventWrapper {
687-
689+
private static class PinningException extends AssertionError {
688690
private final RecordedEvent recordedEvent;
689691

690-
private EventWrapper(RecordedEvent recordedEvent) {
692+
PinningException(RecordedEvent recordedEvent) {
691693
this.recordedEvent = recordedEvent;
694+
if (recordedEvent.getStackTrace() != null) {
695+
StackTraceElement[] stackTraceElements = recordedEvent.getStackTrace().getFrames().stream()
696+
.map(f -> new StackTraceElement(f.getMethod().getType().getName(),
697+
f.getMethod().getName(),
698+
f.getMethod().getType().getName() + ".java",
699+
f.getLineNumber()))
700+
.toArray(StackTraceElement[]::new);
701+
super.setStackTrace(stackTraceElements);
702+
}
692703
}
693704

694705
@Override
695-
public String toString() {
696-
StringBuilder builder = new StringBuilder(recordedEvent.toString());
697-
if (recordedEvent.getStackTrace() != null) {
698-
builder.append("full-stackTrace = [");
699-
List<RecordedFrame> frames = recordedEvent.getStackTrace().getFrames();
700-
for (RecordedFrame frame : frames) {
701-
builder.append("\n\t").append(frame.getMethod().getType().getName())
702-
.append("#").append(frame.getMethod().getName())
703-
.append("(").append(frame.getLineNumber()).append(")");
704-
}
705-
builder.append("\n]");
706-
}
707-
return builder.toString();
706+
public String getMessage() {
707+
return "Pinned virtual threads were detected:\n"
708+
+ recordedEvent.toString();
708709
}
709710
}
710711

0 commit comments

Comments
 (0)