From 27f4e9f72c4acec6139ff322a1d2b1dab5e93049 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bempel Date: Fri, 30 Oct 2026 17:08:04 +0100 Subject: [PATCH] Migrate dd-java-agent groovy files to java part 1 we migrate 10 tests: - AgentLoadedIntoBootstrapTest - CustomLogManagerTest - CustomMBeanServerBuilderTest - DDInfoTest - InitializationTelemetryTest - InstrumenterUnloadTest - JMXFetchTest - ListIntegrationsTest - LogLevelTest - SecurityManagerTest Co-Authored-By: Claude Opus 5.5 --- .../agent/AgentLoadedIntoBootstrapTest.groovy | 40 ---- .../trace/agent/CustomLogManagerTest.groovy | 110 ----------- .../agent/CustomMBeanServerBuilderTest.groovy | 98 ---------- .../datadog/trace/agent/DDInfoTest.groovy | 20 -- .../agent/InitializationTelemetryTest.groovy | 101 ---------- .../trace/agent/InstrumenterUnloadTest.groovy | 51 ----- .../datadog/trace/agent/JMXFetchTest.groovy | 122 ------------ .../trace/agent/ListIntegrationsTest.groovy | 22 --- .../datadog/trace/agent/LogLevelTest.groovy | 119 ------------ .../trace/agent/SecurityManagerTest.groovy | 35 ---- .../agent/AgentLoadedIntoBootstrapTest.java | 43 +++++ .../trace/agent/CustomLogManagerTest.java | 79 ++++++++ .../agent/CustomMBeanServerBuilderTest.java | 71 +++++++ .../java/datadog/trace/agent/DDInfoTest.java | 24 +++ .../agent/InitializationTelemetryTest.java | 118 ++++++++++++ .../trace/agent/InstrumenterUnloadTest.java | 76 ++++++++ .../datadog/trace/agent/JMXFetchTest.java | 176 ++++++++++++++++++ .../trace/agent/ListIntegrationsTest.java | 34 ++++ .../datadog/trace/agent/LogLevelTest.java | 53 ++++++ .../trace/agent/SecurityManagerTest.java | 40 ++++ 20 files changed, 714 insertions(+), 718 deletions(-) delete mode 100644 dd-java-agent/src/test/groovy/datadog/trace/agent/AgentLoadedIntoBootstrapTest.groovy delete mode 100644 dd-java-agent/src/test/groovy/datadog/trace/agent/CustomLogManagerTest.groovy delete mode 100644 dd-java-agent/src/test/groovy/datadog/trace/agent/CustomMBeanServerBuilderTest.groovy delete mode 100644 dd-java-agent/src/test/groovy/datadog/trace/agent/DDInfoTest.groovy delete mode 100644 dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy delete mode 100644 dd-java-agent/src/test/groovy/datadog/trace/agent/InstrumenterUnloadTest.groovy delete mode 100644 dd-java-agent/src/test/groovy/datadog/trace/agent/JMXFetchTest.groovy delete mode 100644 dd-java-agent/src/test/groovy/datadog/trace/agent/ListIntegrationsTest.groovy delete mode 100644 dd-java-agent/src/test/groovy/datadog/trace/agent/LogLevelTest.groovy delete mode 100644 dd-java-agent/src/test/groovy/datadog/trace/agent/SecurityManagerTest.groovy create mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/AgentLoadedIntoBootstrapTest.java create mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/CustomLogManagerTest.java create mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/CustomMBeanServerBuilderTest.java create mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/DDInfoTest.java create mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/InitializationTelemetryTest.java create mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/InstrumenterUnloadTest.java create mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/JMXFetchTest.java create mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/ListIntegrationsTest.java create mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/LogLevelTest.java create mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/SecurityManagerTest.java diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/AgentLoadedIntoBootstrapTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/AgentLoadedIntoBootstrapTest.groovy deleted file mode 100644 index 04e1d1c2361..00000000000 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/AgentLoadedIntoBootstrapTest.groovy +++ /dev/null @@ -1,40 +0,0 @@ -package datadog.trace.agent - -import datadog.trace.agent.test.IntegrationTestUtils -import datadog.trace.bootstrap.AgentBootstrap -import jvmbootstraptest.AgentLoadedChecker -import jvmbootstraptest.MyClassLoaderIsNotBootstrap -import spock.lang.Specification -import spock.lang.Timeout - -@Timeout(30) -class AgentLoadedIntoBootstrapTest extends Specification { - - def "Agent loads in when separate jvm is launched"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(AgentLoadedChecker.getName() - , [] - , [] - , [:] - , true) == 0 - } - - def "AgentBootstrap is loaded not from dd-java-agent.jar"() { - setup: - def mainClassName = MyClassLoaderIsNotBootstrap.getName() - def pathToJar = IntegrationTestUtils.createJarWithClasses(mainClassName, - MyClassLoaderIsNotBootstrap, - AgentBootstrap).getPath() - - expect: - IntegrationTestUtils.runOnSeparateJvm(mainClassName - , [] - , [] - , [:] - , pathToJar as String - , true) == 0 - - cleanup: - new File(pathToJar).delete() - } -} diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/CustomLogManagerTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/CustomLogManagerTest.groovy deleted file mode 100644 index db440efe415..00000000000 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/CustomLogManagerTest.groovy +++ /dev/null @@ -1,110 +0,0 @@ -package datadog.trace.agent - -import datadog.trace.agent.test.IntegrationTestUtils -import jvmbootstraptest.LogManagerSetter -import spock.lang.Specification -import spock.lang.Timeout - -@Timeout(30) -class CustomLogManagerTest extends Specification { - - private static final String DEFAULT_LOG_LEVEL = "debug" - - // Run all tests using forked jvm because groovy has already set the global log manager - def "agent services starts up in premain with no custom log manager set"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.refresh-beans-period=1", - "-Ddd.profiling.enabled=true", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL" - ] - , [] - , [:] - , true) == 0 - } - - def "agent services startup is delayed even if configured log manager on system classpath"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.refresh-beans-period=1", - "-Ddd.profiling.enabled=true", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL", - "-Djava.util.logging.manager=jvmbootstraptest.CustomLogManager" - ] - , [] - , [:] - , true) == 0 - } - - def "agent services startup is delayed with java.util.logging.manager sysprop"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.refresh-beans-period=1", - "-Ddd.profiling.enabled=true", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL", - "-Djava.util.logging.manager=jvmbootstraptest.MissingLogManager" - ] - , [] - , [:] - , true) == 0 - } - - def "agent services startup delayed with tracer custom log manager setting"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.refresh-beans-period=1", - "-Ddd.profiling.enabled=true", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL", - "-Ddd.app.customlogmanager=true" - ] - , [] - , [:] - , true) == 0 - } - - def "agent services startup delayed with JBOSS_HOME environment variable"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.refresh-beans-period=1", - "-Ddd.profiling.enabled=true", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL", - "-Ddd.app.customjmxbuilder=false" - ] - , [] - , ["JBOSS_HOME": "/"] - , true) == 0 - } - - def "agent services startup in premain forced by customlogmanager=false"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.refresh-beans-period=1", - "-Ddd.profiling.enabled=true", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL", - "-Ddd.app.customlogmanager=false", - "-Ddd.app.customjmxbuilder=false", - "-Djava.util.logging.manager=jvmbootstraptest.CustomLogManager" - ] - , [] - , ["JBOSS_HOME": "/"] - , true) == 0 - } -} diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/CustomMBeanServerBuilderTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/CustomMBeanServerBuilderTest.groovy deleted file mode 100644 index de1a80bf163..00000000000 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/CustomMBeanServerBuilderTest.groovy +++ /dev/null @@ -1,98 +0,0 @@ -package datadog.trace.agent - -import datadog.trace.agent.test.IntegrationTestUtils -import jvmbootstraptest.MBeanServerBuilderSetter -import spock.lang.Specification -import spock.lang.Timeout - -@Timeout(30) -class CustomMBeanServerBuilderTest extends Specification { - - private static final String DEFAULT_LOG_LEVEL = "debug" - - // Run all tests using forked jvm so we try different JMX settings - def "JMXFetch starts up in premain with no custom MBeanServerBuilder set"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(MBeanServerBuilderSetter.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.refresh-beans-period=1", - "-Ddd.profiling.enabled=true", - "-Ddd.instrumentation.telemetry.enabled=false", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL" - ] - , [] - , [:] - , true) == 0 - } - - def "JMXFetch startup is delayed even if configured MBeanServerBuilder on system classpath"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(MBeanServerBuilderSetter.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.refresh-beans-period=1", - "-Ddd.profiling.enabled=true", - "-Ddd.instrumentation.telemetry.enabled=false", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL", - "-Djavax.management.builder.initial=jvmbootstraptest.CustomMBeanServerBuilder" - ] - , [] - , [:] - , true) == 0 - } - - def "JMXFetch startup is delayed with javax.management.builder.initial sysprop"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(MBeanServerBuilderSetter.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.refresh-beans-period=1", - "-Ddd.profiling.enabled=true", - "-Ddd.instrumentation.telemetry.enabled=false", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL", - "-Djavax.management.builder.initial=jvmbootstraptest.MissingMBeanServerBuilder" - ] - , [] - , [:] - , true) == 0 - } - - def "JMXFetch startup is delayed with tracer custom JMX builder setting"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(MBeanServerBuilderSetter.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.refresh-beans-period=1", - "-Ddd.profiling.enabled=true", - "-Ddd.instrumentation.telemetry.enabled=false", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL", - "-Ddd.app.customjmxbuilder=true" - ] - , [] - , [:] - , true) == 0 - } - - def "JMXFetch starts up in premain forced by customjmxbuilder=false"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(MBeanServerBuilderSetter.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.refresh-beans-period=1", - "-Ddd.profiling.enabled=true", - "-Ddd.instrumentation.telemetry.enabled=false", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL", - "-Ddd.app.customjmxbuilder=false", - "-Djavax.management.builder.initial=jvmbootstraptest.CustomMBeanServerBuilder" - ] - , [] - , [:] - , true) == 0 - } -} diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/DDInfoTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/DDInfoTest.groovy deleted file mode 100644 index 0a6eb895465..00000000000 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/DDInfoTest.groovy +++ /dev/null @@ -1,20 +0,0 @@ -package datadog.trace.agent - - -import datadog.trace.api.DDTraceApiInfo -import datadog.trace.core.DDTraceCoreInfo -import spock.lang.Specification - -class DDInfoTest extends Specification { - def "info accessible from api"() { - expect: - DDTraceApiInfo.VERSION == DDTraceCoreInfo.VERSION - - DDTraceApiInfo.VERSION != null - DDTraceApiInfo.VERSION != "" - DDTraceApiInfo.VERSION != "unknown" - DDTraceCoreInfo.VERSION != null - DDTraceCoreInfo.VERSION != "" - DDTraceCoreInfo.VERSION != "unknown" - } -} diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy deleted file mode 100644 index 6c957a4a3fd..00000000000 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/InitializationTelemetryTest.groovy +++ /dev/null @@ -1,101 +0,0 @@ -package datadog.trace.agent - -import datadog.environment.JavaVirtualMachine -import datadog.test.SimpleAgentMock -import jvmbootstraptest.InitializationTelemetryCheck -import spock.lang.IgnoreIf -import spock.lang.Specification -import spock.lang.Timeout - -@Timeout(30) -class InitializationTelemetryTest extends Specification { - @IgnoreIf(reason = "SecurityManager is permanently disabled as of JDK 24", value = { - JavaVirtualMachine.isJavaVersionAtLeast(24) - }) - def "block agent start-up"() { - // In this case, the SecurityManager blocks loading of the Premain Class, - // so the JVM is expected to terminate with an error - when: - def result = InitializationTelemetryCheck.runTestJvm(InitializationTelemetryCheck.BlockAgentLoading) - - then: - result.exitCode != 0 - // JVM fails before premain, so no telemetry output - result.telemetryJson == null - } - - def "normal start-up"() { - when: - def result = InitializationTelemetryCheck.runTestJvm(null) - - then: - result.exitCode == 0 - result.telemetryJson.contains('library_entrypoint.complete') - } - - def "test initial telemetry forwarder trace muted"() { - when: - def agent = new SimpleAgentMock().start() - def result = InitializationTelemetryCheck.runTestJvm(null, agent.port) - - then: - result.exitCode == 0 - result.telemetryJson.contains('library_entrypoint.complete') - - // Check that we have only one span related to sub-process execution, - // and it is not initial telemetry forwarder. - agent.spans.size() == 1 - def span = agent.spans.get(0) - span.name == 'command_execution' - span.resource == 'echo' - - cleanup: - agent.close() - } - - @IgnoreIf(reason = "SecurityManager is permanently disabled as of JDK 24", value = { - JavaVirtualMachine.isJavaVersionAtLeast(24) - }) - def "incomplete agent start-up"() { - // In this case, the SecurityManager blocks a custom permission that is checked by bytebuddy causing - // agent initialization to fail. However, we should catch the exception allowing the application - // to run normally. - when: - def result = InitializationTelemetryCheck.runTestJvm(InitializationTelemetryCheck.BlockByteBuddy) - - then: 'should complete successfully and catch error' - result.exitCode == 0 - !result.telemetryJson.contains('library_entrypoint.complete') - result.telemetryJson.contains('error_type:') - } - - @IgnoreIf(reason = "SecurityManager is permanently disabled as of JDK 24", value = { - JavaVirtualMachine.isJavaVersionAtLeast(24) - }) - def "block forwarder env var"() { - // In this case, the SecurityManager blocks access to the forwarder environment variable, - // so the tracer is unable to report initialization telemetry - when: - def result = InitializationTelemetryCheck.runTestJvm(InitializationTelemetryCheck.BlockForwarderEnvVar) - - then: - result.exitCode == 0 - // forwarder env var unreadable, so no telemetry output - result.telemetryJson == null - } - - @IgnoreIf(reason = "SecurityManager is permanently disabled as of JDK 24", value = { - JavaVirtualMachine.isJavaVersionAtLeast(24) - }) - def "block forwarder execution"() { - // In this case, the SecurityManager blocks access to process execution, so the tracer is - // unable to invoke the forwarder executable - when: - def result = InitializationTelemetryCheck.runTestJvm(InitializationTelemetryCheck.BlockForwarderExecution) - - then: - result.exitCode == 0 - // forwarder execution is blocked, so no telemetry output - result.telemetryJson == null - } -} diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/InstrumenterUnloadTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/InstrumenterUnloadTest.groovy deleted file mode 100644 index 140d64902e1..00000000000 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/InstrumenterUnloadTest.groovy +++ /dev/null @@ -1,51 +0,0 @@ -package datadog.trace.agent - -import datadog.trace.agent.test.IntegrationTestUtils -import jvmbootstraptest.UnloadingChecker -import spock.lang.Specification -import spock.lang.Timeout - -@Timeout(30) -class InstrumenterUnloadTest extends Specification { - - private static final String DEFAULT_LOG_LEVEL = "debug" - private static final String API_KEY = "01234567890abcdef123456789ABCDEF" - - // Run test using forked jvm - def "instrumenter and muzzle classes can be unloaded after use"() { - setup: - def testOutput = new ByteArrayOutputStream() - - when: - int returnCode = IntegrationTestUtils.runOnSeparateJvm(UnloadingChecker.getName() - , [ - "-verbose:class", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=$DEFAULT_LOG_LEVEL" - ] - , [] - , ["DD_API_KEY": API_KEY] - , new PrintStream(testOutput)) - - boolean canaryUnloaded = false - int unloadedInstrumentationCount = 0 - new ByteArrayInputStream((testOutput.toByteArray())).eachLine { - System.out.println(it) - if (it =~ /(?i)unload.*Canary/) { - canaryUnloaded = true - } - if (it =~ /(?i)unload.* datadog.trace.instrumentation./) { - unloadedInstrumentationCount++ - } - } - - if (!canaryUnloaded) { - System.out.println("WARNING: Canary class was not unloaded!") - } - - then: - returnCode == 0 - // skip check if we couldn't even unload our Canary class, as that - // indicates full GC didn't happen enough to trigger any unloading - !canaryUnloaded || unloadedInstrumentationCount > 0 - } -} diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/JMXFetchTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/JMXFetchTest.groovy deleted file mode 100644 index 50428eafa1b..00000000000 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/JMXFetchTest.groovy +++ /dev/null @@ -1,122 +0,0 @@ -package datadog.trace.agent - -import datadog.trace.agent.test.IntegrationTestUtils -import jvmbootstraptest.AgentLoadedChecker -import jvmbootstraptest.JmxStartedChecker -import spock.lang.Specification -import spock.lang.Timeout - -@Timeout(30) -class JMXFetchTest extends Specification { - DatagramSocket jmxStatsSocket - - def setup() { - jmxStatsSocket = new DatagramSocket(0) - jmxStatsSocket.setSoTimeout(30 * 1000) - } - - def cleanup() { - jmxStatsSocket.close() - } - - def "test jmxfetch"() { - when: - // verify that JMX starts and reports metrics through the given socket. - def process = IntegrationTestUtils.startOnSeparateJvm(JmxStartedChecker.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.statsd.port=${jmxStatsSocket.localPort}", - "-Ddd.writer.type=DDAgentWriter" - ] - , ["30000"] - , [:] - , System.getProperty("java.class.path")) - - def stdout = new BufferedInputStream(process.getInputStream()) - def lines = stdout.readLines() - - then: - lines.size() > 0 - lines.last() == "READY" - - when: - byte[] buf = new byte[1500] - DatagramPacket packet = new DatagramPacket(buf, buf.length) - jmxStatsSocket.receive(packet) - String received = new String(packet.getData(), 0, packet.getLength()) - - then: - received.contains("service:${JmxStartedChecker.getName()}" as String) - - cleanup: - if (process != null) { - process.destroy() - } - } - - def "Agent loads when JmxFetch is misconfigured"() { - when: - // verify the agent starts up correctly with a bogus address. - def returnCode = IntegrationTestUtils.runOnSeparateJvm(AgentLoadedChecker.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.statsd.host=example.local", - "-Ddd.writer.type=DDAgentWriter" - ] - , [] - , [:] - , true) - - then: - returnCode == 0 - } - - def "test jmxfetch config"() { - setup: - def configSettings = names.collect { - "-Ddd.jmxfetch.${it}.enabled=${enable}" - } - def testOutput = new ByteArrayOutputStream() - - when: - def returnCode = IntegrationTestUtils.runOnSeparateJvm(JmxStartedChecker.getName() - , [ - "-Ddd.jmxfetch.enabled=true", - "-Ddd.jmxfetch.start-delay=0", - "-Ddd.jmxfetch.statsd.port=${jmxStatsSocket.localPort}", - "-Ddd.trace.debug=true", - "-Ddd.writer.type=DDAgentWriter" - ] - + configSettings as List - , [] - , [:] - , new PrintStream(testOutput)) - - def actualConfig = [] - new ByteArrayInputStream((testOutput.toByteArray())).eachLine { - System.out.println(it) - def match = (it =~ 'Reading metric config resource (.*)') - if (match) { - actualConfig += match[0][1] - } - } - - then: - returnCode == 0 - actualConfig as Set == expectedConfig as Set - - where: - // spotless:off - names | enable | expectedConfig - [] | true | [] - ["tomcat"] | false | [] - ["tomcat"] | true | ["jmxfetch/datadog/trace/agent/jmxfetch/metricconfigs/tomcat.yaml"] - ["kafka"] | true | ["jmxfetch/datadog/trace/agent/jmxfetch/metricconfigs/kafka.yaml"] - ["tomcat", "kafka"] | true | ["jmxfetch/datadog/trace/agent/jmxfetch/metricconfigs/tomcat.yaml", "jmxfetch/datadog/trace/agent/jmxfetch/metricconfigs/kafka.yaml"] - ["tomcat", "kafka"] | false | [] - ["invalid"] | true | [] - // spotless:on - } -} diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/ListIntegrationsTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/ListIntegrationsTest.groovy deleted file mode 100644 index 12f609ff3d5..00000000000 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/ListIntegrationsTest.groovy +++ /dev/null @@ -1,22 +0,0 @@ -package datadog.trace.agent - -import datadog.trace.agent.test.IntegrationTestUtils -import datadog.trace.bootstrap.AgentJar -import spock.lang.Specification -import spock.lang.Timeout - -@Timeout(30) -class ListIntegrationsTest extends Specification { - - def "dd.trace.debug false"() { - setup: - def testOutput = new ByteArrayOutputStream() - expect: - IntegrationTestUtils.runOnSeparateJvm(AgentJar.name - , [] - , ["-li"] - , [:] - , new PrintStream(testOutput)) == 0 - !new String(testOutput.toByteArray()).contains("ERROR") - } -} diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/LogLevelTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/LogLevelTest.groovy deleted file mode 100644 index c5b17c7f46d..00000000000 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/LogLevelTest.groovy +++ /dev/null @@ -1,119 +0,0 @@ -package datadog.trace.agent - -import datadog.trace.agent.test.IntegrationTestUtils -import jvmbootstraptest.LogLevelChecker -import spock.lang.Specification -import spock.lang.Timeout - -@Timeout(30) -class LogLevelTest extends Specification { - - - /* Priority: datadog.slf4j.simpleLogger.defaultLogLevel > dd.trace.debug > DD_TRACE_DEBUG - 1: INFO LOGS - 0: DEBUG Logs - */ - - def "dd.trace.debug false"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogLevelChecker.getName() - , [ - "-Ddd.trace.debug=false", - "-Ddd.jmxfetch.enabled=false", - "-Ddd.trace.enabled=false" - ] - , [] - , [:] - , true) == 1 - } - - def "SLF4J DEBUG && dd.trace.debug is false"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogLevelChecker.getName() - , [ - "-Ddd.trace.debug=false", - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=debug", - "-Ddd.jmxfetch.enabled=false", - "-Ddd.trace.enabled=false" - ] - , [] - , [:] - , true) == 0 - } - - def "dd.trace.debug is false && DD_TRACE_DEBUG is true"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogLevelChecker.getName() - , [ - "-Ddd.trace.debug=false", - "-Ddd.jmxfetch.enabled=false", - "-Ddd.trace.enabled=false" - ] - , [] - , ["DD_TRACE_DEBUG": "true"] - , true) == 1 - } - - def "dd.trace.debug is true"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogLevelChecker.getName() - , [ - "-Ddd.trace.debug=true", - "-Ddd.jmxfetch.enabled=false", - "-Ddd.trace.enabled=false" - ] - , [] - , [:] - , true) == 0 - } - - - def "DD_TRACE_DEBUG is true"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogLevelChecker.getName() - , ["-Ddd.jmxfetch.enabled=false", "-Ddd.trace.enabled=false"] - , [] - , ["DD_TRACE_DEBUG": "true"] - , true) == 0 - } - - def "dd.trace.debug is true && DD_TRACE_DEBUG is false"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogLevelChecker.getName() - , [ - "-Ddd.trace.debug=true", - "-Ddd.jmxfetch.enabled=false", - "-Ddd.trace.enabled=false" - ] - , [] - , ["DD_TRACE_DEBUG": "false"] - , true) == 0 - } - - - def "SLF4J DEBUG && DD_TRACE_DEBUG is false"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogLevelChecker.getName() - , [ - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=debug", - "-Ddd.jmxfetch.enabled=false", - "-Ddd.trace.enabled=false" - ] - , [] - , ["DD_TRACE_DEBUG": "false"] - , true) == 0 - } - - def "SLF4J INFO && DD_TRACE_DEBUG is true"() { - expect: - IntegrationTestUtils.runOnSeparateJvm(LogLevelChecker.getName() - , [ - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=info", - "-Ddd.jmxfetch.enabled=false", - "-Ddd.trace.enabled=false" - ] - , [] - , ["DD_TRACE_DEBUG": "true"] - , true) == 1 - } -} diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/SecurityManagerTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/SecurityManagerTest.groovy deleted file mode 100644 index c0504ecfc56..00000000000 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/SecurityManagerTest.groovy +++ /dev/null @@ -1,35 +0,0 @@ -package datadog.trace.agent - -import datadog.environment.JavaVirtualMachine -import spock.lang.Specification -import spock.lang.Timeout -import spock.lang.IgnoreIf - -import jvmbootstraptest.SecurityManagerCheck -import jvmbootstraptest.TestSecurityManager - -@Timeout(30) -@IgnoreIf(reason = "SecurityManager is permanently disabled as of JDK 24", value = { - JavaVirtualMachine.isJavaVersionAtLeast(24) -}) -class SecurityManagerTest extends Specification { - def "no env access"() { - expect: - SecurityManagerCheck.runTestJvm(TestSecurityManager.NoEnvAccess) == 0 - } - - def "minimal property access"() { - expect: - SecurityManagerCheck.runTestJvm(TestSecurityManager.MinimalPropertyAccess, true) == 0 - } - - def "no process execution"() { - expect: - SecurityManagerCheck.runTestJvm(TestSecurityManager.NoProcessExecution) == 0 - } - - def "no network access"() { - expect: - SecurityManagerCheck.runTestJvm(TestSecurityManager.NoNetworkAccess) == 0 - } -} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/AgentLoadedIntoBootstrapTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/AgentLoadedIntoBootstrapTest.java new file mode 100644 index 00000000000..356b7213fcf --- /dev/null +++ b/dd-java-agent/src/test/java/datadog/trace/agent/AgentLoadedIntoBootstrapTest.java @@ -0,0 +1,43 @@ +package datadog.trace.agent; + +import static datadog.trace.agent.test.IntegrationTestUtils.createJarWithClasses; +import static datadog.trace.agent.test.IntegrationTestUtils.runOnSeparateJvm; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import datadog.trace.bootstrap.AgentBootstrap; +import java.io.File; +import jvmbootstraptest.AgentLoadedChecker; +import jvmbootstraptest.MyClassLoaderIsNotBootstrap; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +@Timeout(30) +class AgentLoadedIntoBootstrapTest { + + @Test + void agentLoadsInWhenSeparateJvmIsLaunched() throws Exception { + assertEquals( + 0, + runOnSeparateJvm( + AgentLoadedChecker.class.getName(), emptyList(), emptyList(), emptyMap(), true)); + } + + @Test + void agentBootstrapIsLoadedNotFromDdJavaAgentJar() throws Exception { + // setup + String mainClassName = MyClassLoaderIsNotBootstrap.class.getName(); + String pathToJar = + createJarWithClasses(mainClassName, MyClassLoaderIsNotBootstrap.class, AgentBootstrap.class) + .getPath(); + try { + assertEquals( + 0, + runOnSeparateJvm(mainClassName, emptyList(), emptyList(), emptyMap(), pathToJar, true)); + } finally { + // cleanup + new File(pathToJar).delete(); + } + } +} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/CustomLogManagerTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/CustomLogManagerTest.java new file mode 100644 index 00000000000..b490613c2af --- /dev/null +++ b/dd-java-agent/src/test/java/datadog/trace/agent/CustomLogManagerTest.java @@ -0,0 +1,79 @@ +package datadog.trace.agent; + +import static datadog.trace.agent.test.IntegrationTestUtils.runOnSeparateJvm; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import jvmbootstraptest.LogManagerSetter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +@Timeout(30) +class CustomLogManagerTest { + + private static final String DEFAULT_LOG_LEVEL = "debug"; + + // Run all tests using forked jvm because groovy has already set the global log manager + @Test + void agentServicesStartsUpInPremainWithNoCustomLogManagerSet() throws Exception { + assertEquals(0, runLogManagerSetter(emptyMap())); + } + + @Test + void agentServicesStartupIsDelayedEvenIfConfiguredLogManagerOnSystemClasspath() throws Exception { + assertEquals( + 0, + runLogManagerSetter( + emptyMap(), "-Djava.util.logging.manager=jvmbootstraptest.CustomLogManager")); + } + + @Test + void agentServicesStartupIsDelayedWithJavaUtilLoggingManagerSysprop() throws Exception { + assertEquals( + 0, + runLogManagerSetter( + emptyMap(), "-Djava.util.logging.manager=jvmbootstraptest.MissingLogManager")); + } + + @Test + void agentServicesStartupDelayedWithTracerCustomLogManagerSetting() throws Exception { + assertEquals(0, runLogManagerSetter(emptyMap(), "-Ddd.app.customlogmanager=true")); + } + + @Test + void agentServicesStartupDelayedWithJbossHomeEnvironmentVariable() throws Exception { + assertEquals( + 0, runLogManagerSetter(singletonMap("JBOSS_HOME", "/"), "-Ddd.app.customjmxbuilder=false")); + } + + @Test + void agentServicesStartupInPremainForcedByCustomLogManagerFalse() throws Exception { + assertEquals( + 0, + runLogManagerSetter( + singletonMap("JBOSS_HOME", "/"), + "-Ddd.app.customlogmanager=false", + "-Ddd.app.customjmxbuilder=false", + "-Djava.util.logging.manager=jvmbootstraptest.CustomLogManager")); + } + + private static int runLogManagerSetter(Map envVars, String... extraJvmArgs) + throws Exception { + List jvmArgs = + new ArrayList<>( + asList( + "-Ddd.jmxfetch.enabled=true", + "-Ddd.jmxfetch.start-delay=0", + "-Ddd.jmxfetch.refresh-beans-period=1", + "-Ddd.profiling.enabled=true", + "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=" + DEFAULT_LOG_LEVEL)); + jvmArgs.addAll(asList(extraJvmArgs)); + return runOnSeparateJvm(LogManagerSetter.class.getName(), jvmArgs, emptyList(), envVars, true); + } +} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/CustomMBeanServerBuilderTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/CustomMBeanServerBuilderTest.java new file mode 100644 index 00000000000..e72a8e083b4 --- /dev/null +++ b/dd-java-agent/src/test/java/datadog/trace/agent/CustomMBeanServerBuilderTest.java @@ -0,0 +1,71 @@ +package datadog.trace.agent; + +import static datadog.trace.agent.test.IntegrationTestUtils.runOnSeparateJvm; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import jvmbootstraptest.MBeanServerBuilderSetter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +@Timeout(30) +class CustomMBeanServerBuilderTest { + + private static final String DEFAULT_LOG_LEVEL = "debug"; + + // Run all tests using forked jvm so we try different JMX settings + @Test + void jmxFetchStartsUpInPremainWithNoCustomMBeanServerBuilderSet() throws Exception { + assertEquals(0, runMBeanServerBuilderSetter()); + } + + @Test + void jmxFetchStartupIsDelayedEvenIfConfiguredMBeanServerBuilderOnSystemClasspath() + throws Exception { + assertEquals( + 0, + runMBeanServerBuilderSetter( + "-Djavax.management.builder.initial=jvmbootstraptest.CustomMBeanServerBuilder")); + } + + @Test + void jmxFetchStartupIsDelayedWithJavaxManagementBuilderInitialSysprop() throws Exception { + assertEquals( + 0, + runMBeanServerBuilderSetter( + "-Djavax.management.builder.initial=jvmbootstraptest.MissingMBeanServerBuilder")); + } + + @Test + void jmxFetchStartupIsDelayedWithTracerCustomJmxBuilderSetting() throws Exception { + assertEquals(0, runMBeanServerBuilderSetter("-Ddd.app.customjmxbuilder=true")); + } + + @Test + void jmxFetchStartsUpInPremainForcedByCustomJmxBuilderFalse() throws Exception { + assertEquals( + 0, + runMBeanServerBuilderSetter( + "-Ddd.app.customjmxbuilder=false", + "-Djavax.management.builder.initial=jvmbootstraptest.CustomMBeanServerBuilder")); + } + + private static int runMBeanServerBuilderSetter(String... extraJvmArgs) throws Exception { + List jvmArgs = + new ArrayList<>( + asList( + "-Ddd.jmxfetch.enabled=true", + "-Ddd.jmxfetch.start-delay=0", + "-Ddd.jmxfetch.refresh-beans-period=1", + "-Ddd.profiling.enabled=true", + "-Ddd.instrumentation.telemetry.enabled=false", + "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=" + DEFAULT_LOG_LEVEL)); + jvmArgs.addAll(asList(extraJvmArgs)); + return runOnSeparateJvm( + MBeanServerBuilderSetter.class.getName(), jvmArgs, emptyList(), emptyMap(), true); + } +} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/DDInfoTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/DDInfoTest.java new file mode 100644 index 00000000000..f65f585e4f1 --- /dev/null +++ b/dd-java-agent/src/test/java/datadog/trace/agent/DDInfoTest.java @@ -0,0 +1,24 @@ +package datadog.trace.agent; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import datadog.trace.api.DDTraceApiInfo; +import datadog.trace.core.DDTraceCoreInfo; +import org.junit.jupiter.api.Test; + +class DDInfoTest { + + @Test + void infoAccessibleFromApi() { + assertEquals(DDTraceApiInfo.VERSION, DDTraceCoreInfo.VERSION); + + assertNotNull(DDTraceApiInfo.VERSION); + assertNotEquals("", DDTraceApiInfo.VERSION); + assertNotEquals("unknown", DDTraceApiInfo.VERSION); + assertNotNull(DDTraceCoreInfo.VERSION); + assertNotEquals("", DDTraceCoreInfo.VERSION); + assertNotEquals("unknown", DDTraceCoreInfo.VERSION); + } +} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/InitializationTelemetryTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/InitializationTelemetryTest.java new file mode 100644 index 00000000000..126bc1fc3e8 --- /dev/null +++ b/dd-java-agent/src/test/java/datadog/trace/agent/InitializationTelemetryTest.java @@ -0,0 +1,118 @@ +package datadog.trace.agent; + +import static jvmbootstraptest.InitializationTelemetryCheck.runTestJvm; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.condition.JRE.JAVA_24; + +import datadog.test.SimpleAgentMock; +import datadog.trace.test.agent.decoder.DecodedSpan; +import jvmbootstraptest.InitializationTelemetryCheck.BlockAgentLoading; +import jvmbootstraptest.InitializationTelemetryCheck.BlockByteBuddy; +import jvmbootstraptest.InitializationTelemetryCheck.BlockForwarderEnvVar; +import jvmbootstraptest.InitializationTelemetryCheck.BlockForwarderExecution; +import jvmbootstraptest.InitializationTelemetryCheck.Result; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.condition.DisabledForJreRange; + +@Timeout(30) +class InitializationTelemetryTest { + + @Test + @DisabledForJreRange( + min = JAVA_24, + disabledReason = "SecurityManager is permanently disabled as of JDK 24") + void blockAgentStartUp() throws Exception { + // In this case, the SecurityManager blocks loading of the Premain Class, + // so the JVM is expected to terminate with an error + // when + Result result = runTestJvm(BlockAgentLoading.class); + + // then + assertNotEquals(0, result.exitCode); + // JVM fails before premain, so no telemetry output + assertNull(result.telemetryJson); + } + + @Test + void normalStartUp() throws Exception { + // when + Result result = runTestJvm(null); + + // then + assertEquals(0, result.exitCode); + assertTrue(result.telemetryJson.contains("library_entrypoint.complete")); + } + + @Test + void testInitialTelemetryForwarderTraceMuted() throws Exception { + // when + try (SimpleAgentMock agent = new SimpleAgentMock().start()) { + Result result = runTestJvm(null, agent.getPort()); + + // then + assertEquals(0, result.exitCode); + assertTrue(result.telemetryJson.contains("library_entrypoint.complete")); + + // Check that we have only one span related to sub-process execution, + // and it is not initial telemetry forwarder. + assertEquals(1, agent.getSpans().size()); + DecodedSpan span = agent.getSpans().get(0); + assertEquals("command_execution", span.getName()); + assertEquals("echo", span.getResource()); + } + } + + @Test + @DisabledForJreRange( + min = JAVA_24, + disabledReason = "SecurityManager is permanently disabled as of JDK 24") + void incompleteAgentStartUp() throws Exception { + // In this case, the SecurityManager blocks a custom permission that is checked by bytebuddy + // causing agent initialization to fail. However, we should catch the exception allowing the + // application to run normally. + // when + Result result = runTestJvm(BlockByteBuddy.class); + + // then should complete successfully and catch error + assertEquals(0, result.exitCode); + assertFalse(result.telemetryJson.contains("library_entrypoint.complete")); + assertTrue(result.telemetryJson.contains("error_type:")); + } + + @Test + @DisabledForJreRange( + min = JAVA_24, + disabledReason = "SecurityManager is permanently disabled as of JDK 24") + void blockForwarderEnvVar() throws Exception { + // In this case, the SecurityManager blocks access to the forwarder environment variable, + // so the tracer is unable to report initialization telemetry + // when + Result result = runTestJvm(BlockForwarderEnvVar.class); + + // then + assertEquals(0, result.exitCode); + // forwarder env var unreadable, so no telemetry output + assertNull(result.telemetryJson); + } + + @Test + @DisabledForJreRange( + min = JAVA_24, + disabledReason = "SecurityManager is permanently disabled as of JDK 24") + void blockForwarderExecution() throws Exception { + // In this case, the SecurityManager blocks access to process execution, so the tracer is + // unable to invoke the forwarder executable + // when + Result result = runTestJvm(BlockForwarderExecution.class); + + // then + assertEquals(0, result.exitCode); + // forwarder execution is blocked, so no telemetry output + assertNull(result.telemetryJson); + } +} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/InstrumenterUnloadTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/InstrumenterUnloadTest.java new file mode 100644 index 00000000000..1d8b2576036 --- /dev/null +++ b/dd-java-agent/src/test/java/datadog/trace/agent/InstrumenterUnloadTest.java @@ -0,0 +1,76 @@ +package datadog.trace.agent; + +import static datadog.trace.agent.test.IntegrationTestUtils.runOnSeparateJvm; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonMap; +import static java.util.regex.Pattern.CASE_INSENSITIVE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.util.regex.Pattern; +import jvmbootstraptest.UnloadingChecker; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +@Timeout(30) +class InstrumenterUnloadTest { + + private static final String DEFAULT_LOG_LEVEL = "debug"; + private static final String API_KEY = "01234567890abcdef123456789ABCDEF"; + private static final Pattern CANARY_UNLOAD = Pattern.compile("unload.*Canary", CASE_INSENSITIVE); + private static final Pattern INSTRUMENTATION_UNLOAD = + Pattern.compile("unload.* datadog.trace.instrumentation.", CASE_INSENSITIVE); + + // Run test using forked jvm + @Test + void instrumenterAndMuzzleClassesCanBeUnloadedAfterUse() throws Exception { + // setup + ByteArrayOutputStream testOutput = new ByteArrayOutputStream(); + + // when + int returnCode = + runOnSeparateJvm( + UnloadingChecker.class.getName(), + asList( + "-verbose:class", + "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=" + DEFAULT_LOG_LEVEL), + emptyList(), + singletonMap("DD_API_KEY", API_KEY), + new PrintStream(testOutput)); + + boolean canaryUnloaded = false; + int unloadedInstrumentationCount = 0; + try (BufferedReader reader = + new BufferedReader( + new InputStreamReader(new ByteArrayInputStream(testOutput.toByteArray())))) { + String line; + while ((line = reader.readLine()) != null) { + System.out.println(line); + if (CANARY_UNLOAD.matcher(line).find()) { + canaryUnloaded = true; + } + if (INSTRUMENTATION_UNLOAD.matcher(line).find()) { + unloadedInstrumentationCount++; + } + } + } + + if (!canaryUnloaded) { + System.out.println("WARNING: Canary class was not unloaded!"); + } + + // then + assertEquals(0, returnCode); + // skip check if we couldn't even unload our Canary class, as that + // indicates full GC didn't happen enough to trigger any unloading + if (canaryUnloaded) { + assertTrue(unloadedInstrumentationCount > 0); + } + } +} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/JMXFetchTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/JMXFetchTest.java new file mode 100644 index 00000000000..84fb64418e5 --- /dev/null +++ b/dd-java-agent/src/test/java/datadog/trace/agent/JMXFetchTest.java @@ -0,0 +1,176 @@ +package datadog.trace.agent; + +import static datadog.trace.agent.test.IntegrationTestUtils.runOnSeparateJvm; +import static datadog.trace.agent.test.IntegrationTestUtils.startOnSeparateJvm; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jvmbootstraptest.AgentLoadedChecker; +import jvmbootstraptest.JmxStartedChecker; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.tabletest.junit.TableTest; + +@Timeout(30) +class JMXFetchTest { + + private static final String METRIC_CONFIG_PREFIX = + "jmxfetch/datadog/trace/agent/jmxfetch/metricconfigs/"; + private static final Pattern READING_METRIC_CONFIG = + Pattern.compile("Reading metric config resource (.*)"); + + private DatagramSocket jmxStatsSocket; + + @BeforeEach + void setup() throws IOException { + jmxStatsSocket = new DatagramSocket(0); + jmxStatsSocket.setSoTimeout(30 * 1000); + } + + @AfterEach + void cleanup() { + jmxStatsSocket.close(); + } + + @Test + void testJmxFetch() throws Exception { + // when + // verify that JMX starts and reports metrics through the given socket. + Process process = + startOnSeparateJvm( + JmxStartedChecker.class.getName(), + asList( + "-Ddd.jmxfetch.enabled=true", + "-Ddd.jmxfetch.start-delay=0", + "-Ddd.jmxfetch.statsd.port=" + jmxStatsSocket.getLocalPort(), + "-Ddd.writer.type=DDAgentWriter"), + singletonList("30000"), + emptyMap(), + System.getProperty("java.class.path")); + try { + List lines = readLines(process.getInputStream()); + + // then + assertFalse(lines.isEmpty()); + assertEquals("READY", lines.get(lines.size() - 1)); + + // when + byte[] buf = new byte[1500]; + DatagramPacket packet = new DatagramPacket(buf, buf.length); + jmxStatsSocket.receive(packet); + String received = new String(packet.getData(), 0, packet.getLength()); + + // then + assertTrue(received.contains("service:" + JmxStartedChecker.class.getName())); + } finally { + // cleanup + process.destroy(); + } + } + + @Test + void agentLoadsWhenJmxFetchIsMisconfigured() throws Exception { + // when + // verify the agent starts up correctly with a bogus address. + int returnCode = + runOnSeparateJvm( + AgentLoadedChecker.class.getName(), + asList( + "-Ddd.jmxfetch.enabled=true", + "-Ddd.jmxfetch.start-delay=0", + "-Ddd.jmxfetch.statsd.host=example.local", + "-Ddd.writer.type=DDAgentWriter"), + emptyList(), + emptyMap(), + true); + + // then + assertEquals(0, returnCode); + } + + @TableTest({ + "scenario | names | enable | expectedConfigs", + "no integration | [] | true | {} ", + "tomcat disabled | [tomcat] | false | {} ", + "tomcat enabled | [tomcat] | true | {tomcat} ", + "kafka enabled | [kafka] | true | {kafka} ", + "tomcat, kafka enabled | [tomcat, kafka] | true | {tomcat, kafka}", + "tomcat, kafka disabled | [tomcat, kafka] | false | {} ", + "invalid enabled | [invalid] | true | {} " + }) + void testJmxFetchConfig(List names, boolean enable, Set expectedConfigs) + throws Exception { + // setup + List jvmArgs = + new ArrayList<>( + asList( + "-Ddd.jmxfetch.enabled=true", + "-Ddd.jmxfetch.start-delay=0", + "-Ddd.jmxfetch.statsd.port=" + jmxStatsSocket.getLocalPort(), + "-Ddd.trace.debug=true", + "-Ddd.writer.type=DDAgentWriter")); + for (String name : names) { + jvmArgs.add("-Ddd.jmxfetch." + name + ".enabled=" + enable); + } + ByteArrayOutputStream testOutput = new ByteArrayOutputStream(); + + // when + int returnCode = + runOnSeparateJvm( + JmxStartedChecker.class.getName(), + jvmArgs, + emptyList(), + emptyMap(), + new PrintStream(testOutput)); + + Set actualConfig = new HashSet<>(); + for (String line : readLines(new ByteArrayInputStream(testOutput.toByteArray()))) { + System.out.println(line); + Matcher match = READING_METRIC_CONFIG.matcher(line); + if (match.find()) { + actualConfig.add(match.group(1)); + } + } + + // then + assertEquals(0, returnCode); + Set expectedConfig = new HashSet<>(); + for (String config : expectedConfigs) { + expectedConfig.add(METRIC_CONFIG_PREFIX + config + ".yaml"); + } + assertEquals(expectedConfig, actualConfig); + } + + private static List readLines(InputStream input) throws IOException { + List lines = new ArrayList<>(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(input))) { + String line; + while ((line = reader.readLine()) != null) { + lines.add(line); + } + } + return lines; + } +} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/ListIntegrationsTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/ListIntegrationsTest.java new file mode 100644 index 00000000000..d27b0c9353b --- /dev/null +++ b/dd-java-agent/src/test/java/datadog/trace/agent/ListIntegrationsTest.java @@ -0,0 +1,34 @@ +package datadog.trace.agent; + +import static datadog.trace.agent.test.IntegrationTestUtils.runOnSeparateJvm; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import datadog.trace.bootstrap.AgentJar; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +@Timeout(30) +class ListIntegrationsTest { + + @Test + void ddTraceDebugFalse() throws Exception { + // setup + ByteArrayOutputStream testOutput = new ByteArrayOutputStream(); + + assertEquals( + 0, + runOnSeparateJvm( + AgentJar.class.getName(), + emptyList(), + singletonList("-li"), + emptyMap(), + new PrintStream(testOutput))); + assertFalse(new String(testOutput.toByteArray()).contains("ERROR")); + } +} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/LogLevelTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/LogLevelTest.java new file mode 100644 index 00000000000..6a19a69ef98 --- /dev/null +++ b/dd-java-agent/src/test/java/datadog/trace/agent/LogLevelTest.java @@ -0,0 +1,53 @@ +package datadog.trace.agent; + +import static datadog.trace.agent.test.IntegrationTestUtils.runOnSeparateJvm; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import jvmbootstraptest.LogLevelChecker; +import org.junit.jupiter.api.Timeout; +import org.tabletest.junit.TableTest; + +@Timeout(30) +class LogLevelTest { + + /* Priority: datadog.slf4j.simpleLogger.defaultLogLevel > dd.trace.debug > DD_TRACE_DEBUG + 1: INFO LOGS + 0: DEBUG Logs + */ + + @TableTest({ + "scenario | traceDebug | slf4jLogLevel | envTraceDebug | expectedExitCode", + "dd.trace.debug false | false | | | 1 ", + "SLF4J DEBUG && dd.trace.debug is false | false | debug | | 0 ", + "dd.trace.debug is false && DD_TRACE_DEBUG is true | false | | true | 1 ", + "dd.trace.debug is true | true | | | 0 ", + "DD_TRACE_DEBUG is true | | | true | 0 ", + "dd.trace.debug is true && DD_TRACE_DEBUG is false | true | | false | 0 ", + "SLF4J DEBUG && DD_TRACE_DEBUG is false | | debug | false | 0 ", + "SLF4J INFO && DD_TRACE_DEBUG is true | | info | true | 1 " + }) + void logLevel(String traceDebug, String slf4jLogLevel, String envTraceDebug, int expectedExitCode) + throws Exception { + List jvmArgs = new ArrayList<>(); + if (traceDebug != null) { + jvmArgs.add("-Ddd.trace.debug=" + traceDebug); + } + if (slf4jLogLevel != null) { + jvmArgs.add("-Ddatadog.slf4j.simpleLogger.defaultLogLevel=" + slf4jLogLevel); + } + jvmArgs.add("-Ddd.jmxfetch.enabled=false"); + jvmArgs.add("-Ddd.trace.enabled=false"); + Map envVars = + envTraceDebug == null ? emptyMap() : singletonMap("DD_TRACE_DEBUG", envTraceDebug); + + assertEquals( + expectedExitCode, + runOnSeparateJvm(LogLevelChecker.class.getName(), jvmArgs, emptyList(), envVars, true)); + } +} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/SecurityManagerTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/SecurityManagerTest.java new file mode 100644 index 00000000000..99c17422ed9 --- /dev/null +++ b/dd-java-agent/src/test/java/datadog/trace/agent/SecurityManagerTest.java @@ -0,0 +1,40 @@ +package datadog.trace.agent; + +import static jvmbootstraptest.SecurityManagerCheck.runTestJvm; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.condition.JRE.JAVA_24; + +import jvmbootstraptest.TestSecurityManager.MinimalPropertyAccess; +import jvmbootstraptest.TestSecurityManager.NoEnvAccess; +import jvmbootstraptest.TestSecurityManager.NoNetworkAccess; +import jvmbootstraptest.TestSecurityManager.NoProcessExecution; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.condition.DisabledForJreRange; + +@Timeout(30) +@DisabledForJreRange( + min = JAVA_24, + disabledReason = "SecurityManager is permanently disabled as of JDK 24") +class SecurityManagerTest { + + @Test + void noEnvAccess() throws Exception { + assertEquals(0, runTestJvm(NoEnvAccess.class)); + } + + @Test + void minimalPropertyAccess() throws Exception { + assertEquals(0, runTestJvm(MinimalPropertyAccess.class, true)); + } + + @Test + void noProcessExecution() throws Exception { + assertEquals(0, runTestJvm(NoProcessExecution.class)); + } + + @Test + void noNetworkAccess() throws Exception { + assertEquals(0, runTestJvm(NoNetworkAccess.class)); + } +}