diff --git a/components/context/build.gradle.kts b/components/context/build.gradle.kts index 27992034fc2..9889bf0a86a 100644 --- a/components/context/build.gradle.kts +++ b/components/context/build.gradle.kts @@ -1,3 +1,22 @@ 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") + +tasks.named("jacocoTestCoverageVerification") { + violationRules { + rule { + element = "CLASS" + includes = listOf("datadog.context.ThreadLocalContextManager.ContextContinuationImpl") + limit { + counter = "BRANCH" + minimum = "0.8".toBigDecimal() + } + } + } +} 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()); } }