Skip to content

Commit 2cd4b81

Browse files
committed
Change how the synthetic stack trace frame is inserted to use InvocationInterceptor. This works for dynamic tests as well. Fixes #11.
1 parent 2fd57a2 commit 2cd4b81

2 files changed

Lines changed: 95 additions & 23 deletions

File tree

randomizedtesting-jupiter/src/main/java/com/carrotsearch/randomizedtesting/jupiter/internals/RandomizedContextExtension.java

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,27 @@
66
import com.carrotsearch.randomizedtesting.jupiter.Seed;
77
import com.carrotsearch.randomizedtesting.jupiter.SeedChain;
88
import com.carrotsearch.randomizedtesting.jupiter.SysProps;
9+
import java.lang.reflect.Constructor;
10+
import java.lang.reflect.Method;
911
import java.util.ArrayList;
1012
import java.util.Arrays;
1113
import java.util.List;
1214
import java.util.Objects;
1315
import java.util.Optional;
1416
import java.util.Random;
1517
import java.util.function.LongFunction;
18+
import org.jspecify.annotations.Nullable;
1619
import org.junit.jupiter.api.extension.BeforeAllCallback;
20+
import org.junit.jupiter.api.extension.DynamicTestInvocationContext;
1721
import org.junit.jupiter.api.extension.ExtensionContext;
18-
import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;
22+
import org.junit.jupiter.api.extension.InvocationInterceptor;
1923
import org.junit.jupiter.api.extension.ParameterContext;
2024
import org.junit.jupiter.api.extension.ParameterResolutionException;
2125
import org.junit.jupiter.api.extension.ParameterResolver;
22-
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
26+
import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
2327

2428
public class RandomizedContextExtension
25-
implements ParameterResolver,
26-
BeforeAllCallback,
27-
TestExecutionExceptionHandler,
28-
LifecycleMethodExecutionExceptionHandler {
29+
implements ParameterResolver, BeforeAllCallback, InvocationInterceptor {
2930
private static final ExtensionContext.Namespace EXTENSION_NAMESPACE =
3031
ExtensionContext.Namespace.create(RandomizedContextExtension.class);
3132

@@ -131,7 +132,7 @@ public Object resolveParameter(
131132
// internal handling of randomized contexts within junit contexts.
132133
//
133134

134-
private RandomizedContextImpl getRandomizedContextFor(ExtensionContext extensionContext) {
135+
private static RandomizedContextImpl getRandomizedContextFor(ExtensionContext extensionContext) {
135136
var store =
136137
extensionContext.getStore(
137138
ExtensionContext.StoreScope.EXTENSION_CONTEXT, EXTENSION_NAMESPACE);
@@ -153,37 +154,97 @@ private RandomizedContextImpl getRandomizedContextFor(ExtensionContext extension
153154
// exception handling and seed stack frame injection
154155
//
155156

157+
private static <T> T wrapInvoke(Invocation<T> invocation, ExtensionContext context)
158+
throws Throwable {
159+
try {
160+
return invocation.proceed();
161+
} catch (Throwable throwable) {
162+
throw addSeedChainStackFrame(throwable, getRandomizedContextFor(context).getSeedChain());
163+
}
164+
}
165+
156166
@Override
157-
public void handleTestExecutionException(ExtensionContext context, Throwable throwable)
167+
public <T> T interceptTestClassConstructor(
168+
Invocation<T> invocation,
169+
ReflectiveInvocationContext<Constructor<T>> invocationContext,
170+
ExtensionContext extensionContext)
158171
throws Throwable {
159-
throw addSeedChainStackFrame(throwable, getRandomizedContextFor(context).getSeedChain());
172+
return wrapInvoke(invocation, extensionContext);
160173
}
161174

162175
@Override
163-
public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable)
176+
public void interceptBeforeAllMethod(
177+
Invocation<@Nullable Void> invocation,
178+
ReflectiveInvocationContext<Method> invocationContext,
179+
ExtensionContext extensionContext)
164180
throws Throwable {
165-
throw addSeedChainStackFrame(throwable, getRandomizedContextFor(context).getSeedChain());
181+
wrapInvoke(invocation, extensionContext);
166182
}
167183

168184
@Override
169-
public void handleBeforeEachMethodExecutionException(
170-
ExtensionContext context, Throwable throwable) throws Throwable {
171-
throw addSeedChainStackFrame(throwable, getRandomizedContextFor(context).getSeedChain());
185+
public void interceptBeforeEachMethod(
186+
Invocation<@Nullable Void> invocation,
187+
ReflectiveInvocationContext<Method> invocationContext,
188+
ExtensionContext extensionContext)
189+
throws Throwable {
190+
wrapInvoke(invocation, extensionContext);
191+
}
192+
193+
@Override
194+
public void interceptTestMethod(
195+
Invocation<@Nullable Void> invocation,
196+
ReflectiveInvocationContext<Method> invocationContext,
197+
ExtensionContext extensionContext)
198+
throws Throwable {
199+
wrapInvoke(invocation, extensionContext);
200+
}
201+
202+
@Override
203+
public <T> T interceptTestFactoryMethod(
204+
Invocation<T> invocation,
205+
ReflectiveInvocationContext<Method> invocationContext,
206+
ExtensionContext extensionContext)
207+
throws Throwable {
208+
return wrapInvoke(invocation, extensionContext);
209+
}
210+
211+
@Override
212+
public void interceptTestTemplateMethod(
213+
Invocation<@Nullable Void> invocation,
214+
ReflectiveInvocationContext<Method> invocationContext,
215+
ExtensionContext extensionContext)
216+
throws Throwable {
217+
wrapInvoke(invocation, extensionContext);
218+
}
219+
220+
@Override
221+
public void interceptAfterEachMethod(
222+
Invocation<@Nullable Void> invocation,
223+
ReflectiveInvocationContext<Method> invocationContext,
224+
ExtensionContext extensionContext)
225+
throws Throwable {
226+
wrapInvoke(invocation, extensionContext);
172227
}
173228

174229
@Override
175-
public void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable)
230+
public void interceptAfterAllMethod(
231+
Invocation<@Nullable Void> invocation,
232+
ReflectiveInvocationContext<Method> invocationContext,
233+
ExtensionContext extensionContext)
176234
throws Throwable {
177-
throw addSeedChainStackFrame(throwable, getRandomizedContextFor(context).getSeedChain());
235+
wrapInvoke(invocation, extensionContext);
178236
}
179237

180238
@Override
181-
public void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable)
239+
public void interceptDynamicTest(
240+
Invocation<@Nullable Void> invocation,
241+
DynamicTestInvocationContext invocationContext,
242+
ExtensionContext extensionContext)
182243
throws Throwable {
183-
throw addSeedChainStackFrame(throwable, getRandomizedContextFor(context).getSeedChain());
244+
wrapInvoke(invocation, extensionContext);
184245
}
185246

186-
private Throwable addSeedChainStackFrame(Throwable throwable, SeedChain seedChain) {
247+
private static Throwable addSeedChainStackFrame(Throwable throwable, SeedChain seedChain) {
187248
List<StackTraceElement> stack = new ArrayList<>(Arrays.asList(throwable.getStackTrace()));
188249
stack.addFirst(
189250
new StackTraceElement(Constants.AUGMENTED_SEED_CLASS, "seed", seedChain.toString(), -1));

randomizedtesting-jupiter/src/test/java/com/carrotsearch/randomizedtesting/tests/F002_SeedRecovery.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Stream<DynamicTest> dynamicTestsFromIterator() {
5454
new Check("@BeforeAll", AtBeforeAll.class, "[DEAD:BEEF]"),
5555
new Check("@BeforeEach", AtBeforeEachLevel.class, "[DEAD:BEEF:CAFE]"),
5656
new Check("@Test", AtTestLevel.class, "[DEAD:BEEF:CAFE]"),
57+
new Check("@TestFactory", InDynamicTest.class, "[DEAD:BEEF:CAFE:"),
5758
new Check("@AfterEach", AtAfterEachLevel.class, "[DEAD:BEEF:CAFE]"),
5859
new Check("@AfterAll", AtAfterAll.class, "[DEAD:BEEF]"))
5960
.map(e -> DynamicTest.dynamicTest(e.name(), () -> runCheck(e)));
@@ -76,15 +77,25 @@ private void runCheck(Check e) {
7677
t -> {
7778
Assertions.assertThat(t.getStackTrace()[0].toString())
7879
.contains(
79-
Constants.AUGMENTED_SEED_CLASS
80-
+ ".seed("
81-
+ e.expectedSeed
82-
+ ")");
80+
Constants.AUGMENTED_SEED_CLASS + ".seed(" + e.expectedSeed);
8381
return true;
8482
},
8583
"first stack frame contains seed entry"))));
8684
}
8785

86+
@Randomized
87+
static class InDynamicTest extends IgnoreInStandaloneRuns {
88+
@TestFactory
89+
public Stream<DynamicTest> dynamicTests() {
90+
return Stream.of(
91+
DynamicTest.dynamicTest(
92+
"dynamicTest",
93+
() -> {
94+
throw new AssertionError("Failure.");
95+
}));
96+
}
97+
}
98+
8899
@Randomized
89100
static class AtBeforeAll extends IgnoreInStandaloneRuns {
90101
@BeforeAll

0 commit comments

Comments
 (0)