diff --git a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/CacheProviderTest.groovy b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/CacheProviderTest.groovy deleted file mode 100644 index 6aad662c118c..000000000000 --- a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/CacheProviderTest.groovy +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling - -import io.opentelemetry.javaagent.tooling.muzzle.AgentCachingPoolStrategy -import net.bytebuddy.description.type.TypeDescription -import net.bytebuddy.pool.TypePool -import spock.lang.Specification - -import java.lang.ref.WeakReference - -class CacheProviderTest extends Specification { - def "key bootstrap equivalence"() { - // def loader = null - def loaderHash = AgentCachingPoolStrategy.BOOTSTRAP_HASH - def loaderRef = null - - def key1 = new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "foo") - def key2 = new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "foo") - - expect: - key1.hashCode() == key2.hashCode() - key1.equals(key2) - } - - def "key same ref equivalence"() { - setup: - def loader = newClassLoader() - def loaderHash = loader.hashCode() - def loaderRef = new WeakReference(loader) - - def key1 = new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "foo") - def key2 = new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "foo") - - expect: - key1.hashCode() == key2.hashCode() - key1.equals(key2) - // ensures that loader isn't collected - loader != null - } - - def "key different ref equivalence"() { - setup: - def loader = newClassLoader() - def loaderHash = loader.hashCode() - def loaderRef1 = new WeakReference(loader) - def loaderRef2 = new WeakReference(loader) - - def key1 = new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef1, "foo") - def key2 = new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef2, "foo") - - expect: - loaderRef1 != loaderRef2 - - key1.hashCode() == key2.hashCode() - key1.equals(key2) - // ensures that loader isn't collected - loader != null - } - - def "key mismatch -- same loader - diff name"() { - setup: - def loader = newClassLoader() - def loaderHash = loader.hashCode() - def loaderRef = new WeakReference(loader) - def fooKey = new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "foo") - def barKey = new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "bar") - - expect: - // not strictly guaranteed -- but important for performance - fooKey.hashCode() != barKey.hashCode() - !fooKey.equals(barKey) - // ensures that loader isn't collected - loader != null - } - - def "key mismatch -- same name - diff loader"() { - setup: - def loader1 = newClassLoader() - def loader1Hash = loader1.hashCode() - def loaderRef1 = new WeakReference(loader1) - - def loader2 = newClassLoader() - def loader2Hash = loader2.hashCode() - def loaderRef2 = new WeakReference(loader2) - - def fooKey1 = new AgentCachingPoolStrategy.TypeCacheKey(loader1Hash, loaderRef1, "foo") - def fooKey2 = new AgentCachingPoolStrategy.TypeCacheKey(loader2Hash, loaderRef2, "foo") - - expect: - // not strictly guaranteed -- but important for performance - fooKey1.hashCode() != fooKey2.hashCode() - !fooKey1.equals(fooKey2) - // ensures that loader isn't collected - loader1 != null - loader2 != null - } - - def "test basic caching"() { - setup: - def poolStrat = new AgentCachingPoolStrategy() - - def loader = newClassLoader() - - def cacheProvider = poolStrat.getCacheProvider(loader) - - when: - cacheProvider.register("foo", new TypePool.Resolution.Simple(TypeDescription.VOID)) - - then: - // not strictly guaranteed, but fine for this test - cacheProvider.find("foo") != null - // ensures that loader isn't collected - loader != null - } - - def "test loader equivalence"() { - setup: - def poolStrat = new AgentCachingPoolStrategy() - - def loader1 = newClassLoader() - - def cacheProvider1A = poolStrat.getCacheProvider(loader1) - def cacheProvider1B = poolStrat.getCacheProvider(loader1) - - when: - cacheProvider1A.register("foo", newVoid()) - - then: - // not strictly guaranteed, but fine for this test - cacheProvider1A.find("foo") != null - cacheProvider1B.find("foo") != null - - cacheProvider1A.find("foo").is(cacheProvider1B.find("foo")) - - // ensures that loader isn't collected - loader1 != null - } - - def "test loader separation"() { - setup: - def poolStrat = new AgentCachingPoolStrategy() - - def loader1 = newClassLoader() - def loader2 = newClassLoader() - - def cacheProvider1 = poolStrat.getCacheProvider(loader1) - def cacheProvider2 = poolStrat.getCacheProvider(loader2) - - when: - cacheProvider1.register("foo", newVoid()) - cacheProvider2.register("foo", newVoid()) - - then: - // not strictly guaranteed, but fine for this test - cacheProvider1.find("foo") != null - cacheProvider2.find("foo") != null - - !cacheProvider1.find("foo").is(cacheProvider2.find("foo")) - - // ensures that loader isn't collected - loader1 != null - loader2 != null - } - - static newVoid() { - return new TypePool.Resolution.Simple(TypeDescription.ForLoadedType.of(void.class)) - } - - static newClassLoader() { - return new URLClassLoader([] as URL[], (ClassLoader) null) - } -} diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/AgentCachingPoolStrategy.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/AgentCachingPoolStrategy.java index e931af800f76..c6865f9a8eab 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/AgentCachingPoolStrategy.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/AgentCachingPoolStrategy.java @@ -146,7 +146,8 @@ public AgentTypePool typePool( return typePool(classFileLocator, classLoader); } - private TypePool.CacheProvider getCacheProvider(ClassLoader classLoader) { + // visible for testing + TypePool.CacheProvider getCacheProvider(ClassLoader classLoader) { if (classLoader == null) { return bootstrapCacheProvider; } @@ -167,7 +168,8 @@ private TypePool.CacheProvider getCacheProvider(ClassLoader classLoader) { * *

The loaderHash exists to avoid calling get & strengthening the Reference. */ - private static final class TypeCacheKey { + // visible for testing + static final class TypeCacheKey { private final int loaderHash; @Nullable private final WeakReference loaderRef; private final String className; diff --git a/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/CacheProviderTest.java b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/CacheProviderTest.java new file mode 100644 index 000000000000..7e43794f7bab --- /dev/null +++ b/muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/CacheProviderTest.java @@ -0,0 +1,176 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.muzzle; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.ref.WeakReference; +import java.net.URL; +import java.net.URLClassLoader; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.pool.TypePool; +import org.junit.jupiter.api.Test; + +class CacheProviderTest { + + @Test + void keyBootstrapEquivalence() { + int loaderHash = AgentCachingPoolStrategy.BOOTSTRAP_HASH; + WeakReference loaderRef = null; + + AgentCachingPoolStrategy.TypeCacheKey key1 = + new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "foo"); + AgentCachingPoolStrategy.TypeCacheKey key2 = + new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "foo"); + + assertThat(key1).hasSameHashCodeAs(key2).isEqualTo(key2); + } + + @Test + void keySameRefEquivalence() { + ClassLoader loader = newClassLoader(); + int loaderHash = loader.hashCode(); + WeakReference loaderRef = new WeakReference<>(loader); + + AgentCachingPoolStrategy.TypeCacheKey key1 = + new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "foo"); + AgentCachingPoolStrategy.TypeCacheKey key2 = + new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "foo"); + + assertThat(key1).hasSameHashCodeAs(key2).isEqualTo(key2); + // use loader to ensures that it isn't collected before the test completes + assertThat(loader).isNotNull(); + } + + @Test + void keyDifferentRefEquivalence() { + ClassLoader loader = newClassLoader(); + int loaderHash = loader.hashCode(); + WeakReference loaderRef1 = new WeakReference<>(loader); + WeakReference loaderRef2 = new WeakReference<>(loader); + + AgentCachingPoolStrategy.TypeCacheKey key1 = + new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef1, "foo"); + AgentCachingPoolStrategy.TypeCacheKey key2 = + new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef2, "foo"); + + assertThat(loaderRef1).isNotSameAs(loaderRef2); + + assertThat(key1).hasSameHashCodeAs(key2).isEqualTo(key2); + // use loader to ensures that it isn't collected before the test completes + assertThat(loader).isNotNull(); + } + + @Test + void keyMismatchSameLoaderDifferentName() { + ClassLoader loader = newClassLoader(); + int loaderHash = loader.hashCode(); + WeakReference loaderRef = new WeakReference<>(loader); + AgentCachingPoolStrategy.TypeCacheKey fooKey = + new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "foo"); + AgentCachingPoolStrategy.TypeCacheKey barKey = + new AgentCachingPoolStrategy.TypeCacheKey(loaderHash, loaderRef, "bar"); + + // not strictly guaranteed -- but important for performance + assertThat(fooKey.hashCode()).isNotEqualTo(barKey.hashCode()); + assertThat(fooKey).isNotEqualTo(barKey); + // use loader to ensures that it isn't collected before the test completes + assertThat(loader).isNotNull(); + } + + @Test + void keyMismatchSameNameDifferentLoader() { + ClassLoader loader1 = newClassLoader(); + int loader1Hash = loader1.hashCode(); + WeakReference loaderRef1 = new WeakReference<>(loader1); + + ClassLoader loader2 = newClassLoader(); + int loader2Hash = loader2.hashCode(); + WeakReference loaderRef2 = new WeakReference<>(loader2); + + AgentCachingPoolStrategy.TypeCacheKey fooKey1 = + new AgentCachingPoolStrategy.TypeCacheKey(loader1Hash, loaderRef1, "foo"); + AgentCachingPoolStrategy.TypeCacheKey fooKey2 = + new AgentCachingPoolStrategy.TypeCacheKey(loader2Hash, loaderRef2, "foo"); + + // not strictly guaranteed -- but important for performance + assertThat(fooKey1.hashCode()).isNotEqualTo(fooKey2.hashCode()); + assertThat(fooKey1).isNotEqualTo(fooKey2); + // use loader to ensures that it isn't collected before the test completes + assertThat(loader1).isNotNull(); + assertThat(loader2).isNotNull(); + } + + @Test + void testBasicCaching() { + AgentCachingPoolStrategy poolStrat = new AgentCachingPoolStrategy(null); + + ClassLoader loader = newClassLoader(); + + TypePool.CacheProvider cacheProvider = poolStrat.getCacheProvider(loader); + + cacheProvider.register( + "foo", new TypePool.Resolution.Simple(TypeDescription.ForLoadedType.of(void.class))); + + // not strictly guaranteed, but fine for this test + assertThat(cacheProvider.find("foo")).isNotNull(); + // use loader to ensures that it isn't collected before the test completes + assertThat(loader).isNotNull(); + } + + @Test + void testLoaderEquivalence() { + AgentCachingPoolStrategy poolStrat = new AgentCachingPoolStrategy(null); + + ClassLoader loader1 = newClassLoader(); + + TypePool.CacheProvider cacheProvider1A = poolStrat.getCacheProvider(loader1); + TypePool.CacheProvider cacheProvider1B = poolStrat.getCacheProvider(loader1); + + cacheProvider1A.register("foo", newVoid()); + + // not strictly guaranteed, but fine for this test + assertThat(cacheProvider1A.find("foo")).isNotNull(); + assertThat(cacheProvider1B.find("foo")).isNotNull(); + + assertThat(cacheProvider1A.find("foo")).isSameAs(cacheProvider1B.find("foo")); + + // use loader to ensures that it isn't collected before the test completes + assertThat(loader1).isNotNull(); + } + + @Test + void testLoaderSeparation() { + AgentCachingPoolStrategy poolStrat = new AgentCachingPoolStrategy(null); + + ClassLoader loader1 = newClassLoader(); + ClassLoader loader2 = newClassLoader(); + + TypePool.CacheProvider cacheProvider1 = poolStrat.getCacheProvider(loader1); + TypePool.CacheProvider cacheProvider2 = poolStrat.getCacheProvider(loader2); + + cacheProvider1.register("foo", newVoid()); + cacheProvider2.register("foo", newVoid()); + + // not strictly guaranteed, but fine for this test + assertThat(cacheProvider1.find("foo")).isNotNull(); + assertThat(cacheProvider2.find("foo")).isNotNull(); + + assertThat(cacheProvider1.find("foo")).isNotSameAs(cacheProvider2.find("foo")); + + // use loader to ensures that it isn't collected before the test completes + assertThat(loader1).isNotNull(); + assertThat(loader2).isNotNull(); + } + + private static TypePool.Resolution newVoid() { + return new TypePool.Resolution.Simple(TypeDescription.ForLoadedType.of(void.class)); + } + + private static ClassLoader newClassLoader() { + return new URLClassLoader(new URL[0], null); + } +}