Skip to content

Commit f453621

Browse files
committed
Extend context/scope tests
1 parent 0a3d354 commit f453621

5 files changed

Lines changed: 90 additions & 46 deletions

File tree

components/context/src/test/java/datadog/context/ContextContinuationTest.java

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -255,16 +255,15 @@ void testOutOfOrderScopeCloseReleasesImmediately() {
255255
Context contextD = root().with(CONTINUATION_KEY, "D");
256256
try (ContextScope scopeR = continuation.resume()) {
257257
assertEquals(contextC, current());
258-
ContextScope scopeD = contextD.attach(); // attaching D fires detach:C, attach:D
259-
assertEquals(contextD, current());
258+
try (ContextScope scopeD = contextD.attach()) { // attaching D fires detach:C, attach:D
259+
assertEquals(contextD, current());
260260

261-
// close the resume scope out-of-order while D is still nested on top;
262-
// release fires immediately, but detach:C does not (C is not current)
263-
scopeR.close();
264-
assertEquals(asList("attach:C", "detach:C", "attach:D", "release:C"), events);
265-
assertEquals(contextD, current()); // D is still current
266-
267-
scopeD.close(); // unwind D normally, restores C
261+
// close the resume scope out-of-order while D is still nested on top;
262+
// release fires immediately, but detach:C does not (C is not current)
263+
scopeR.close();
264+
assertEquals(asList("attach:C", "detach:C", "attach:D", "release:C"), events);
265+
assertEquals(contextD, current()); // D is still current
266+
} // scopeD closes here: unwind D normally, restores C
268267
assertEquals(
269268
asList("attach:C", "detach:C", "attach:D", "release:C", "detach:D", "attach:C"), events);
270269
} // try-with-resources closes scopeR again; no second release, C unwinds to root
@@ -292,14 +291,13 @@ void testHoldWithOutOfOrderScopeCloseFiresReleaseOnExplicitRelease() {
292291
Context contextD = root().with(CONTINUATION_KEY, "D");
293292
try (ContextScope scopeR = continuation.resume()) {
294293
assertEquals(contextC, current());
295-
ContextScope scopeD = contextD.attach(); // detach:C, attach:D
296-
assertEquals(contextD, current());
297-
298-
scopeR.close(); // out-of-order close while D is still on top; hold prevents auto-release
299-
assertEquals(asList("attach:C", "detach:C", "attach:D"), events);
300-
assertEquals(contextD, current());
294+
try (ContextScope scopeD = contextD.attach()) { // detach:C, attach:D
295+
assertEquals(contextD, current());
301296

302-
scopeD.close(); // unwind D, restores C
297+
scopeR.close(); // out-of-order close while D is still on top; hold prevents auto-release
298+
assertEquals(asList("attach:C", "detach:C", "attach:D"), events);
299+
assertEquals(contextD, current());
300+
} // scopeD closes here: unwind D, restores C
303301
} // TWR closes scopeR again (now in-order); detach:C, no release yet (hold is active)
304302

305303
assertEquals(root(), current());
@@ -312,6 +310,46 @@ void testHoldWithOutOfOrderScopeCloseFiresReleaseOnExplicitRelease() {
312310
events);
313311
}
314312

313+
@Test
314+
void testMultipleHoldCallsAreIdempotent() {
315+
// Calling hold() more than once should not require more than one explicit release().
316+
List<String> events = new ArrayList<>();
317+
ContextManager.register(trackingListener(events));
318+
Context context = root().with(CONTINUATION_KEY, "value");
319+
ContextContinuation continuation;
320+
try (ContextScope scope = context.attach()) {
321+
continuation = context.capture();
322+
continuation.hold();
323+
continuation.hold(); // second hold must be a no-op
324+
}
325+
// One explicit release() is enough — no extra releases needed for the second hold().
326+
continuation.release();
327+
assertEquals(asList("attach", "capture", "detach", "release"), events);
328+
continuation.release(); // still idempotent after the final release
329+
assertEquals(asList("attach", "capture", "detach", "release"), events);
330+
}
331+
332+
@Test
333+
void testHoldAfterReleaseIsIgnored() {
334+
// hold() on an already-released continuation must not resurrect it.
335+
List<String> events = new ArrayList<>();
336+
ContextManager.register(trackingListener(events));
337+
Context context = root().with(CONTINUATION_KEY, "value");
338+
ContextContinuation continuation;
339+
try (ContextScope scope = context.attach()) {
340+
continuation = context.capture();
341+
}
342+
continuation.release();
343+
assertEquals(asList("attach", "capture", "detach", "release"), events);
344+
continuation.hold(); // must be silently ignored
345+
// resume() after release is already a noop, even with the spurious hold()
346+
try (ContextScope scope = continuation.resume()) {
347+
assertEquals(root(), current());
348+
}
349+
continuation.release(); // must not fire a second release event
350+
assertEquals(asList("attach", "capture", "detach", "release"), events);
351+
}
352+
315353
@Test
316354
void testHoldAllowsMultipleReleaseCalls() {
317355
List<String> events = new ArrayList<>();

components/context/src/test/java/datadog/context/ContextListenerEventTest.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ void testListenersNotifiedOnAttachAndDetach() {
2828
void testListenersNotNotifiedForRootContext() {
2929
List<String> events = new ArrayList<>();
3030
ContextManager.register(trackingListener(events));
31+
root().attach(); // current is already root, no events
32+
assertTrue(events.isEmpty(), "root attach should not trigger listeners");
3133
root().swap(); // current is already root, no events
3234
assertTrue(events.isEmpty(), "root swap should not trigger listeners");
3335
Context context = root().with(STRING_KEY, "value");
@@ -42,14 +44,14 @@ void testListenersNotNotifiedOnSameContextAttach() {
4244
List<String> events = new ArrayList<>();
4345
ContextManager.register(trackingListener(events));
4446
Context context = root().with(STRING_KEY, "same");
45-
ContextScope outer = context.attach();
46-
assertEquals(asList("attach"), events);
47-
try (ContextScope noop = context.attach()) {
48-
assertEquals(context, current());
49-
assertEquals(asList("attach"), events); // no new events on same-context attach
47+
try (ContextScope outer = context.attach()) {
48+
assertEquals(asList("attach"), events);
49+
try (ContextScope noop = context.attach()) {
50+
assertEquals(context, current());
51+
assertEquals(asList("attach"), events); // no new events on same-context attach
52+
}
53+
assertEquals(asList("attach"), events); // noop close fires no events either
5054
}
51-
assertEquals(asList("attach"), events); // noop close fires no events either
52-
outer.close();
5355
assertEquals(asList("attach", "detach"), events);
5456
}
5557

components/context/src/test/java/datadog/context/ContextListenerExceptionTest.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,13 @@ public void onCapture(Context c) {
4646
});
4747
Context context = root().with(STRING_KEY, "value");
4848
try (ContextScope scope = context.attach()) {
49-
ContextContinuation[] ref = {null};
50-
assertDoesNotThrow(() -> ref[0] = context.capture());
51-
assertNotNull(ref[0]);
52-
assertEquals(context, ref[0].context());
53-
ref[0].release();
49+
assertDoesNotThrow(
50+
() -> {
51+
ContextContinuation continuation = context.capture();
52+
assertNotNull(continuation);
53+
assertEquals(context, continuation.context());
54+
continuation.release();
55+
});
5456
}
5557
}
5658

components/context/src/test/java/datadog/context/ContextManagerTest.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ void testNoopScopeCacheHitReturnsSameScope() {
6666
Context context = root().with(STRING_KEY, "value");
6767
try (ContextScope outer = context.attach()) {
6868
// two consecutive noop scopes for the same context should be the same cached instance
69-
ContextScope noop1 = context.attach();
70-
try (ContextScope noop2 = context.attach()) {
69+
try (ContextScope noop1 = context.attach();
70+
ContextScope noop2 = context.attach()) {
7171
assertSame(noop1, noop2);
7272
}
7373
}
@@ -127,15 +127,16 @@ void testClosingMultipleTimes() {
127127
Context context1 = root().with(STRING_KEY, "value1");
128128
try (ContextScope ignored = context1.attach()) {
129129
Context context2 = context1.with(STRING_KEY, "value2");
130-
ContextScope scope = context2.attach();
131-
// Test current context
132-
assertEquals(context2, current());
133-
// Test current context deactivation
134-
scope.close();
135-
assertEquals(context1, current());
136-
// Test multiple context deactivations don’t change current context
137-
scope.close();
138-
assertEquals(context1, current());
130+
try (ContextScope scope = context2.attach()) {
131+
// Test current context
132+
assertEquals(context2, current());
133+
// Test current context deactivation
134+
scope.close();
135+
assertEquals(context1, current());
136+
// Test multiple context deactivations don’t change current context
137+
scope.close();
138+
assertEquals(context1, current());
139+
}
139140
}
140141
}
141142

dd-trace-core/src/test/java/datadog/trace/core/scopemanager/ScopeManagerTest.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static datadog.trace.core.scopemanager.ScopeManagerTest.EVENT.ACTIVATE;
66
import static datadog.trace.core.scopemanager.ScopeManagerTest.EVENT.CLOSE;
77
import static datadog.trace.test.util.GCUtils.awaitGC;
8+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
89
import static org.junit.jupiter.api.Assertions.assertEquals;
910
import static org.junit.jupiter.api.Assertions.assertFalse;
1011
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
@@ -1133,13 +1134,13 @@ void continuationReleaseIsSameAsCancel() throws Exception {
11331134
void captureContextWithoutSpanUsesNoopTraceCollector() {
11341135
ContextKey<String> key = ContextKey.named("test-key");
11351136
Context ctx = Context.root().with(key, "value");
1136-
try (ContextScope scope = ctx.attach()) {
1137-
// context with no span uses NoopAgentTraceCollector — should not crash
1138-
ContextContinuation continuation = Context.current().capture();
1139-
assertNotNull(continuation);
1140-
assertEquals(ctx, continuation.context());
1141-
continuation.release(); // no-op on NoopAgentTraceCollector
1142-
}
1137+
assertDoesNotThrow(
1138+
() -> {
1139+
// NoopAgentTraceCollector handles capture/release without throwing
1140+
try (ContextScope scope = ctx.attach()) {
1141+
Context.current().capture().release();
1142+
}
1143+
});
11431144
}
11441145

11451146
private boolean spanFinished(AgentSpan span) {

0 commit comments

Comments
 (0)