From b4c11ee0d7a97ea51d963127765b23017c0a7f67 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Mon, 29 Jun 2026 10:26:49 +0100 Subject: [PATCH 1/3] Cover continuation and listener behaviour when registering no-op manager during tests. --- .../context/ContextProvidersForkedTest.java | 53 ++++++++++++++----- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/components/context/src/test/java/datadog/context/ContextProvidersForkedTest.java b/components/context/src/test/java/datadog/context/ContextProvidersForkedTest.java index b8e61ddc4a4..47e6287ea36 100644 --- a/components/context/src/test/java/datadog/context/ContextProvidersForkedTest.java +++ b/components/context/src/test/java/datadog/context/ContextProvidersForkedTest.java @@ -2,10 +2,13 @@ import static datadog.context.Context.root; import static datadog.context.ContextTest.STRING_KEY; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static datadog.context.ContextTestBase.trackingListener; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.ArrayList; +import java.util.List; import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; @@ -15,13 +18,15 @@ void testCustomBinder() { assertTrue(ContextBinder.allowTesting()); Context context = root().with(STRING_KEY, "value"); + assertNotEquals(root(), context); + Object carrier = new Object(); // should delegate to the default binder context.attachTo(carrier); - assertNotEquals(root(), Context.from(carrier)); - assertEquals(context, Context.detachFrom(carrier)); - assertEquals(root(), Context.from(carrier)); + assertSame(context, Context.from(carrier)); + assertSame(context, Context.detachFrom(carrier)); + assertSame(root(), Context.from(carrier)); // now register a NOOP context binder ContextBinder.register( @@ -44,8 +49,9 @@ public Context detachFrom(@Nonnull Object carrier) { // NOOP binder, context will always be root context.attachTo(carrier); - assertEquals(root(), Context.from(carrier)); - assertEquals(root(), Context.detachFrom(carrier)); + assertSame(root(), Context.from(carrier)); + assertSame(root(), Context.detachFrom(carrier)); + assertSame(root(), Context.from(carrier)); } @Test @@ -53,15 +59,22 @@ void testCustomManager() { assertTrue(ContextManager.allowTesting()); Context context = root().with(STRING_KEY, "value"); + assertNotEquals(root(), context); // should delegate to the default manager - try (ContextScope ignored = context.attach()) { - assertNotEquals(root(), Context.current()); + try (ContextScope scope = context.attach()) { + assertSame(context, scope.context()); + assertSame(context, Context.current()); + ContextContinuation cont = context.capture(); + assertSame(context, cont.context()); + cont.release(); } Context swapped = context.swap(); - assertNotEquals(root(), Context.current()); - swapped.swap(); + assertSame(root(), swapped); + assertSame(context, Context.current()); + assertSame(context, swapped.swap()); + assertSame(root(), Context.current()); // now register a NOOP context manager ContextManager.register( @@ -90,14 +103,26 @@ public ContextContinuation capture(Context context) { public void addListener(ContextListener listener) {} }); + List events = new ArrayList<>(); + ContextManager.register(trackingListener(events)); + // NOOP manager, context will always be root - try (ContextScope ignored = context.attach()) { - assertEquals(root(), Context.current()); + try (ContextScope scope = context.attach()) { + assertSame(root(), scope.context()); + assertSame(root(), Context.current()); + ContextContinuation cont = context.capture(); + assertSame(root(), cont.context()); + cont.release(); } // NOOP manager, context will always be root swapped = context.swap(); - assertEquals(root(), Context.current()); - swapped.swap(); + assertSame(root(), swapped); + assertSame(root(), Context.current()); + assertSame(root(), swapped.swap()); + assertSame(root(), Context.current()); + + // NOOP manager, no events emitted + assertTrue(events.isEmpty()); } } From 20128f120524a0c733158581657dcd8de84b4d57 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Mon, 29 Jun 2026 10:55:39 +0100 Subject: [PATCH 2/3] Relax default 90% branch coverage rule to 80% for ContextContinuationImpl (couple of branches involve a nanosecond CAS race that can't be reliably reproduced) --- components/context/build.gradle.kts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/components/context/build.gradle.kts b/components/context/build.gradle.kts index 27992034fc2..9dab6ccb0b5 100644 --- a/components/context/build.gradle.kts +++ b/components/context/build.gradle.kts @@ -1,3 +1,24 @@ apply(from = "$rootDir/gradle/java.gradle") -extra["excludedClassesInstructionCoverage"] = listOf("datadog.context.ContextProviders") // covered by forked test +extra["excludedClassesInstructionCoverage"] = + listOf("datadog.context.ContextProviders") // covered by forked test + +// excluded from the default 90% rule so the relaxed 80% rule below can apply instead +// (couple of branches involve a nanosecond CAS race that can't be reliably reproduced) +extra["excludedClassesBranchCoverage"] = + listOf("datadog.context.ThreadLocalContextManager.ContextContinuationImpl") + +afterEvaluate { + tasks.named("jacocoTestCoverageVerification") { + violationRules { + rule { + element = "CLASS" + includes = listOf("datadog.context.ThreadLocalContextManager.ContextContinuationImpl") + limit { + counter = "BRANCH" + minimum = "0.8".toBigDecimal() + } + } + } + } +} From 1c94887241973ab6de2e3baaed94f59fe4e26bc4 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Mon, 29 Jun 2026 14:54:51 +0100 Subject: [PATCH 3/3] Update jacoco task outside of afterEvaluate block --- components/context/build.gradle.kts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/components/context/build.gradle.kts b/components/context/build.gradle.kts index 9dab6ccb0b5..9889bf0a86a 100644 --- a/components/context/build.gradle.kts +++ b/components/context/build.gradle.kts @@ -8,16 +8,14 @@ extra["excludedClassesInstructionCoverage"] = extra["excludedClassesBranchCoverage"] = listOf("datadog.context.ThreadLocalContextManager.ContextContinuationImpl") -afterEvaluate { - tasks.named("jacocoTestCoverageVerification") { - violationRules { - rule { - element = "CLASS" - includes = listOf("datadog.context.ThreadLocalContextManager.ContextContinuationImpl") - limit { - counter = "BRANCH" - minimum = "0.8".toBigDecimal() - } +tasks.named("jacocoTestCoverageVerification") { + violationRules { + rule { + element = "CLASS" + includes = listOf("datadog.context.ThreadLocalContextManager.ContextContinuationImpl") + limit { + counter = "BRANCH" + minimum = "0.8".toBigDecimal() } } }