From f6e2be7fc9c78070f5ea4add9bd7d5800d2d92de Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Sat, 17 Oct 2026 20:01:29 +0100 Subject: [PATCH 1/2] Cover competing boot and system loader-value computations --- .../utils/ClassLoaderValueTest.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/utils/src/test/java/datadog/instrument/utils/ClassLoaderValueTest.java b/utils/src/test/java/datadog/instrument/utils/ClassLoaderValueTest.java index 0147309..f8ac3fa 100644 --- a/utils/src/test/java/datadog/instrument/utils/ClassLoaderValueTest.java +++ b/utils/src/test/java/datadog/instrument/utils/ClassLoaderValueTest.java @@ -4,11 +4,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; @SuppressFBWarnings("DM_GC") @SuppressWarnings({"UnusedAssignment", "ConstantValue"}) @@ -158,6 +167,50 @@ void removeStaleEntries() { visited.clear(); } + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void competingComputationsReturnTheInstalledValue(boolean systemLoader) throws Exception { + ClassLoader loader = systemLoader ? ClassLoader.getSystemClassLoader() : null; + int threadCount = 8; + CountDownLatch computing = new CountDownLatch(threadCount); + AtomicInteger computeCalls = new AtomicInteger(); + ClassLoaderValue values = + new ClassLoaderValue() { + @Override + protected Object computeValue(ClassLoader cl) { + assertThat(cl).isSameAs(loader); + computeCalls.incrementAndGet(); + // Nobody can install a value until every contender is already computing one. + computing.countDown(); + try { + assertThat(computing.await(10, TimeUnit.SECONDS)).isTrue(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new AssertionError(e); + } + return new Object(); + } + }; + + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + List> results = new ArrayList<>(); + try { + for (int i = 0; i < threadCount; i++) { + results.add(executor.submit(() -> values.get(loader))); + } + Object winner = results.get(0).get(10, TimeUnit.SECONDS); + assertThat(winner).isNotNull(); + for (Future result : results) { + assertThat(result.get(10, TimeUnit.SECONDS)).isSameAs(winner); + } + assertThat(values.get(loader)).isSameAs(winner); + assertThat(computeCalls).hasValue(threadCount); + assertThat(values.size()).isEqualTo(1); + } finally { + executor.shutdownNow(); + } + } + private static ClassLoader newCL(String name) { return new ClassLoader() { @Override From 636fca5f94c6a9fca9ec1491c33e4681e4b4d2c5 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Sat, 17 Oct 2026 20:01:36 +0100 Subject: [PATCH 2/2] Cover class-name filter collisions and invalid file magic --- .../instrument/utils/ClassNameFilterTest.java | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/utils/src/test/java/datadog/instrument/utils/ClassNameFilterTest.java b/utils/src/test/java/datadog/instrument/utils/ClassNameFilterTest.java index 77530ee..971a0f1 100644 --- a/utils/src/test/java/datadog/instrument/utils/ClassNameFilterTest.java +++ b/utils/src/test/java/datadog/instrument/utils/ClassNameFilterTest.java @@ -1,6 +1,7 @@ package datadog.instrument.utils; import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -15,6 +16,8 @@ import java.util.HashSet; import java.util.Set; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; class ClassNameFilterTest { @@ -36,24 +39,37 @@ void basicOperation() { assertFalse(filter.contains("example.test.NotMyClass")); } - @Test - void uniqueClassCodes() { + @ParameterizedTest + @CsvSource({ + "example.GAnxX, example.ZvaQyFGz", // different simple-name lengths + "example.test.FB, example.test.Ea", // different first characters + "example.AaAa, example.AaBB" // same first character, different last characters + }) + void uniqueClassCodes(String stored, String collision) { ClassNameFilter filter = new ClassNameFilter(8); - assertEquals("example.test.FB".hashCode(), "example.test.Ea".hashCode()); + assertEquals(stored.hashCode(), collision.hashCode()); + assertFalse(filter.contains(stored)); + assertFalse(filter.contains(collision)); - assertFalse(filter.contains("example.test.FB")); - assertFalse(filter.contains("example.test.Ea")); + filter.add(stored); - filter.add("example.test.FB"); + assertTrue(filter.contains(stored)); + assertFalse(filter.contains(collision)); - assertTrue(filter.contains("example.test.FB")); - assertFalse(filter.contains("example.test.Ea")); + filter.add(collision); - filter.add("example.test.Ea"); + assertTrue(filter.contains(stored)); + assertFalse(filter.contains(collision)); + } - assertTrue(filter.contains("example.test.FB")); - assertFalse(filter.contains("example.test.Ea")); + @Test + void rejectsInvalidFileMagic() throws IOException { + try (DataInputStream input = new DataInputStream(new ByteArrayInputStream(new byte[4]))) { + assertThatThrownBy(() -> ClassNameFilter.readFrom(input)) + .isInstanceOf(IOException.class) + .hasMessage("Unexpected file magic 0"); + } } @Test