Skip to content

Commit d243e81

Browse files
committed
feat(junit): Add context extension
1 parent f588bcf commit d243e81

4 files changed

Lines changed: 47 additions & 49 deletions

File tree

dd-java-agent/instrumentation-testing/src/main/java/datadog/trace/agent/test/AbstractInstrumentationTest.java

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import datadog.trace.core.DDSpan;
2121
import datadog.trace.core.PendingTrace;
2222
import datadog.trace.core.TraceCollector;
23-
import de.thetaphi.forbiddenapis.SuppressForbidden;
23+
import datadog.trace.junit.utils.context.AllowContextTestingExtension;
2424
import java.lang.instrument.ClassFileTransformer;
2525
import java.lang.instrument.Instrumentation;
2626
import java.util.List;
@@ -31,7 +31,6 @@
3131
import java.util.function.Predicate;
3232
import net.bytebuddy.agent.ByteBuddyAgent;
3333
import org.junit.jupiter.api.AfterEach;
34-
import org.junit.jupiter.api.BeforeAll;
3534
import org.junit.jupiter.api.BeforeEach;
3635
import org.junit.jupiter.api.extension.ExtendWith;
3736
import org.opentest4j.AssertionFailedError;
@@ -42,7 +41,7 @@
4241
* current implementation is inspired and kept close to it Groovy / Spock counterpart, the {@code
4342
* InstrumentationSpecification}.
4443
*/
45-
@ExtendWith(TestClassShadowingExtension.class)
44+
@ExtendWith({TestClassShadowingExtension.class, AllowContextTestingExtension.class})
4645
public abstract class AbstractInstrumentationTest {
4746
static final Instrumentation INSTRUMENTATION = ByteBuddyAgent.getInstrumentation();
4847

@@ -55,19 +54,6 @@ public abstract class AbstractInstrumentationTest {
5554
protected ClassFileTransformer activeTransformer;
5655
protected ClassFileTransformerListener transformerLister;
5756

58-
@SuppressForbidden // Class.forName() used to dynamically configure context if present
59-
@BeforeAll
60-
static void allowContextTesting() {
61-
// Allow re-registration of context managers so each test can use a fresh tracer.
62-
// This mirrors DDSpecification.allowContextTesting() for the Spock test framework.
63-
try {
64-
Class.forName("datadog.context.ContextManager").getMethod("allowTesting").invoke(null);
65-
Class.forName("datadog.context.ContextBinder").getMethod("allowTesting").invoke(null);
66-
} catch (Throwable ignore) {
67-
// don't block testing if context types aren't available
68-
}
69-
}
70-
7157
@BeforeEach
7258
public void init() {
7359
// If this fails, it's likely the result of another test loading Config before it can be
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package datadog.trace.junit.utils.context;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
import org.junit.jupiter.api.extension.ExtendWith;
8+
9+
/**
10+
* Enables context testing by allowing re-registration of {@code ContextManager} and {@code
11+
* ContextBinder} singletons.
12+
*/
13+
@Retention(RetentionPolicy.RUNTIME)
14+
@Target(ElementType.TYPE)
15+
@ExtendWith(AllowContextTestingExtension.class)
16+
public @interface AllowContextTesting {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package datadog.trace.junit.utils.context;
2+
3+
import de.thetaphi.forbiddenapis.SuppressForbidden;
4+
import org.junit.jupiter.api.extension.BeforeAllCallback;
5+
import org.junit.jupiter.api.extension.ExtensionContext;
6+
7+
/**
8+
* JUnit 5 extension that allows re-registration of context managers so each test can use a fresh
9+
* tracer. This is needed because {@code ContextManager} and {@code ContextBinder} are singletons
10+
* that normally reject re-registration.
11+
*
12+
* <p>Auto-registered when using {@link AllowContextTesting}. Can also be used explicitly via
13+
* {@code @ExtendWith(AllowContextTestingExtension.class)}.
14+
*/
15+
@SuppressForbidden
16+
public class AllowContextTestingExtension implements BeforeAllCallback {
17+
18+
@Override
19+
public void beforeAll(ExtensionContext context) {
20+
try {
21+
Class.forName("datadog.context.ContextManager").getMethod("allowTesting").invoke(null);
22+
Class.forName("datadog.context.ContextBinder").getMethod("allowTesting").invoke(null);
23+
} catch (Throwable ignore) {
24+
// don't block testing if context types aren't available
25+
}
26+
}
27+
}

utils/test-utils/src/main/java/datadog/trace/test/util/DDJavaSpecification.java

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
import datadog.environment.EnvironmentVariables;
66
import datadog.trace.junit.utils.config.WithConfigExtension;
7+
import datadog.trace.junit.utils.context.AllowContextTestingExtension;
78
import de.thetaphi.forbiddenapis.SuppressForbidden;
8-
import java.lang.reflect.Method;
99
import java.util.Arrays;
1010
import java.util.List;
1111
import java.util.Map;
@@ -16,32 +16,23 @@
1616
import org.junit.jupiter.api.BeforeAll;
1717
import org.junit.jupiter.api.extension.ExtendWith;
1818

19-
@ExtendWith(WithConfigExtension.class)
19+
@ExtendWith({WithConfigExtension.class, AllowContextTestingExtension.class})
2020
@SuppressForbidden
2121
public class DDJavaSpecification {
2222

2323
private static final long CHECK_TIMEOUT_MS = 3000;
2424

25-
static final String CONTEXT_BINDER = "datadog.context.ContextBinder";
26-
static final String CONTEXT_MANAGER = "datadog.context.ContextManager";
27-
28-
private static Boolean contextTestingAllowed;
29-
3025
protected boolean assertThreadsEachCleanup = true;
3126
private static volatile boolean ignoreThreadCleanup;
3227

3328
@BeforeAll
3429
static void beforeAll() {
35-
allowContextTesting();
3630
assertTrue(
3731
EnvironmentVariables.getAll().entrySet().stream()
3832
.noneMatch(e -> e.getKey().startsWith("DD_")));
3933
assertTrue(
4034
systemPropertiesExceptAllowed().entrySet().stream()
4135
.noneMatch(e -> e.getKey().toString().startsWith("dd.")));
42-
assertTrue(
43-
contextTestingAllowed,
44-
"Context not ready for testing. Ensure all test classes extend DDJavaSpecification");
4536

4637
if (getDDThreads().isEmpty()) {
4738
ignoreThreadCleanup = false;
@@ -52,27 +43,6 @@ static void beforeAll() {
5243
}
5344
}
5445

55-
static void allowContextTesting() {
56-
if (contextTestingAllowed == null) {
57-
try {
58-
Class<?> binderClass = Class.forName(CONTEXT_BINDER);
59-
Method binderAllowTesting = binderClass.getDeclaredMethod("allowTesting");
60-
binderAllowTesting.setAccessible(true);
61-
Class<?> managerClass = Class.forName(CONTEXT_MANAGER);
62-
Method managerAllowTesting = managerClass.getDeclaredMethod("allowTesting");
63-
managerAllowTesting.setAccessible(true);
64-
contextTestingAllowed =
65-
(Boolean) binderAllowTesting.invoke(null) && (Boolean) managerAllowTesting.invoke(null);
66-
} catch (ClassNotFoundException e) {
67-
// don't block testing if these types aren't found (project doesn't use context API)
68-
contextTestingAllowed =
69-
CONTEXT_BINDER.equals(e.getMessage()) || CONTEXT_MANAGER.equals(e.getMessage());
70-
} catch (Throwable ignore) {
71-
contextTestingAllowed = false;
72-
}
73-
}
74-
}
75-
7646
@AfterAll
7747
static void afterAll() {
7848
assertTrue(
@@ -135,5 +105,4 @@ static void checkThreads() {
135105
System.out.println(names);
136106
}
137107
}
138-
139108
}

0 commit comments

Comments
 (0)