diff --git a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/UtilsTest.groovy b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/UtilsTest.groovy deleted file mode 100644 index 05c2cd50f50a..000000000000 --- a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/UtilsTest.groovy +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling - -import spock.lang.Specification - -class UtilsTest extends Specification { - - def "getResourceName() adds suffix and converts dots to slashes"() { - setup: - def result = Utils.getResourceName("com.example.Something") - expect: - result == "com/example/Something.class" - } - - def "getClassName() converts slashes to dots"() { - setup: - def result = Utils.getClassName("com/example/Something") - expect: - result == "com.example.Something" - } - - def "getInternalName() converts slashes to dots"() { - setup: - def result = Utils.getInternalName(UtilsTest) - expect: - result == "io/opentelemetry/javaagent/tooling/UtilsTest" - } - -} diff --git a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/LoggingFailSafeMatcherTest.groovy b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/LoggingFailSafeMatcherTest.groovy deleted file mode 100644 index 10493115c276..000000000000 --- a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/LoggingFailSafeMatcherTest.groovy +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling.bytebuddy.matcher - - -import io.opentelemetry.javaagent.tooling.bytebuddy.LoggingFailSafeMatcher -import net.bytebuddy.matcher.ElementMatcher -import spock.lang.Specification - -class LoggingFailSafeMatcherTest extends Specification { - - def mockMatcher = Mock(ElementMatcher) - - def "test matcher"() { - setup: - def matcher = new LoggingFailSafeMatcher<>(mockMatcher, "test") - - when: - def result = matcher.matches(new Object()) - - then: - 1 * mockMatcher.matches(_) >> match - result == match - - where: - match << [true, false] - } - - def "test matcher exception"() { - setup: - def matcher = new LoggingFailSafeMatcher<>(mockMatcher, "test") - - when: - def result = matcher.matches(new Object()) - - then: - 1 * mockMatcher.matches(_) >> { throw new Exception("matcher exception") } - 0 * _ - noExceptionThrown() - !result // default to false - } -} diff --git a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ThrowOnFirstElement.groovy b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ThrowOnFirstElement.groovy deleted file mode 100644 index f9012bb8d4b2..000000000000 --- a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ThrowOnFirstElement.groovy +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling.bytebuddy.matcher - -class ThrowOnFirstElement implements Iterator { - - int i = 0 - - @Override - boolean hasNext() { - return i++ < 1 - } - - @Override - Object next() { - throw new Exception("iteration exception") - } -} diff --git a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/config/MethodsConfigurationParserTest.groovy b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/config/MethodsConfigurationParserTest.groovy deleted file mode 100644 index 1fe11b29b7f4..000000000000 --- a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/config/MethodsConfigurationParserTest.groovy +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling.config - - -import spock.lang.Specification - -import static java.util.Collections.emptySet - -class MethodsConfigurationParserTest extends Specification { - - def "test configuration #value"() { - expect: - MethodsConfigurationParser.parse(value) == expected - - where: - value | expected - null | [:] - " " | [:] - "some.package.ClassName" | ["some.package.ClassName":emptySet()] - "some.package.ClassName[ , ]" | [:] - "some.package.ClassName[ , method]" | [:] - "some.package.Class\$Name[ method , ]" | ["some.package.Class\$Name": ["method"].toSet()] - "ClassName[ method1,]" | ["ClassName": ["method1"].toSet()] - "ClassName[method1 , method2]" | ["ClassName": ["method1", "method2"].toSet()] - "Class\$1[method1 ] ; Class\$2[ method2];" | ["Class\$1": ["method1"].toSet(), "Class\$2": ["method2"].toSet()] - "Duplicate[method1] ; Duplicate[method2] ;Duplicate[method3];" | ["Duplicate": ["method3"].toSet()] - } -} diff --git a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicateTest.groovy b/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicateTest.groovy deleted file mode 100644 index 9d325421691a..000000000000 --- a/javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicateTest.groovy +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling.muzzle - -import spock.lang.Specification -import spock.lang.Unroll - -class HelperClassPredicateTest extends Specification { - @Unroll - def "should collect references for #desc"() { - setup: - def predicate = new HelperClassPredicate({ it.startsWith("com.example.instrumentation.library") }) - - expect: - predicate.isHelperClass(className) - - where: - desc | className - "javaagent instrumentation class" | "io.opentelemetry.javaagent.instrumentation.some_instrumentation.Advice" - "library instrumentation class" | "io.opentelemetry.instrumentation.LibraryClass" - "additional library instrumentation class" | "com.example.instrumentation.library.ThirdPartyExternalInstrumentation" - } - - @Unroll - def "should not collect references for #desc"() { - setup: - def predicate = new HelperClassPredicate({ false }) - - expect: - !predicate.isHelperClass(className) - - where: - desc | className - "Java SDK class" | "java.util.ArrayList" - "javaagent-tooling class" | "io.opentelemetry.javaagent.tooling.Constants" - "instrumentation-api class" | "io.opentelemetry.instrumentation.api.InstrumentationVersion" - "bootstrap class" | "io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge" - } -} diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/UtilsTest.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/UtilsTest.java new file mode 100644 index 000000000000..2a124761ff74 --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/UtilsTest.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class UtilsTest { + + @Test + void getResourceName_addsSuffixAndConvertsDots() { + String result = Utils.getResourceName("com.example.Something"); + assertThat(result).isEqualTo("com/example/Something.class"); + } + + @Test + void getClassName_convertsSlashesToDots() { + String result = Utils.getClassName("com/example/Something"); + assertThat(result).isEqualTo("com.example.Something"); + } + + @Test + void getInternalName_convertsDotsToSlashes() { + String result = Utils.getInternalName(UtilsTest.class); + assertThat(result).isEqualTo("io/opentelemetry/javaagent/tooling/UtilsTest"); + } +} \ No newline at end of file diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ExtendsClassMatcherTest.java.temp b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ExtendsClassMatcherTest.java.temp new file mode 100644 index 000000000000..3149f90ca791 --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ExtendsClassMatcherTest.java.temp @@ -0,0 +1,81 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.bytebuddy.matcher; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.A; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.B; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.F; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.G; +import io.opentelemetry.javaagent.tooling.muzzle.AgentTooling; +import java.util.stream.Stream; +import net.bytebuddy.description.NamedElement; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.pool.TypePool; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.objectweb.asm.Opcodes; + +class ExtendsClassMatcherTest { + + private static TypePool typePool; + + @BeforeAll + static void setUp() { + typePool = + AgentTooling.poolStrategy() + .typePool( + AgentTooling.locationStrategy() + .classFileLocator(ExtendsClassMatcherTest.class.getClassLoader(), null), + ExtendsClassMatcherTest.class.getClassLoader()); + } + + @ParameterizedTest(name = "test matcher {0} -> {1}") + @MethodSource("testMatcherProvider") + void testMatcher(Class matcherClass, Class type, boolean expected) { + ElementMatcher matcher = named(matcherClass.getName()); + TypeDescription argument = typePool.describe(type.getName()).resolve(); + + boolean result = extendsClass(matcher).matches(argument); + + assertEquals(expected, result); + } + + private static Stream testMatcherProvider() { + return Stream.of( + Arguments.of(A.class, B.class, false), + Arguments.of(A.class, F.class, false), + Arguments.of(G.class, F.class, false), + Arguments.of(F.class, F.class, true), + Arguments.of(F.class, G.class, true)); + } + + @Test + void testTraversalExceptions() { + TypeDescription type = mock(TypeDescription.class); + TypeDescription.Generic typeGeneric = mock(TypeDescription.Generic.class); + + when(type.getModifiers()).thenReturn(Opcodes.ACC_ABSTRACT); + when(type.asGenericType()).thenReturn(typeGeneric); + when(typeGeneric.asErasure()).thenThrow(new RuntimeException("asErasure exception")); + when(type.getSuperClass()).thenThrow(new RuntimeException("getSuperClass exception")); + + boolean result = extendsClass(named(Object.class.getName())).matches(type); + + // Should default to false when exception occurs + assertFalse(result); + } +} \ No newline at end of file diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/HasInterfaceMatcherTest.java.temp b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/HasInterfaceMatcherTest.java.temp new file mode 100644 index 000000000000..4c489d3c8cf1 --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/HasInterfaceMatcherTest.java.temp @@ -0,0 +1,91 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.bytebuddy.matcher; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.A; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.B; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.E; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.F; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.G; +import io.opentelemetry.javaagent.tooling.muzzle.AgentTooling; +import java.util.stream.Stream; +import net.bytebuddy.description.NamedElement; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.description.type.TypeList; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.pool.TypePool; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class HasInterfaceMatcherTest { + + private static TypePool typePool; + + @BeforeAll + static void setUp() { + typePool = + AgentTooling.poolStrategy() + .typePool( + AgentTooling.locationStrategy() + .classFileLocator(HasInterfaceMatcherTest.class.getClassLoader(), null), + HasInterfaceMatcherTest.class.getClassLoader()); + } + + @ParameterizedTest(name = "test matcher {0} -> {1}") + @MethodSource("testMatcherProvider") + void testMatcher(Class matcherClass, Class type, boolean expected) { + ElementMatcher matcher = named(matcherClass.getName()); + TypeDescription argument = typePool.describe(type.getName()).resolve(); + + boolean result = implementsInterface(matcher).matches(argument); + + assertEquals(expected, result); + } + + private static Stream testMatcherProvider() { + return Stream.of( + Arguments.of(A.class, A.class, true), + Arguments.of(A.class, B.class, true), + Arguments.of(B.class, A.class, false), + Arguments.of(A.class, E.class, true), + Arguments.of(A.class, F.class, true), + Arguments.of(A.class, G.class, true), + Arguments.of(F.class, A.class, false), + Arguments.of(F.class, F.class, false), + Arguments.of(F.class, G.class, false)); + } + + @Test + void testTraversalExceptions() { + TypeDescription type = mock(TypeDescription.class); + TypeDescription.Generic typeGeneric = mock(TypeDescription.Generic.class); + TypeList.Generic interfaces = mock(TypeList.Generic.class); + @SuppressWarnings("unchecked") + ThrowOnFirstElement iterator = new ThrowOnFirstElement(); + + when(type.isInterface()).thenReturn(true); + when(type.asGenericType()).thenReturn(typeGeneric); + when(typeGeneric.asErasure()).thenThrow(new RuntimeException("asErasure exception")); + when(type.getInterfaces()).thenReturn(interfaces); + when(interfaces.iterator()).thenReturn(iterator); + when(type.getSuperClass()).thenThrow(new RuntimeException("getSuperClass exception")); + + boolean result = implementsInterface(named(Object.class.getName())).matches(type); + + // Should default to false when exception occurs + assertFalse(result); + } +} \ No newline at end of file diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/HasSuperMethodMatcherTest.java.temp b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/HasSuperMethodMatcherTest.java.temp new file mode 100644 index 000000000000..95916e219ef4 --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/HasSuperMethodMatcherTest.java.temp @@ -0,0 +1,81 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.bytebuddy.matcher; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperMethod; +import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; +import static net.bytebuddy.matcher.ElementMatchers.none; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.A; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.B; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.C; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.F; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.G; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.Trace; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.TracedClass; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.UntracedClass; +import java.util.stream.Stream; +import net.bytebuddy.description.method.MethodDescription; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class HasSuperMethodMatcherTest { + + @ParameterizedTest(name = "test matcher {0}.{1}") + @MethodSource("testMatcherProvider") + void testMatcher(Class type, String methodName, boolean expected) throws Exception { + MethodDescription argument = + new MethodDescription.ForLoadedMethod(type.getDeclaredMethod(methodName)); + + boolean result = hasSuperMethod(isAnnotatedWith(Trace.class)).matches(argument); + + assertEquals(expected, result); + } + + private static Stream testMatcherProvider() { + return Stream.of( + Arguments.of(A.class, "a", false), + Arguments.of(B.class, "b", true), + Arguments.of(C.class, "c", false), + Arguments.of(F.class, "f", true), + Arguments.of(G.class, "g", false), + Arguments.of(TracedClass.class, "a", true), + Arguments.of(UntracedClass.class, "a", false), + Arguments.of(UntracedClass.class, "b", true)); + } + + @Test + void testConstructorNeverMatches() { + MethodDescription method = mock(MethodDescription.class); + when(method.isConstructor()).thenReturn(true); + + boolean result = hasSuperMethod(none()).matches(method); + + assertFalse(result); + } + + @Test + void testTraversalExceptions() throws Exception { + MethodDescription method = mock(MethodDescription.class); + MethodDescription.SignatureToken sigToken = + new MethodDescription.ForLoadedMethod(A.class.getDeclaredMethod("a")).asSignatureToken(); + + when(method.isConstructor()).thenReturn(false); + when(method.asSignatureToken()).thenReturn(sigToken); + when(method.getDeclaringType()).thenReturn(null); + + boolean result = hasSuperMethod(none()).matches(method); + + // Should default to false when exception occurs + assertFalse(result); + } +} \ No newline at end of file diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ImplementsInterfaceMatcherTest.java.temp b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ImplementsInterfaceMatcherTest.java.temp new file mode 100644 index 000000000000..2246a7c2570d --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ImplementsInterfaceMatcherTest.java.temp @@ -0,0 +1,108 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.bytebuddy.matcher; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.A; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.B; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.E; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.F; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.G; +import io.opentelemetry.javaagent.tooling.muzzle.AgentTooling; +import java.util.stream.Stream; +import net.bytebuddy.description.NamedElement; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.description.type.TypeList; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.pool.TypePool; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class ImplementsInterfaceMatcherTest { + + private static TypePool typePool; + + @BeforeAll + static void setUp() { + typePool = + AgentTooling.poolStrategy() + .typePool( + AgentTooling.locationStrategy() + .classFileLocator(ImplementsInterfaceMatcherTest.class.getClassLoader(), null), + ImplementsInterfaceMatcherTest.class.getClassLoader()); + } + + @ParameterizedTest(name = "test matcher {0} -> {1}") + @MethodSource("testMatcherProvider") + void testMatcher(Class matcherClass, Class type, boolean expected) { + ElementMatcher matcher = named(matcherClass.getName()); + TypeDescription argument = typePool.describe(type.getName()).resolve(); + + boolean result = implementsInterface(matcher).matches(argument); + + assertEquals(expected, result); + } + + private static Stream testMatcherProvider() { + return Stream.of( + Arguments.of(A.class, A.class, true), + Arguments.of(A.class, B.class, true), + Arguments.of(B.class, A.class, false), + Arguments.of(A.class, E.class, true), + Arguments.of(A.class, F.class, true), + Arguments.of(A.class, G.class, true), + Arguments.of(F.class, A.class, false), + Arguments.of(F.class, F.class, false), + Arguments.of(F.class, G.class, false)); + } + + @Test + void testExceptionGettingInterfaces() { + TypeDescription type = mock(TypeDescription.class); + TypeDescription.Generic typeGeneric = mock(TypeDescription.Generic.class); + + when(type.isInterface()).thenReturn(true); + when(type.asGenericType()).thenReturn(typeGeneric); + when(typeGeneric.asErasure()).thenThrow(new RuntimeException("asErasure exception")); + when(type.getInterfaces()).thenThrow(new RuntimeException("getInterfaces exception")); + when(type.getSuperClass()).thenThrow(new RuntimeException("getSuperClass exception")); + + boolean result = implementsInterface(named(Object.class.getName())).matches(type); + + // Should default to false when exception occurs + assertFalse(result); + } + + @Test + void testTraversalExceptions() { + TypeDescription type = mock(TypeDescription.class); + TypeDescription.Generic typeGeneric = mock(TypeDescription.Generic.class); + TypeList.Generic interfaces = mock(TypeList.Generic.class); + @SuppressWarnings("unchecked") + ThrowOnFirstElement iterator = new ThrowOnFirstElement(); + + when(type.isInterface()).thenReturn(true); + when(type.asGenericType()).thenReturn(typeGeneric); + when(typeGeneric.asErasure()).thenThrow(new RuntimeException("asErasure exception")); + when(type.getInterfaces()).thenReturn(interfaces); + when(interfaces.iterator()).thenReturn(iterator); + when(type.getSuperClass()).thenThrow(new RuntimeException("getSuperClass exception")); + + boolean result = implementsInterface(named(Object.class.getName())).matches(type); + + // Should default to false when exception occurs + assertFalse(result); + } +} \ No newline at end of file diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/LoggingFailSafeMatcherTest.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/LoggingFailSafeMatcherTest.java new file mode 100644 index 000000000000..cb33d1f01f53 --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/LoggingFailSafeMatcherTest.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.bytebuddy.matcher; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.opentelemetry.javaagent.tooling.bytebuddy.LoggingFailSafeMatcher; +import net.bytebuddy.matcher.ElementMatcher; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.api.Test; + +class LoggingFailSafeMatcherTest { + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testMatcher(boolean match) { + @SuppressWarnings("unchecked") + ElementMatcher mockMatcher = mock(ElementMatcher.class); + when(mockMatcher.matches(any())).thenReturn(match); + + LoggingFailSafeMatcher matcher = new LoggingFailSafeMatcher<>(mockMatcher, "test"); + Object testObject = new Object(); + + boolean result = matcher.matches(testObject); + + assertThat(result).isEqualTo(match); + } + + @Test + void testMatcherException() { + @SuppressWarnings("unchecked") + ElementMatcher mockMatcher = mock(ElementMatcher.class); + when(mockMatcher.matches(any())).thenThrow(new RuntimeException("matcher exception")); + + LoggingFailSafeMatcher matcher = new LoggingFailSafeMatcher<>(mockMatcher, "test"); + Object testObject = new Object(); + + boolean result = matcher.matches(testObject); + + // Should default to false when exception occurs + assertThat(result).isFalse(); + } +} \ No newline at end of file diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/SafeHasSuperTypeMatcherTest.java.temp b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/SafeHasSuperTypeMatcherTest.java.temp new file mode 100644 index 000000000000..2ee0932c3300 --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/SafeHasSuperTypeMatcherTest.java.temp @@ -0,0 +1,105 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.bytebuddy.matcher; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.A; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.B; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.E; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.F; +import io.opentelemetry.javaagent.tooling.bytebuddy.matcher.testclasses.G; +import io.opentelemetry.javaagent.tooling.muzzle.AgentTooling; +import java.util.stream.Stream; +import net.bytebuddy.description.NamedElement; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.description.type.TypeList; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.pool.TypePool; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class SafeHasSuperTypeMatcherTest { + + private static TypePool typePool; + + @BeforeAll + static void setUp() { + typePool = + AgentTooling.poolStrategy() + .typePool( + AgentTooling.locationStrategy() + .classFileLocator(SafeHasSuperTypeMatcherTest.class.getClassLoader(), null), + SafeHasSuperTypeMatcherTest.class.getClassLoader()); + } + + @ParameterizedTest(name = "test matcher {0} -> {1}") + @MethodSource("testMatcherProvider") + void testMatcher(Class matcherClass, Class type, boolean expected) { + ElementMatcher matcher = named(matcherClass.getName()); + TypeDescription argument = typePool.describe(type.getName()).resolve(); + + boolean result = hasSuperType(matcher).matches(argument); + + assertEquals(expected, result); + } + + private static Stream testMatcherProvider() { + return Stream.of( + Arguments.of(A.class, A.class, true), + Arguments.of(A.class, B.class, true), + Arguments.of(B.class, A.class, false), + Arguments.of(A.class, E.class, true), + Arguments.of(A.class, F.class, true), + Arguments.of(B.class, G.class, true), + Arguments.of(F.class, A.class, false), + Arguments.of(F.class, F.class, true), + Arguments.of(F.class, G.class, true)); + } + + @Test + void testExceptionGettingInterfaces() { + TypeDescription type = mock(TypeDescription.class); + TypeDescription.Generic typeGeneric = mock(TypeDescription.Generic.class); + + when(type.asGenericType()).thenReturn(typeGeneric); + when(typeGeneric.asErasure()).thenThrow(new RuntimeException("asErasure exception")); + when(type.getInterfaces()).thenThrow(new RuntimeException("getInterfaces exception")); + when(type.getSuperClass()).thenThrow(new RuntimeException("getSuperClass exception")); + + boolean result = hasSuperType(named(Object.class.getName())).matches(type); + + // Should default to false when exception occurs + assertFalse(result); + } + + @Test + void testTraversalExceptions() { + TypeDescription type = mock(TypeDescription.class); + TypeDescription.Generic typeGeneric = mock(TypeDescription.Generic.class); + TypeList.Generic interfaces = mock(TypeList.Generic.class); + @SuppressWarnings("unchecked") + ThrowOnFirstElement iterator = new ThrowOnFirstElement(); + + when(type.getInterfaces()).thenReturn(interfaces); + when(interfaces.iterator()).thenReturn(iterator); + when(type.asGenericType()).thenReturn(typeGeneric); + when(typeGeneric.asErasure()).thenThrow(new RuntimeException("asErasure exception")); + + boolean result = hasSuperType(named(Object.class.getName())).matches(type); + + // Should default to false when exception occurs + assertFalse(result); + } +} \ No newline at end of file diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ThrowOnFirstElement.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ThrowOnFirstElement.java new file mode 100644 index 000000000000..d3d5ce8705ee --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/bytebuddy/matcher/ThrowOnFirstElement.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.bytebuddy.matcher; + +import java.util.Iterator; + +class ThrowOnFirstElement implements Iterator { + + private int i = 0; + + @Override + public boolean hasNext() { + return i++ < 1; + } + + @Override + public Object next() { + throw new RuntimeException("iteration exception"); + } +} \ No newline at end of file diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/MethodsConfigurationParserTest.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/MethodsConfigurationParserTest.java new file mode 100644 index 000000000000..0fc9daef7309 --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/MethodsConfigurationParserTest.java @@ -0,0 +1,83 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.config; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class MethodsConfigurationParserTest { + + @ParameterizedTest(name = "{0}") + @MethodSource("testConfigurationProvider") + void testConfiguration(String testName, String value, Map> expected) { + assertThat(MethodsConfigurationParser.parse(value)).isEqualTo(expected); + } + + private static Stream testConfigurationProvider() { + return Stream.of( + Arguments.of("null value", null, emptyMap()), + Arguments.of("empty string", " ", emptyMap()), + Arguments.of( + "simple class name", + "some.package.ClassName", + mapOf("some.package.ClassName", emptySet())), + Arguments.of("class with empty method list", "some.package.ClassName[ , ]", emptyMap()), + Arguments.of( + "class with invalid method", "some.package.ClassName[ , method]", emptyMap()), + Arguments.of( + "class with single method", + "some.package.Class$Name[ method , ]", + mapOf("some.package.Class$Name", singleton("method"))), + Arguments.of( + "class with trailing comma", + "ClassName[ method1,]", + mapOf("ClassName", singleton("method1"))), + Arguments.of( + "class with multiple methods", + "ClassName[method1 , method2]", + mapOf("ClassName", setOf("method1", "method2"))), + Arguments.of( + "multiple classes", + "Class$1[method1 ] ; Class$2[ method2];", + mapOfTwo("Class$1", singleton("method1"), "Class$2", singleton("method2"))), + Arguments.of( + "duplicate class names (last wins)", + "Duplicate[method1] ; Duplicate[method2] ;Duplicate[method3];", + mapOf("Duplicate", singleton("method3")))); + } + + private static Map> mapOf(String key, Set value) { + Map> map = new HashMap<>(); + map.put(key, value); + return map; + } + + private static Map> mapOfTwo(String key1, Set value1, String key2, Set value2) { + Map> map = new HashMap<>(); + map.put(key1, value1); + map.put(key2, value2); + return map; + } + + private static Set setOf(String... values) { + Set set = new HashSet<>(); + for (String value : values) { + set.add(value); + } + return set; + } +} \ No newline at end of file diff --git a/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicateTest.java b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicateTest.java new file mode 100644 index 000000000000..baa4be1f57ca --- /dev/null +++ b/javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/HelperClassPredicateTest.java @@ -0,0 +1,58 @@ +/* + * 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.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class HelperClassPredicateTest { + + @ParameterizedTest(name = "should collect references for {0}") + @MethodSource("shouldCollectReferencesProvider") + void shouldCollectReferences(String description, String className) { + HelperClassPredicate predicate = + new HelperClassPredicate(it -> it.startsWith("com.example.instrumentation.library")); + + assertThat(predicate.isHelperClass(className)).isTrue(); + } + + @ParameterizedTest(name = "should not collect references for {0}") + @MethodSource("shouldNotCollectReferencesProvider") + void shouldNotCollectReferences(String description, String className) { + HelperClassPredicate predicate = new HelperClassPredicate(it -> false); + + assertThat(predicate.isHelperClass(className)).isFalse(); + } + + private static Stream shouldCollectReferencesProvider() { + return Stream.of( + Arguments.of( + "javaagent instrumentation class", + "io.opentelemetry.javaagent.instrumentation.some_instrumentation.Advice"), + Arguments.of( + "library instrumentation class", "io.opentelemetry.instrumentation.LibraryClass"), + Arguments.of( + "additional library instrumentation class", + "com.example.instrumentation.library.ThirdPartyExternalInstrumentation")); + } + + private static Stream shouldNotCollectReferencesProvider() { + return Stream.of( + Arguments.of("Java SDK class", "java.util.ArrayList"), + Arguments.of( + "javaagent-tooling class", "io.opentelemetry.javaagent.tooling.Constants"), + Arguments.of( + "instrumentation-api class", + "io.opentelemetry.instrumentation.api.InstrumentationVersion"), + Arguments.of( + "bootstrap class", + "io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge")); + } +} \ No newline at end of file diff --git a/javaagent/src/test/groovy/io/opentelemetry/javaagent/muzzle/MuzzleBytecodeTransformTest.groovy b/javaagent/src/test/groovy/io/opentelemetry/javaagent/muzzle/MuzzleBytecodeTransformTest.groovy deleted file mode 100644 index b998f663dc16..000000000000 --- a/javaagent/src/test/groovy/io/opentelemetry/javaagent/muzzle/MuzzleBytecodeTransformTest.groovy +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.muzzle - -import io.opentelemetry.javaagent.IntegrationTestUtils -import spock.lang.Specification - -import java.lang.reflect.Field -import java.lang.reflect.Method - -class MuzzleBytecodeTransformTest extends Specification { - - def "muzzle fields added to all instrumentation"() { - setup: - List unMuzzledClasses = [] - List nonLazyFields = [] - List unInitFields = [] - def instrumentationModuleClass = IntegrationTestUtils.getAgentClassLoader().loadClass("io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule") - for (Object instrumenter : ServiceLoader.load(instrumentationModuleClass)) { - if (!instrumentationModuleClass.isAssignableFrom(instrumenter.getClass())) { - // muzzle only applies to default instrumenters - continue - } - Field f - Method m - try { - f = instrumenter.getClass().getDeclaredField("muzzleReferences") - f.setAccessible(true) - if (f.get(instrumenter) != null) { - nonLazyFields.add(instrumenter.getClass()) - } - m = instrumenter.getClass().getDeclaredMethod("getMuzzleReferences") - m.setAccessible(true) - m.invoke(instrumenter) - if (f.get(instrumenter) == null) { - unInitFields.add(instrumenter.getClass()) - } - } catch (NoSuchFieldException | NoSuchMethodException e) { - unMuzzledClasses.add(instrumenter.getClass()) - } finally { - if (null != f) { - f.setAccessible(false) - } - if (null != m) { - m.setAccessible(false) - } - } - } - expect: - unMuzzledClasses == [] - nonLazyFields == [] - unInitFields == [] - } - -} diff --git a/javaagent/src/test/java/io/opentelemetry/javaagent/muzzle/MuzzleBytecodeTransformTest.java b/javaagent/src/test/java/io/opentelemetry/javaagent/muzzle/MuzzleBytecodeTransformTest.java new file mode 100644 index 000000000000..cc9a53af187d --- /dev/null +++ b/javaagent/src/test/java/io/opentelemetry/javaagent/muzzle/MuzzleBytecodeTransformTest.java @@ -0,0 +1,66 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.muzzle; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.javaagent.IntegrationTestUtils; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; +import org.junit.jupiter.api.Test; + +class MuzzleBytecodeTransformTest { + + @Test + void muzzleFieldsAddedToAllInstrumentation() throws Exception { + List> unMuzzledClasses = new ArrayList<>(); + List> nonLazyFields = new ArrayList<>(); + List> unInitFields = new ArrayList<>(); + + Class instrumentationModuleClass = + IntegrationTestUtils.getAgentClassLoader() + .loadClass("io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule"); + + ServiceLoader serviceLoader = ServiceLoader.load(instrumentationModuleClass); + for (Object instrumenter : serviceLoader) { + if (!instrumentationModuleClass.isAssignableFrom(instrumenter.getClass())) { + // muzzle only applies to default instrumenters + continue; + } + Field field = null; + Method method = null; + try { + field = instrumenter.getClass().getDeclaredField("muzzleReferences"); + field.setAccessible(true); + if (field.get(instrumenter) != null) { + nonLazyFields.add(instrumenter.getClass()); + } + method = instrumenter.getClass().getDeclaredMethod("getMuzzleReferences"); + method.setAccessible(true); + method.invoke(instrumenter); + if (field.get(instrumenter) == null) { + unInitFields.add(instrumenter.getClass()); + } + } catch (NoSuchFieldException | NoSuchMethodException e) { + unMuzzledClasses.add(instrumenter.getClass()); + } finally { + if (field != null) { + field.setAccessible(false); + } + if (method != null) { + method.setAccessible(false); + } + } + } + + assertThat(unMuzzledClasses).isEmpty(); + assertThat(nonLazyFields).isEmpty(); + assertThat(unInitFields).isEmpty(); + } +} \ No newline at end of file