From c16de48c3c18a89b2a471b4eba391283a5d642b3 Mon Sep 17 00:00:00 2001 From: Jay DeLuca Date: Fri, 3 Jan 2025 04:03:30 -0500 Subject: [PATCH] Convert internal-class-loader tests from groovy to java (#12983) Co-authored-by: Lauri Tulmin --- .../src/test/groovy/RegressionTest.groovy | 19 ------ .../test/groovy/ResourceInjectionTest.groovy | 53 --------------- .../internal/classloader/RegressionTest.java | 24 +++++++ .../classloader/ResourceInjectionTest.java | 68 +++++++++++++++++++ 4 files changed, 92 insertions(+), 72 deletions(-) delete mode 100644 instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/groovy/RegressionTest.groovy delete mode 100644 instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/groovy/ResourceInjectionTest.groovy create mode 100644 instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/RegressionTest.java create mode 100644 instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ResourceInjectionTest.java diff --git a/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/groovy/RegressionTest.groovy b/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/groovy/RegressionTest.groovy deleted file mode 100644 index 8a4601927225..000000000000 --- a/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/groovy/RegressionTest.groovy +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification - -class RegressionTest extends AgentInstrumentationSpecification { - - // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/5155 - // loading a class that is extended/implemented by a helper class causes - // java.lang.LinkageError: loader 'app' (instance of jdk.internal.loader.ClassLoaders$AppClassLoader) attempted duplicate interface definition for org.apache.commons.lang3.function.FailableCallable - // this test verifies that the duplicate class definition LinkageError is not thrown into - // application code - def "test no duplicate class definition"() { - expect: - Class.forName("org.apache.commons.lang3.function.FailableCallable") != null - } -} diff --git a/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/groovy/ResourceInjectionTest.groovy b/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/groovy/ResourceInjectionTest.groovy deleted file mode 100644 index b5b3d799c496..000000000000 --- a/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/groovy/ResourceInjectionTest.groovy +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import org.apache.commons.lang3.SystemUtils - -import java.lang.ref.WeakReference -import java.time.Duration -import java.util.concurrent.atomic.AtomicReference - -import static io.opentelemetry.instrumentation.test.utils.GcUtils.awaitGc - -class ResourceInjectionTest extends AgentInstrumentationSpecification { - - def "resources injected to non-delegating class loader"() { - setup: - String resourceName = 'test-resources/test-resource.txt' - URL[] urls = [SystemUtils.getProtectionDomain().getCodeSource().getLocation()] - AtomicReference emptyLoader = new AtomicReference<>(new URLClassLoader(urls, (ClassLoader) null)) - - when: - def resourceUrls = emptyLoader.get().getResources(resourceName) - then: - !resourceUrls.hasMoreElements() - - when: - URLClassLoader notInjectedLoader = new URLClassLoader(urls, (ClassLoader) null) - - // this triggers resource injection - emptyLoader.get().loadClass(SystemUtils.getName()) - - resourceUrls = Collections.list(emptyLoader.get().getResources(resourceName)) - - then: - resourceUrls.size() == 2 - resourceUrls.get(0).openStream().text.trim() == 'Hello world!' - resourceUrls.get(1).openStream().text.trim() == 'Hello there' - - !notInjectedLoader.getResources(resourceName).hasMoreElements() - - when: "references to emptyLoader are gone" - emptyLoader.get().close() // cleanup - def ref = new WeakReference(emptyLoader.get()) - emptyLoader.set(null) - - awaitGc(ref, Duration.ofSeconds(10)) - - then: "HelperInjector doesn't prevent it from being collected" - null == ref.get() - } -} diff --git a/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/RegressionTest.java b/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/RegressionTest.java new file mode 100644 index 000000000000..a5b5cdd8e2a3 --- /dev/null +++ b/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/RegressionTest.java @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.internal.classloader; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.Test; + +class RegressionTest { + // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/5155 + // loading a class that is extended/implemented by a helper class causes + // java.lang.LinkageError: loader 'app' (instance of + // jdk.internal.loader.ClassLoaders$AppClassLoader) + // attempted duplicate interface definition for org.apache.commons.lang3.function.FailableCallable + // this test verifies that the duplicate class definition LinkageError is not thrown into + // application code + @Test + void noDuplicateClassDefinition() throws ClassNotFoundException { + assertThat(Class.forName("org.apache.commons.lang3.function.FailableCallable")).isNotNull(); + } +} diff --git a/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ResourceInjectionTest.java b/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ResourceInjectionTest.java new file mode 100644 index 000000000000..342d0b94567f --- /dev/null +++ b/instrumentation/internal/internal-class-loader/javaagent-integration-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ResourceInjectionTest.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.internal.classloader; + +import static io.opentelemetry.instrumentation.test.utils.GcUtils.awaitGc; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.lang.ref.WeakReference; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.commons.lang3.SystemUtils; +import org.junit.jupiter.api.Test; + +class ResourceInjectionTest { + + private static String readLine(URL url) throws Exception { + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { + return reader.readLine().trim(); + } + } + + @Test + @SuppressWarnings("UnnecessaryAsync") + void resourcesInjectedToNonDelegatingClassLoader() throws Exception { + String resourceName = "test-resources/test-resource.txt"; + URL[] urls = {SystemUtils.class.getProtectionDomain().getCodeSource().getLocation()}; + AtomicReference emptyLoader = + new AtomicReference<>(new URLClassLoader(urls, null)); + + Enumeration resourceUrls = emptyLoader.get().getResources(resourceName); + assertThat(resourceUrls.hasMoreElements()).isFalse(); + resourceUrls = null; + + URLClassLoader notInjectedLoader = new URLClassLoader(urls, null); + + // this triggers resource injection + emptyLoader.get().loadClass(SystemUtils.class.getName()); + + List resourceList = Collections.list(emptyLoader.get().getResources(resourceName)); + + assertThat(resourceList.size()).isEqualTo(2); + assertThat(readLine(resourceList.get(0))).isEqualTo("Hello world!"); + assertThat(readLine(resourceList.get(1))).isEqualTo("Hello there"); + + assertThat(notInjectedLoader.getResources(resourceName).hasMoreElements()).isFalse(); + + // references to emptyloader are gone + emptyLoader.get().close(); // cleanup + WeakReference ref = new WeakReference<>(emptyLoader.get()); + emptyLoader.set(null); + + awaitGc(ref, Duration.ofSeconds(10)); + + assertThat(ref.get()).isNull(); + } +}