From 2c219fdca830101ea2f6abd316874bec6d5fd519 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Fri, 4 Jul 2025 12:22:57 +0200 Subject: [PATCH 01/24] add `di_enabled` to settings response --- .../civisibility/config/CiVisibilitySettings.java | 11 +++++++++++ .../config/ConfigurationApiImplTest.groovy | 8 ++++---- .../trace/civisibility/config/settings-response.ftl | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CiVisibilitySettings.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CiVisibilitySettings.java index bdce3fb02b8..13e254496ca 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CiVisibilitySettings.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CiVisibilitySettings.java @@ -17,6 +17,7 @@ public class CiVisibilitySettings { false, false, false, + false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null); @@ -28,6 +29,7 @@ public class CiVisibilitySettings { private final boolean flakyTestRetriesEnabled; private final boolean impactedTestsDetectionEnabled; private final boolean knownTestsEnabled; + private final boolean failedTestReplayEnabled; private final EarlyFlakeDetectionSettings earlyFlakeDetectionSettings; private final TestManagementSettings testManagementSettings; @Nullable private final String defaultBranch; @@ -40,6 +42,7 @@ public class CiVisibilitySettings { boolean flakyTestRetriesEnabled, boolean impactedTestsDetectionEnabled, boolean knownTestsEnabled, + boolean failedTestReplayEnabled, EarlyFlakeDetectionSettings earlyFlakeDetectionSettings, TestManagementSettings testManagementSettings, @Nullable String defaultBranch) { @@ -50,6 +53,7 @@ public class CiVisibilitySettings { this.flakyTestRetriesEnabled = flakyTestRetriesEnabled; this.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled; this.knownTestsEnabled = knownTestsEnabled; + this.failedTestReplayEnabled = failedTestReplayEnabled; this.earlyFlakeDetectionSettings = earlyFlakeDetectionSettings; this.testManagementSettings = testManagementSettings; this.defaultBranch = defaultBranch; @@ -83,6 +87,10 @@ public boolean isKnownTestsEnabled() { return knownTestsEnabled; } + public boolean isFailedTestReplayEnabled() { + return failedTestReplayEnabled; + } + public EarlyFlakeDetectionSettings getEarlyFlakeDetectionSettings() { return earlyFlakeDetectionSettings; } @@ -112,6 +120,7 @@ public boolean equals(Object o) { && flakyTestRetriesEnabled == that.flakyTestRetriesEnabled && impactedTestsDetectionEnabled == that.impactedTestsDetectionEnabled && knownTestsEnabled == that.knownTestsEnabled + && failedTestReplayEnabled == that.failedTestReplayEnabled && Objects.equals(earlyFlakeDetectionSettings, that.earlyFlakeDetectionSettings) && Objects.equals(testManagementSettings, that.testManagementSettings) && Objects.equals(defaultBranch, that.defaultBranch); @@ -127,6 +136,7 @@ public int hashCode() { flakyTestRetriesEnabled, impactedTestsDetectionEnabled, knownTestsEnabled, + failedTestReplayEnabled, earlyFlakeDetectionSettings, testManagementSettings, defaultBranch); @@ -154,6 +164,7 @@ public CiVisibilitySettings fromJson(Map json) { getBoolean(json, "flaky_test_retries_enabled", false), getBoolean(json, "impacted_tests_enabled", false), getBoolean(json, "known_tests_enabled", false), + getBoolean(json, "di_enabled", false), EarlyFlakeDetectionSettings.JsonAdapter.INSTANCE.fromJson( (Map) json.get("early_flake_detection")), TestManagementSettings.JsonAdapter.INSTANCE.fromJson( diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy index 4cb3ee89c5a..f8e71d07528 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy @@ -55,10 +55,10 @@ class ConfigurationApiImplTest extends Specification { where: agentless | compression | expectedSettings - false | false | new CiVisibilitySettings(false, false, false, false, false, false, false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null) - false | true | new CiVisibilitySettings(true, true, true, true, true, true, true, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, "main") - true | false | new CiVisibilitySettings(false, true, false, true, false, true, false, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(1000, 3)], 10), new TestManagementSettings(true, 10), "master") - true | true | new CiVisibilitySettings(false, false, true, true, false, false, true, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(5000, 3), new ExecutionsByDuration(120000, 2)], 10), new TestManagementSettings(true, 20), "prod") + false | false | new CiVisibilitySettings(false, false, false, false, false, false, false, false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null) + false | true | new CiVisibilitySettings(true, true, true, true, true, true, true, true, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, "main") + true | false | new CiVisibilitySettings(false, true, false, true, false, true, false, true, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(1000, 3)], 10), new TestManagementSettings(true, 10), "master") + true | true | new CiVisibilitySettings(false, false, true, true, false, false, true, false, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(5000, 3), new ExecutionsByDuration(120000, 2)], 10), new TestManagementSettings(true, 20), "prod") } def "test skippable tests request"() { diff --git a/dd-java-agent/agent-ci-visibility/src/test/resources/datadog/trace/civisibility/config/settings-response.ftl b/dd-java-agent/agent-ci-visibility/src/test/resources/datadog/trace/civisibility/config/settings-response.ftl index f21b821434e..43f79b251b8 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/resources/datadog/trace/civisibility/config/settings-response.ftl +++ b/dd-java-agent/agent-ci-visibility/src/test/resources/datadog/trace/civisibility/config/settings-response.ftl @@ -10,6 +10,7 @@ "flaky_test_retries_enabled": ${settings.flakyTestRetriesEnabled?c}, "impacted_tests_enabled": ${settings.impactedTestsDetectionEnabled?c}, "known_tests_enabled": ${settings.knownTestsEnabled?c}, + "di_enabled": ${settings.failedTestReplayEnabled?c}, <#if settings.defaultBranch??> "default_branch": "${settings.defaultBranch}", From a911ae043a1ee9fa5a37d5e6013d8f865dd1dc54 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Fri, 4 Jul 2025 12:25:26 +0200 Subject: [PATCH 02/24] add FTR related metrics --- .../config/ConfigurationApiImpl.java | 2 ++ .../domain/AbstractTestSession.java | 3 +- .../trace/civisibility/domain/TestImpl.java | 1 + .../telemetry/CiVisibilityCountMetric.java | 6 +++- .../tag/FailedTestReplayEnabled.java | 32 +++++++++++++++++++ 5 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/tag/FailedTestReplayEnabled.java diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ConfigurationApiImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ConfigurationApiImpl.java index 76dcc1ece2e..16ba542e2a3 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ConfigurationApiImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ConfigurationApiImpl.java @@ -17,6 +17,7 @@ import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector; import datadog.trace.api.civisibility.telemetry.tag.CoverageEnabled; import datadog.trace.api.civisibility.telemetry.tag.EarlyFlakeDetectionEnabled; +import datadog.trace.api.civisibility.telemetry.tag.FailedTestReplayEnabled; import datadog.trace.api.civisibility.telemetry.tag.FlakyTestRetriesEnabled; import datadog.trace.api.civisibility.telemetry.tag.ImpactedTestsDetectionEnabled; import datadog.trace.api.civisibility.telemetry.tag.ItrEnabled; @@ -156,6 +157,7 @@ public CiVisibilitySettings getSettings(TracerEnvironment tracerEnvironment) thr settings.isKnownTestsEnabled() ? KnownTestsEnabled.TRUE : null, settings.isImpactedTestsDetectionEnabled() ? ImpactedTestsDetectionEnabled.TRUE : null, settings.getTestManagementSettings().isEnabled() ? TestManagementEnabled.TRUE : null, + settings.isFailedTestReplayEnabled() ? FailedTestReplayEnabled.SettingsMetric.TRUE : null, settings.isGitUploadRequired() ? RequireGit.TRUE : null); return settings; diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java index 51e9392732b..2e191940f52 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java @@ -130,7 +130,8 @@ public AbstractTestSession( config.isAgentlessLogSubmissionEnabled() ? AgentlessLogSubmissionEnabled.TRUE : null, CIConstants.FAIL_FAST_TEST_ORDER.equalsIgnoreCase(config.getCiVisibilityTestOrder()) ? FailFastTestOrderEnabled.TRUE - : null); + : null, + null); if (instrumentationType == InstrumentationType.MANUAL_API) { metricCollector.add(CiVisibilityCountMetric.MANUAL_API_EVENTS, 1, EventType.SESSION); diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java index 47042b7effa..13e9cfb2940 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java @@ -307,6 +307,7 @@ public void end(@Nullable Long endTime) { span.getTag(Tags.TEST_IS_RETRY) != null ? IsRetry.TRUE : null, span.getTag(Tags.TEST_HAS_FAILED_ALL_RETRIES) != null ? HasFailedAllRetries.TRUE : null, retryReason instanceof TagValue ? (TagValue) retryReason : null, + null, span.getTag(Tags.TEST_IS_RUM_ACTIVE) != null ? IsRum.TRUE : null, CIConstants.SELENIUM_BROWSER_DRIVER.equals(span.getTag(Tags.TEST_BROWSER_DRIVER)) ? BrowserDriver.SELENIUM diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java b/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java index 868ecde90b8..d214f775ac8 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java @@ -13,6 +13,7 @@ import datadog.trace.api.civisibility.telemetry.tag.EventType; import datadog.trace.api.civisibility.telemetry.tag.ExitCode; import datadog.trace.api.civisibility.telemetry.tag.FailFastTestOrderEnabled; +import datadog.trace.api.civisibility.telemetry.tag.FailedTestReplayEnabled; import datadog.trace.api.civisibility.telemetry.tag.FlakyTestRetriesEnabled; import datadog.trace.api.civisibility.telemetry.tag.GitProviderDiscrepant; import datadog.trace.api.civisibility.telemetry.tag.GitProviderExpected; @@ -53,7 +54,8 @@ public enum CiVisibilityCountMetric { Provider.class, AutoInjected.class, AgentlessLogSubmissionEnabled.class, - FailFastTestOrderEnabled.class), + FailFastTestOrderEnabled.class, + FailedTestReplayEnabled.SessionMetric.class), /** The number of events created */ EVENT_CREATED( "event_created", @@ -84,6 +86,7 @@ public enum CiVisibilityCountMetric { IsRetry.class, HasFailedAllRetries.class, RetryReason.class, + FailedTestReplayEnabled.TestMetric.class, IsRum.class, BrowserDriver.class), /** The number of successfully collected code coverages that are empty */ @@ -138,6 +141,7 @@ public enum CiVisibilityCountMetric { ImpactedTestsDetectionEnabled.class, KnownTestsEnabled.class, TestManagementEnabled.class, + FailedTestReplayEnabled.SettingsMetric.class, RequireGit.class), /** The number of requests sent to the itr skippable tests endpoint */ ITR_SKIPPABLE_TESTS_REQUEST("itr_skippable_tests.request", RequestCompressed.class), diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/tag/FailedTestReplayEnabled.java b/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/tag/FailedTestReplayEnabled.java new file mode 100644 index 00000000000..3bca7092725 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/tag/FailedTestReplayEnabled.java @@ -0,0 +1,32 @@ +package datadog.trace.api.civisibility.telemetry.tag; + +import datadog.trace.api.civisibility.telemetry.TagValue; + +public abstract class FailedTestReplayEnabled { + public enum SettingsMetric implements TagValue { + TRUE; + + @Override + public String asString() { + return "failed_test_replay_enabled:true"; + } + } + + public enum SessionMetric implements TagValue { + TRUE; + + @Override + public String asString() { + return "has_failed_test_replay:true"; + } + } + + public enum TestMetric implements TagValue { + TRUE; + + @Override + public String asString() { + return "is_failed_test_replay_enabled:true"; + } + } +} From d20159e24e33c2cce5cb2b6a488cdc54ebe74840 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Fri, 4 Jul 2025 12:58:49 +0200 Subject: [PATCH 03/24] add FTR to execution settings --- .../civisibility/config/ExecutionSettings.java | 18 +++++++++++++++++- .../config/ExecutionSettingsFactoryImpl.java | 12 ++++++++++-- .../config/ExecutionSettingsTest.groovy | 7 +++++-- .../trace/api/config/CiVisibilityConfig.java | 1 + .../main/java/datadog/trace/api/Config.java | 8 ++++++++ 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettings.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettings.java index 4d93dcf5d38..c20ce5d15db 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettings.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettings.java @@ -26,6 +26,7 @@ public class ExecutionSettings { false, false, false, + false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null, @@ -43,6 +44,7 @@ public class ExecutionSettings { private final boolean testSkippingEnabled; private final boolean flakyTestRetriesEnabled; private final boolean impactedTestsDetectionEnabled; + private final boolean failedTestReplayEnabled; @Nonnull private final EarlyFlakeDetectionSettings earlyFlakeDetectionSettings; @Nonnull private final TestManagementSettings testManagementSettings; @Nullable private final String itrCorrelationId; @@ -58,6 +60,7 @@ public ExecutionSettings( boolean testSkippingEnabled, boolean flakyTestRetriesEnabled, boolean impactedTestsDetectionEnabled, + boolean failedTestReplayEnabled, @Nonnull EarlyFlakeDetectionSettings earlyFlakeDetectionSettings, @Nonnull TestManagementSettings testManagementSettings, @Nullable String itrCorrelationId, @@ -74,6 +77,7 @@ public ExecutionSettings( this.testSkippingEnabled = testSkippingEnabled; this.flakyTestRetriesEnabled = flakyTestRetriesEnabled; this.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled; + this.failedTestReplayEnabled = failedTestReplayEnabled; this.earlyFlakeDetectionSettings = earlyFlakeDetectionSettings; this.testManagementSettings = testManagementSettings; this.itrCorrelationId = itrCorrelationId; @@ -110,6 +114,7 @@ private ExecutionSettings( boolean testSkippingEnabled, boolean flakyTestRetriesEnabled, boolean impactedTestsDetectionEnabled, + boolean failedTestReplayEnabled, @Nonnull EarlyFlakeDetectionSettings earlyFlakeDetectionSettings, @Nonnull TestManagementSettings testManagementSettings, @Nullable String itrCorrelationId, @@ -123,6 +128,7 @@ private ExecutionSettings( this.testSkippingEnabled = testSkippingEnabled; this.flakyTestRetriesEnabled = flakyTestRetriesEnabled; this.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled; + this.failedTestReplayEnabled = failedTestReplayEnabled; this.earlyFlakeDetectionSettings = earlyFlakeDetectionSettings; this.testManagementSettings = testManagementSettings; this.itrCorrelationId = itrCorrelationId; @@ -157,6 +163,10 @@ public boolean isImpactedTestsDetectionEnabled() { return impactedTestsDetectionEnabled; } + public boolean isFailedTestReplayEnabled() { + return failedTestReplayEnabled; + } + @Nonnull public EarlyFlakeDetectionSettings getEarlyFlakeDetectionSettings() { return earlyFlakeDetectionSettings; @@ -243,6 +253,7 @@ public boolean equals(Object o) { && testSkippingEnabled == that.testSkippingEnabled && flakyTestRetriesEnabled == that.flakyTestRetriesEnabled && impactedTestsDetectionEnabled == that.impactedTestsDetectionEnabled + && failedTestReplayEnabled == that.failedTestReplayEnabled && Objects.equals(earlyFlakeDetectionSettings, that.earlyFlakeDetectionSettings) && Objects.equals(testManagementSettings, that.testManagementSettings) && Objects.equals(itrCorrelationId, that.itrCorrelationId) @@ -261,6 +272,7 @@ public int hashCode() { testSkippingEnabled, flakyTestRetriesEnabled, impactedTestsDetectionEnabled, + failedTestReplayEnabled, earlyFlakeDetectionSettings, testManagementSettings, itrCorrelationId, @@ -278,6 +290,7 @@ public static class Serializer { private static final int TEST_SKIPPING_ENABLED_FLAG = 4; private static final int FLAKY_TEST_RETRIES_ENABLED_FLAG = 8; private static final int IMPACTED_TESTS_DETECTION_ENABLED_FLAG = 16; + private static final int FAILED_TEST_REPLAY_ENABLED_FLAG = 32; public static ByteBuffer serialize(ExecutionSettings settings) { datadog.trace.civisibility.ipc.serialization.Serializer s = @@ -291,7 +304,8 @@ public static ByteBuffer serialize(ExecutionSettings settings) { | (settings.flakyTestRetriesEnabled ? FLAKY_TEST_RETRIES_ENABLED_FLAG : 0) | (settings.impactedTestsDetectionEnabled ? IMPACTED_TESTS_DETECTION_ENABLED_FLAG - : 0)); + : 0) + | (settings.failedTestReplayEnabled ? FAILED_TEST_REPLAY_ENABLED_FLAG : 0)); s.write(flags); EarlyFlakeDetectionSettings.Serializer.serialize(s, settings.earlyFlakeDetectionSettings); @@ -330,6 +344,7 @@ public static ExecutionSettings deserialize(ByteBuffer buffer) { boolean testSkippingEnabled = (flags & TEST_SKIPPING_ENABLED_FLAG) != 0; boolean flakyTestRetriesEnabled = (flags & FLAKY_TEST_RETRIES_ENABLED_FLAG) != 0; boolean impactedTestsDetectionEnabled = (flags & IMPACTED_TESTS_DETECTION_ENABLED_FLAG) != 0; + boolean failedTestReplayEnabled = (flags & FAILED_TEST_REPLAY_ENABLED_FLAG) != 0; EarlyFlakeDetectionSettings earlyFlakeDetectionSettings = EarlyFlakeDetectionSettings.Serializer.deserialize(buffer); @@ -372,6 +387,7 @@ public static ExecutionSettings deserialize(ByteBuffer buffer) { testSkippingEnabled, flakyTestRetriesEnabled, impactedTestsDetectionEnabled, + failedTestReplayEnabled, earlyFlakeDetectionSettings, testManagementSettings, itrCorrelationId, diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettingsFactoryImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettingsFactoryImpl.java index 7d50d1a573b..eacedca6178 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettingsFactoryImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettingsFactoryImpl.java @@ -176,6 +176,11 @@ private Map doCreate( settings, CiVisibilitySettings::isKnownTestsEnabled, Config::isCiVisibilityKnownTestsRequestEnabled); + boolean failedTestReplayEnabled = + isFeatureEnabled( + settings, + CiVisibilitySettings::isFailedTestReplayEnabled, + Config::isCiVisibilityFailedTestReplayEnabled); TestManagementSettings testManagementSettings = getTestManagementSettings(settings); @@ -188,7 +193,8 @@ private Map doCreate( + "Impacted tests detection - {},\n" + "Known tests marking - {},\n" + "Auto test retries - {},\n" - + "Test Management - {}", + + "Test Management - {},\n" + + "Failed Test Replay - {}", repositoryRoot, tracerEnvironment.getConfigurations().getRuntimeName(), tracerEnvironment.getConfigurations().getRuntimeVersion(), @@ -200,7 +206,8 @@ private Map doCreate( impactedTestsEnabled, knownTestsRequest, flakyTestRetriesEnabled, - testManagementSettings.isEnabled()); + testManagementSettings.isEnabled(), + failedTestReplayEnabled); Future skippableTestsFuture = executor.submit(() -> getSkippableTests(tracerEnvironment, itrEnabled)); @@ -252,6 +259,7 @@ private Map doCreate( testSkippingEnabled, flakyTestRetriesEnabled, impactedTestsEnabled, + failedTestReplayEnabled, earlyFlakeDetectionEnabled ? settings.getEarlyFlakeDetectionSettings() : EarlyFlakeDetectionSettings.DEFAULT, diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ExecutionSettingsTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ExecutionSettingsTest.groovy index cd9a84e3fd8..2af232bfb1c 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ExecutionSettingsTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ExecutionSettingsTest.groovy @@ -1,7 +1,6 @@ package datadog.trace.civisibility.config import datadog.trace.api.civisibility.CIConstants -import datadog.trace.api.civisibility.config.LibraryCapability import datadog.trace.api.civisibility.config.TestFQN import datadog.trace.api.civisibility.config.TestIdentifier import datadog.trace.api.civisibility.config.TestMetadata @@ -9,7 +8,6 @@ import datadog.trace.api.config.CiVisibilityConfig import datadog.trace.civisibility.diff.LineDiff import datadog.trace.test.util.DDSpecification -import java.util.stream.Collectors import static datadog.trace.civisibility.TestUtils.lines @@ -31,6 +29,7 @@ class ExecutionSettingsTest extends DDSpecification { false, false, false, + false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null, @@ -49,6 +48,7 @@ class ExecutionSettingsTest extends DDSpecification { false, true, true, + true, new EarlyFlakeDetectionSettings(true, [], 10), new TestManagementSettings(true, 20), "", @@ -68,6 +68,7 @@ class ExecutionSettingsTest extends DDSpecification { true, false, true, + false, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(10, 20)], 10), new TestManagementSettings(true, 20), "itrCorrelationId", @@ -91,6 +92,7 @@ class ExecutionSettingsTest extends DDSpecification { true, true, true, + true, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(10, 20), new ExecutionsByDuration(30, 40)], 10), new TestManagementSettings(true, 20), "itrCorrelationId", @@ -127,6 +129,7 @@ class ExecutionSettingsTest extends DDSpecification { settingsEnabled, settingsEnabled, settingsEnabled, + settingsEnabled, earlyFlakeDetectionSettings, testManagementSettings, null, diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java index ad93aa9dfc7..e468bcf6761 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java @@ -82,6 +82,7 @@ public final class CiVisibilityConfig { public static final String TEST_MANAGEMENT_ENABLED = "test.management.enabled"; public static final String TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES = "test.management.attempt.to.fix.retries"; + public static final String TEST_FAILED_TEST_REPLAY_ENABLED = "test.failed.test.replay.enabled"; /* Git PR info */ public static final String GIT_PULL_REQUEST_BASE_BRANCH = "git.pull.request.base.branch"; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 8f1c06837ce..d0886af9fc2 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -266,6 +266,7 @@ import static datadog.trace.api.config.CiVisibilityConfig.GIT_COMMIT_HEAD_SHA; import static datadog.trace.api.config.CiVisibilityConfig.GIT_PULL_REQUEST_BASE_BRANCH; import static datadog.trace.api.config.CiVisibilityConfig.GIT_PULL_REQUEST_BASE_BRANCH_SHA; +import static datadog.trace.api.config.CiVisibilityConfig.TEST_FAILED_TEST_REPLAY_ENABLED; import static datadog.trace.api.config.CiVisibilityConfig.TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES; import static datadog.trace.api.config.CiVisibilityConfig.TEST_MANAGEMENT_ENABLED; import static datadog.trace.api.config.CiVisibilityConfig.TEST_SESSION_NAME; @@ -1002,6 +1003,7 @@ public static String getHostName() { private final String gitPullRequestBaseBranch; private final String gitPullRequestBaseBranchSha; private final String gitCommitHeadSha; + private final boolean ciVisibilityFailedTestReplayEnabled; private final boolean remoteConfigEnabled; private final boolean remoteConfigIntegrityCheckEnabled; @@ -2248,6 +2250,8 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) gitPullRequestBaseBranch = configProvider.getString(GIT_PULL_REQUEST_BASE_BRANCH); gitPullRequestBaseBranchSha = configProvider.getString(GIT_PULL_REQUEST_BASE_BRANCH_SHA); gitCommitHeadSha = configProvider.getString(GIT_COMMIT_HEAD_SHA); + ciVisibilityFailedTestReplayEnabled = + configProvider.getBoolean(TEST_FAILED_TEST_REPLAY_ENABLED, true); remoteConfigEnabled = configProvider.getBoolean( @@ -3832,6 +3836,10 @@ public Integer getCiVisibilityTestManagementAttemptToFixRetries() { return ciVisibilityTestManagementAttemptToFixRetries; } + public boolean isCiVisibilityFailedTestReplayEnabled() { + return ciVisibilityFailedTestReplayEnabled; + } + public String getGitPullRequestBaseBranch() { return gitPullRequestBaseBranch; } From c4c84d6ab74db30e8429600289e7fdfb4fd91607 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Mon, 14 Jul 2025 12:41:49 +0200 Subject: [PATCH 04/24] add basic exception replay integration in agent mode --- .../civisibility/CiVisibilitySystem.java | 4 +++ .../buildsystem/BuildSystemModuleImpl.java | 16 ++++++++++ .../exception/DefaultExceptionDebugger.java | 32 +++++++++++++++++-- .../com/datadog/debugger/sink/Snapshot.java | 13 ++++++++ .../main/java/datadog/trace/api/Config.java | 8 ++++- 5 files changed, 70 insertions(+), 3 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java index 575e9a38442..21de314fc84 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java @@ -101,6 +101,10 @@ public static void start(Instrumentation inst, SharedCommunicationObjects sco) { inst.addTransformer(new CoverageClassTransformer(instrumentationFilter)); } + if (executionSettings.isFailedTestReplayEnabled()) { + // TODO + } + CiVisibilityCoverageServices.Child coverageServices = new CiVisibilityCoverageServices.Child(services, repoServices, executionSettings); TestEventsHandlerFactory testEventsHandlerFactory = diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java index 4fc0c27f59a..f52ec297043 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java @@ -12,7 +12,9 @@ import datadog.trace.api.civisibility.domain.JavaAgent; import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector; import datadog.trace.api.config.CiVisibilityConfig; +import datadog.trace.api.config.DebuggerConfig; import datadog.trace.api.config.GeneralConfig; +import datadog.trace.api.config.RemoteConfigConfig; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; import datadog.trace.bootstrap.instrumentation.api.Tags; @@ -182,6 +184,20 @@ private Map getPropertiesPropagatedToChildProcess( Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.TEST_MANAGEMENT_ENABLED), Boolean.toString(executionSettings.getTestManagementSettings().isEnabled())); + // enable exception replay if failed test replay is enabled + if (executionSettings.isFailedTestReplayEnabled()) { + propagatedSystemProperties.put( + Strings.propertyNameToSystemPropertyName( + CiVisibilityConfig.TEST_FAILED_TEST_REPLAY_ENABLED), + "true"); + propagatedSystemProperties.put( + Strings.propertyNameToSystemPropertyName(DebuggerConfig.EXCEPTION_REPLAY_ENABLED), + "true"); + propagatedSystemProperties.put( + Strings.propertyNameToSystemPropertyName(RemoteConfigConfig.REMOTE_CONFIGURATION_ENABLED), + "true"); + } + // explicitly disable build instrumentation in child processes, // because some projects run "embedded" Maven/Gradle builds as part of their integration tests, // and we don't want to show those as if they were regular build executions diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java index 981f52208b8..5ed6ceda72b 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java @@ -9,6 +9,7 @@ import com.datadog.debugger.sink.Snapshot; import com.datadog.debugger.util.CircuitBreaker; import com.datadog.debugger.util.ExceptionHelper; +import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.DebuggerContext; import datadog.trace.bootstrap.debugger.DebuggerContext.ClassNameFilter; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -34,6 +35,10 @@ public class DefaultExceptionDebugger implements DebuggerContext.ExceptionDebugg public static final String ERROR_DEBUG_INFO_CAPTURED = "error.debug_info_captured"; public static final String SNAPSHOT_ID_TAG_FMT = DD_DEBUG_ERROR_PREFIX + "%d.snapshot_id"; + // Test Optimization / Failed Test Replay specific + public static final String TEST_DEBUG_ERROR_FILE_TAG_FMT = DD_DEBUG_ERROR_PREFIX + "%d.file"; + public static final String TEST_DEBUG_ERROR_LINE_TAG_FMT = DD_DEBUG_ERROR_PREFIX + "%d.line"; + private final ExceptionProbeManager exceptionProbeManager; private final ConfigurationUpdater configurationUpdater; private final ClassNameFilter classNameFiltering; @@ -69,7 +74,7 @@ public DefaultExceptionDebugger( @Override public void handleException(Throwable t, AgentSpan span) { - if (t instanceof Error) { + if (t instanceof Error && !Config.get().isCiVisibilityEnabled()) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Skip handling error: {}", t.toString()); } @@ -93,6 +98,7 @@ public void handleException(Throwable t, AgentSpan span) { if (exceptionProbeManager.isAlreadyInstrumented(fingerprint)) { ThrowableState state = exceptionProbeManager.getStateByThrowable(innerMostException); if (state == null) { + LOGGER.info("Unable to find state for throwable: {}", innerMostException.toString()); LOGGER.debug("Unable to find state for throwable: {}", innerMostException.toString()); return; } @@ -108,7 +114,12 @@ public void handleException(Throwable t, AgentSpan span) { exceptionProbeManager.createProbesForException( throwable.getStackTrace(), chainedExceptionIdx); if (creationResult.probesCreated > 0) { - AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); + LOGGER.info("Creating probes for: {}", t.getMessage()); + if (Config.get().isCiVisibilityEnabled()) { + applyExceptionConfiguration(fingerprint); + } else { + AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); + } break; } else { if (LOGGER.isDebugEnabled()) { @@ -147,6 +158,7 @@ private static void processSnapshotsAndSetTags( } }); } + LOGGER.info("Processing exception snapshot for: {}", t.getMessage()); boolean snapshotAssigned = false; List snapshots = state.getSnapshots(); int maxSnapshotSize = Math.min(snapshots.size(), maxCapturedFrames); @@ -166,10 +178,26 @@ private static void processSnapshotsAndSetTags( String tagName = String.format(SNAPSHOT_ID_TAG_FMT, frameIndex); span.setTag(tagName, snapshot.getId()); LOGGER.debug("add tag to span[{}]: {}: {}", span.getSpanId(), tagName, snapshot.getId()); + + StackTraceElement stackFrame = innerTrace[currentIdx]; + String fileTag = String.format(TEST_DEBUG_ERROR_FILE_TAG_FMT, frameIndex); + String lineTag = String.format(TEST_DEBUG_ERROR_LINE_TAG_FMT, frameIndex); + span.setTag(fileTag, stackFrame.getFileName()); + span.setTag(lineTag, stackFrame.getLineNumber()); + + LOGGER.debug( + "add ftr debug tags to span[{}]: {}={}, {}={}", + span.getSpanId(), + fileTag, + stackFrame.getFileName(), + lineTag, + stackFrame.getLineNumber()); + if (!state.isSnapshotSent()) { DebuggerAgent.getSink().addSnapshot(snapshot); } snapshotAssigned = true; + LOGGER.info("Capture for {}: {}", t.getMessage(), snapshots.get(i).getVariables()); } if (snapshotAssigned) { state.markAsSnapshotSent(); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java index 1de96d27c34..02877e1c424 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; /** Data class representing all data collected at a probe location */ public class Snapshot { @@ -161,6 +162,18 @@ public List getEvaluationErrors() { return evaluationErrors; } + public String getVariables() { + String variables = ""; + CapturedContext returnContext = captures.getReturn(); + if (returnContext != null) { + Map allVars = new HashMap<>(); + if (returnContext.getArguments() != null) allVars.putAll(returnContext.getArguments()); + if (returnContext.getLocals() != null) allVars.putAll(returnContext.getLocals()); + variables = allVars.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue().getStrValue()).collect(Collectors.joining(", ")); + } + return variables; + } + public void addEvaluationErrors(List errors) { if (errors == null || errors.isEmpty()) { return; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index d0886af9fc2..a8572ce2c5f 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -4009,7 +4009,13 @@ public Set getThirdPartyShadingIdentifiers() { } private String getFinalDebuggerBaseUrl() { - if (agentUrl.startsWith("unix:")) { + if (isCiVisibilityEnabled() && isCiVisibilityAgentlessEnabled()) { + String agentlessUrl = getCiVisibilityAgentlessUrl(); + if (Strings.isNotBlank(agentlessUrl)) { + return agentlessUrl; + } + return "https://http-intake.logs." + getSite(); + } else if (agentUrl.startsWith("unix:")) { // provide placeholder agent URL, in practice we'll be tunnelling over UDS return "http://" + agentHost + ":" + agentPort; } else { From ae316705c4126e56ae039c13ce8237f06eff6a48 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Tue, 22 Jul 2025 16:27:03 +0200 Subject: [PATCH 05/24] feat: headless and agentless changes --- .../java/datadog/trace/bootstrap/Agent.java | 5 +--- .../civisibility/CiVisibilitySystem.java | 2 +- .../buildsystem/BuildSystemModuleImpl.java | 3 --- .../exception/DefaultExceptionDebugger.java | 11 ++++---- .../com/datadog/debugger/sink/Snapshot.java | 13 --------- .../main/java/datadog/trace/api/Config.java | 27 ++++++++++++------- 6 files changed, 24 insertions(+), 37 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java index 2913d27c957..9641fad503b 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java @@ -577,6 +577,7 @@ public void execute() { } maybeStartAppSec(scoClass, sco); + // start before debugger to enable Failed Test Replay correctly maybeStartCiVisibility(instrumentation, scoClass, sco); // start debugger before remote config to subscribe to it before starting to poll maybeStartDebugger(instrumentation, scoClass, sco); @@ -1153,10 +1154,6 @@ && isExplicitlyDisabled(TraceInstrumentationConfig.CODE_ORIGIN_FOR_SPANS_ENABLED && isExplicitlyDisabled(DebuggerConfig.DISTRIBUTED_DEBUGGER_ENABLED)) { return; } - if (!remoteConfigEnabled) { - log.warn("Cannot enable Dynamic Instrumentation because Remote Configuration is not enabled"); - return; - } startDebuggerAgent(inst, scoClass, sco); } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java index 21de314fc84..50004983ed9 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java @@ -102,7 +102,7 @@ public static void start(Instrumentation inst, SharedCommunicationObjects sco) { } if (executionSettings.isFailedTestReplayEnabled()) { - // TODO + config.setCiVisibilityFailedTestReplayEnabled(true); } CiVisibilityCoverageServices.Child coverageServices = diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java index f52ec297043..f2bbf9c7565 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java @@ -193,9 +193,6 @@ private Map getPropertiesPropagatedToChildProcess( propagatedSystemProperties.put( Strings.propertyNameToSystemPropertyName(DebuggerConfig.EXCEPTION_REPLAY_ENABLED), "true"); - propagatedSystemProperties.put( - Strings.propertyNameToSystemPropertyName(RemoteConfigConfig.REMOTE_CONFIGURATION_ENABLED), - "true"); } // explicitly disable build instrumentation in child processes, diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java index 5ed6ceda72b..05ede1f2d68 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java @@ -74,7 +74,8 @@ public DefaultExceptionDebugger( @Override public void handleException(Throwable t, AgentSpan span) { - if (t instanceof Error && !Config.get().isCiVisibilityEnabled()) { + // CIVIS Failed Test Replay acts on errors + if (t instanceof Error && !Config.get().isCiVisibilityFailedTestReplayEnabled()) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Skip handling error: {}", t.toString()); } @@ -98,7 +99,6 @@ public void handleException(Throwable t, AgentSpan span) { if (exceptionProbeManager.isAlreadyInstrumented(fingerprint)) { ThrowableState state = exceptionProbeManager.getStateByThrowable(innerMostException); if (state == null) { - LOGGER.info("Unable to find state for throwable: {}", innerMostException.toString()); LOGGER.debug("Unable to find state for throwable: {}", innerMostException.toString()); return; } @@ -114,8 +114,9 @@ public void handleException(Throwable t, AgentSpan span) { exceptionProbeManager.createProbesForException( throwable.getStackTrace(), chainedExceptionIdx); if (creationResult.probesCreated > 0) { - LOGGER.info("Creating probes for: {}", t.getMessage()); - if (Config.get().isCiVisibilityEnabled()) { + if (Config.get().isCiVisibilityFailedTestReplayEnabled()) { + // Assume Exception Replay is working under Failed Test Replay logic, + // instrumentation applied sync for immediate test retries applyExceptionConfiguration(fingerprint); } else { AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); @@ -158,7 +159,6 @@ private static void processSnapshotsAndSetTags( } }); } - LOGGER.info("Processing exception snapshot for: {}", t.getMessage()); boolean snapshotAssigned = false; List snapshots = state.getSnapshots(); int maxSnapshotSize = Math.min(snapshots.size(), maxCapturedFrames); @@ -197,7 +197,6 @@ private static void processSnapshotsAndSetTags( DebuggerAgent.getSink().addSnapshot(snapshot); } snapshotAssigned = true; - LOGGER.info("Capture for {}: {}", t.getMessage(), snapshots.get(i).getVariables()); } if (snapshotAssigned) { state.markAsSnapshotSent(); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java index 02877e1c424..1de96d27c34 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/Snapshot.java @@ -12,7 +12,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; /** Data class representing all data collected at a probe location */ public class Snapshot { @@ -162,18 +161,6 @@ public List getEvaluationErrors() { return evaluationErrors; } - public String getVariables() { - String variables = ""; - CapturedContext returnContext = captures.getReturn(); - if (returnContext != null) { - Map allVars = new HashMap<>(); - if (returnContext.getArguments() != null) allVars.putAll(returnContext.getArguments()); - if (returnContext.getLocals() != null) allVars.putAll(returnContext.getLocals()); - variables = allVars.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue().getStrValue()).collect(Collectors.joining(", ")); - } - return variables; - } - public void addEvaluationErrors(List errors) { if (errors == null || errors.isEmpty()) { return; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index a8572ce2c5f..fdcfdf69080 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -1003,7 +1003,7 @@ public static String getHostName() { private final String gitPullRequestBaseBranch; private final String gitPullRequestBaseBranchSha; private final String gitCommitHeadSha; - private final boolean ciVisibilityFailedTestReplayEnabled; + private boolean ciVisibilityFailedTestReplayEnabled; private final boolean remoteConfigEnabled; private final boolean remoteConfigIntegrityCheckEnabled; @@ -3840,6 +3840,10 @@ public boolean isCiVisibilityFailedTestReplayEnabled() { return ciVisibilityFailedTestReplayEnabled; } + public void setCiVisibilityFailedTestReplayEnabled(boolean enabled) { + ciVisibilityFailedTestReplayEnabled = enabled; + } + public String getGitPullRequestBaseBranch() { return gitPullRequestBaseBranch; } @@ -3961,7 +3965,7 @@ public boolean isSymbolDatabaseCompressed() { } public boolean isDebuggerExceptionEnabled() { - return debuggerExceptionEnabled; + return debuggerExceptionEnabled || ciVisibilityFailedTestReplayEnabled; } public int getDebuggerMaxExceptionPerSecond() { @@ -4009,13 +4013,7 @@ public Set getThirdPartyShadingIdentifiers() { } private String getFinalDebuggerBaseUrl() { - if (isCiVisibilityEnabled() && isCiVisibilityAgentlessEnabled()) { - String agentlessUrl = getCiVisibilityAgentlessUrl(); - if (Strings.isNotBlank(agentlessUrl)) { - return agentlessUrl; - } - return "https://http-intake.logs." + getSite(); - } else if (agentUrl.startsWith("unix:")) { + if (agentUrl.startsWith("unix:")) { // provide placeholder agent URL, in practice we'll be tunnelling over UDS return "http://" + agentHost + ":" + agentPort; } else { @@ -4024,7 +4022,16 @@ private String getFinalDebuggerBaseUrl() { } public String getFinalDebuggerSnapshotUrl() { - return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; + if (isCiVisibilityFailedTestReplayEnabled() && isCiVisibilityAgentlessEnabled()) { + // Used in Failed Test Replay agentless + String agentlessUrl = getCiVisibilityAgentlessUrl(); + if (Strings.isBlank(agentlessUrl)) { + agentlessUrl = "https://http-intake.logs." + getSite(); + } + return agentlessUrl + "/api/v2/logs"; + } else { + return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; + } } public String getFinalDebuggerSymDBUrl() { From c7eeac8854253eec66266e7f48b03d278a310273 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 23 Jul 2025 16:18:06 +0200 Subject: [PATCH 06/24] fix: tests --- .../domain/buildsystem/BuildSystemModuleImpl.java | 1 - .../civisibility/CiVisibilityInstrumentationTest.groovy | 7 +++++++ .../debugger/exception/DefaultExceptionDebugger.java | 6 +++--- .../trace/instrumentation/junit4/CucumberUtils.java | 1 + .../datadog/trace/instrumentation/junit4/MUnitUtils.java | 1 + .../datadog/trace/instrumentation/junit4/JUnit4Utils.java | 1 + .../trace/instrumentation/junit5/JUnitPlatformUtils.java | 4 ++++ .../datadog/trace/instrumentation/karate/KarateUtils.java | 2 ++ .../trace/instrumentation/scalatest/ScalatestUtils.java | 1 + .../datadog/trace/instrumentation/testng/TestNGUtils.java | 5 ++++- internal-api/src/main/java/datadog/trace/api/Config.java | 2 +- .../trace/api/civisibility/config/LibraryCapability.java | 1 + 12 files changed, 26 insertions(+), 6 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java index f2bbf9c7565..ef1c5935dde 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java @@ -14,7 +14,6 @@ import datadog.trace.api.config.CiVisibilityConfig; import datadog.trace.api.config.DebuggerConfig; import datadog.trace.api.config.GeneralConfig; -import datadog.trace.api.config.RemoteConfigConfig; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; import datadog.trace.bootstrap.instrumentation.api.Tags; diff --git a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy index e4031ecdf30..f2a4f7989ec 100644 --- a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy @@ -96,6 +96,7 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { injectSysConfig(CiVisibilityConfig.TEST_MANAGEMENT_ENABLED, "true") injectSysConfig(CiVisibilityConfig.TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES, "5") injectSysConfig(CiVisibilityConfig.CIVISIBILITY_TEST_ORDER, CIConstants.FAIL_FAST_TEST_ORDER) + injectSysConfig(CiVisibilityConfig.TEST_FAILED_TEST_REPLAY_ENABLED, "true") } private static final class Settings { @@ -112,6 +113,7 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { private volatile boolean earlyFlakinessDetectionEnabled private volatile boolean impactedTestsDetectionEnabled private volatile boolean testManagementEnabled + private volatile boolean failedTestReplayEnabled } private final Settings settings = new Settings() @@ -233,6 +235,7 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { settings.itrEnabled, settings.flakyRetryEnabled, settings.impactedTestsDetectionEnabled, + settings.failedTestReplayEnabled, earlyFlakinessDetectionSettings, testManagementSettings, settings.itrEnabled ? "itrCorrelationId" : null, @@ -346,6 +349,10 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { settings.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled } + def givenFailedTestReplayEnabled(boolean failedTestReplayEnabled) { + settings.failedTestReplayEnabled = failedTestReplayEnabled + } + def assertSpansData(String testcaseName, Map replacements = [:], List ignoredTags = []) { Predicate sessionSpan = span -> span.spanType == "test_session_end" spanFilter.waitForSpan(sessionSpan, TimeUnit.SECONDS.toMillis(20)) diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java index 05ede1f2d68..00e6bf60da8 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java @@ -115,9 +115,9 @@ public void handleException(Throwable t, AgentSpan span) { throwable.getStackTrace(), chainedExceptionIdx); if (creationResult.probesCreated > 0) { if (Config.get().isCiVisibilityFailedTestReplayEnabled()) { - // Assume Exception Replay is working under Failed Test Replay logic, - // instrumentation applied sync for immediate test retries - applyExceptionConfiguration(fingerprint); + // Assume Exception Replay is working under Failed Test Replay logic, + // instrumentation applied sync for immediate test retries + applyExceptionConfiguration(fingerprint); } else { AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); } diff --git a/dd-java-agent/instrumentation/junit-4.10/cucumber-junit-4/src/main/java/datadog/trace/instrumentation/junit4/CucumberUtils.java b/dd-java-agent/instrumentation/junit-4.10/cucumber-junit-4/src/main/java/datadog/trace/instrumentation/junit4/CucumberUtils.java index d3014374988..7587e58373d 100644 --- a/dd-java-agent/instrumentation/junit-4.10/cucumber-junit-4/src/main/java/datadog/trace/instrumentation/junit4/CucumberUtils.java +++ b/dd-java-agent/instrumentation/junit-4.10/cucumber-junit-4/src/main/java/datadog/trace/instrumentation/junit4/CucumberUtils.java @@ -73,6 +73,7 @@ public static String getVersion() { LibraryCapability.TIA, LibraryCapability.ATR, LibraryCapability.EFD, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); diff --git a/dd-java-agent/instrumentation/junit-4.10/munit-junit-4/src/main/java/datadog/trace/instrumentation/junit4/MUnitUtils.java b/dd-java-agent/instrumentation/junit-4.10/munit-junit-4/src/main/java/datadog/trace/instrumentation/junit4/MUnitUtils.java index cb0755ffe0d..3ba001c3ba4 100644 --- a/dd-java-agent/instrumentation/junit-4.10/munit-junit-4/src/main/java/datadog/trace/instrumentation/junit4/MUnitUtils.java +++ b/dd-java-agent/instrumentation/junit-4.10/munit-junit-4/src/main/java/datadog/trace/instrumentation/junit4/MUnitUtils.java @@ -27,6 +27,7 @@ public abstract class MUnitUtils { LibraryCapability.ATR, LibraryCapability.EFD, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.ATTEMPT_TO_FIX); diff --git a/dd-java-agent/instrumentation/junit-4.10/src/main/java/datadog/trace/instrumentation/junit4/JUnit4Utils.java b/dd-java-agent/instrumentation/junit-4.10/src/main/java/datadog/trace/instrumentation/junit4/JUnit4Utils.java index 2351d58a076..9b46dc9b351 100644 --- a/dd-java-agent/instrumentation/junit-4.10/src/main/java/datadog/trace/instrumentation/junit4/JUnit4Utils.java +++ b/dd-java-agent/instrumentation/junit-4.10/src/main/java/datadog/trace/instrumentation/junit4/JUnit4Utils.java @@ -65,6 +65,7 @@ public abstract class JUnit4Utils { LibraryCapability.ATR, LibraryCapability.EFD, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); diff --git a/dd-java-agent/instrumentation/junit-5.3/src/main/java/datadog/trace/instrumentation/junit5/JUnitPlatformUtils.java b/dd-java-agent/instrumentation/junit-5.3/src/main/java/datadog/trace/instrumentation/junit5/JUnitPlatformUtils.java index d8435812082..d2b62499aa1 100644 --- a/dd-java-agent/instrumentation/junit-5.3/src/main/java/datadog/trace/instrumentation/junit5/JUnitPlatformUtils.java +++ b/dd-java-agent/instrumentation/junit-5.3/src/main/java/datadog/trace/instrumentation/junit5/JUnitPlatformUtils.java @@ -59,6 +59,7 @@ public abstract class JUnitPlatformUtils { LibraryCapability.ATR, LibraryCapability.EFD, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); @@ -69,6 +70,7 @@ public abstract class JUnitPlatformUtils { LibraryCapability.ATR, LibraryCapability.EFD, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX, @@ -80,6 +82,7 @@ public abstract class JUnitPlatformUtils { LibraryCapability.ATR, LibraryCapability.EFD, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); @@ -89,6 +92,7 @@ public abstract class JUnitPlatformUtils { LibraryCapability.TIA, LibraryCapability.ATR, LibraryCapability.EFD, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); diff --git a/dd-java-agent/instrumentation/karate/src/main/java/datadog/trace/instrumentation/karate/KarateUtils.java b/dd-java-agent/instrumentation/karate/src/main/java/datadog/trace/instrumentation/karate/KarateUtils.java index 3fc6348aa6e..72ac6775cbe 100644 --- a/dd-java-agent/instrumentation/karate/src/main/java/datadog/trace/instrumentation/karate/KarateUtils.java +++ b/dd-java-agent/instrumentation/karate/src/main/java/datadog/trace/instrumentation/karate/KarateUtils.java @@ -56,6 +56,7 @@ private KarateUtils() {} Arrays.asList( LibraryCapability.ATR, LibraryCapability.EFD, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.ATTEMPT_TO_FIX); @@ -63,6 +64,7 @@ private KarateUtils() {} Arrays.asList( LibraryCapability.ATR, LibraryCapability.EFD, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.ATTEMPT_TO_FIX, LibraryCapability.TIA, diff --git a/dd-java-agent/instrumentation/scalatest/src/main/java/datadog/trace/instrumentation/scalatest/ScalatestUtils.java b/dd-java-agent/instrumentation/scalatest/src/main/java/datadog/trace/instrumentation/scalatest/ScalatestUtils.java index 140b276244a..c6be5171199 100644 --- a/dd-java-agent/instrumentation/scalatest/src/main/java/datadog/trace/instrumentation/scalatest/ScalatestUtils.java +++ b/dd-java-agent/instrumentation/scalatest/src/main/java/datadog/trace/instrumentation/scalatest/ScalatestUtils.java @@ -24,6 +24,7 @@ public abstract class ScalatestUtils { LibraryCapability.EFD, LibraryCapability.ATR, LibraryCapability.IMPACTED, + LibraryCapability.FTR, LibraryCapability.QUARANTINE, LibraryCapability.DISABLED, LibraryCapability.ATTEMPT_TO_FIX); diff --git a/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java b/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java index b29bdae6d25..8a012d69270 100644 --- a/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java +++ b/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java @@ -282,7 +282,10 @@ public static List capabilities(String version) { List baseCapabilities = new ArrayList<>( Arrays.asList( - LibraryCapability.TIA, LibraryCapability.IMPACTED, LibraryCapability.DISABLED)); + LibraryCapability.TIA, + LibraryCapability.IMPACTED, + LibraryCapability.FTR, + LibraryCapability.DISABLED)); boolean isEFDSupported = isEFDSupported(version); boolean isExceptionSuppressionSupported = isExceptionSuppressionSupported(version); diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index fd99dcae99e..f3853485e08 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -2296,7 +2296,7 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) gitPullRequestBaseBranchSha = configProvider.getString(GIT_PULL_REQUEST_BASE_BRANCH_SHA); gitCommitHeadSha = configProvider.getString(GIT_COMMIT_HEAD_SHA); ciVisibilityFailedTestReplayEnabled = - configProvider.getBoolean(TEST_FAILED_TEST_REPLAY_ENABLED, true); + configProvider.getBoolean(TEST_FAILED_TEST_REPLAY_ENABLED, false); remoteConfigEnabled = configProvider.getBoolean( diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/config/LibraryCapability.java b/internal-api/src/main/java/datadog/trace/api/civisibility/config/LibraryCapability.java index f4ed209ae30..9087e7d5783 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/config/LibraryCapability.java +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/config/LibraryCapability.java @@ -6,6 +6,7 @@ public enum LibraryCapability { ATR("auto_test_retries", "1"), IMPACTED("impacted_tests", "1"), FAIL_FAST("fail_fast_test_order", "1"), + FTR("failed_test_replay", "1"), QUARANTINE("test_management.quarantine", "1"), DISABLED("test_management.disable", "1"), ATTEMPT_TO_FIX("test_management.attempt_to_fix", "5"); From 14cf11c43da99090b4aae1a95bc3d532a7b88d49 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Mon, 4 Aug 2025 10:21:14 +0200 Subject: [PATCH 07/24] fix: testng capabilities --- .../datadog/trace/instrumentation/testng/TestNGUtils.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java b/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java index 8a012d69270..c67da143832 100644 --- a/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java +++ b/dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java @@ -282,15 +282,13 @@ public static List capabilities(String version) { List baseCapabilities = new ArrayList<>( Arrays.asList( - LibraryCapability.TIA, - LibraryCapability.IMPACTED, - LibraryCapability.FTR, - LibraryCapability.DISABLED)); + LibraryCapability.TIA, LibraryCapability.IMPACTED, LibraryCapability.DISABLED)); boolean isEFDSupported = isEFDSupported(version); boolean isExceptionSuppressionSupported = isExceptionSuppressionSupported(version); if (isExceptionSuppressionSupported) { baseCapabilities.add(LibraryCapability.ATR); + baseCapabilities.add(LibraryCapability.FTR); baseCapabilities.add(LibraryCapability.QUARANTINE); } if (isEFDSupported) { From 461fb1f24eddb78b36b4b21cc88a33e6b8585068 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 6 Aug 2025 09:42:39 +0200 Subject: [PATCH 08/24] feat: refactor agentless intakes --- .../communication/BackendApiFactory.java | 45 ++-------------- .../civisibility/CiVisibilityServices.java | 3 +- .../config/ConfigurationApiImplTest.groovy | 4 +- .../trace/logging/intake/LogsWriterImpl.java | 4 +- .../main/java/datadog/trace/api/Config.java | 14 ++--- .../trace/api/intake/AgentlessIntake.java | 52 +++++++++++++++++++ 6 files changed, 69 insertions(+), 53 deletions(-) create mode 100644 internal-api/src/main/java/datadog/trace/api/intake/AgentlessIntake.java diff --git a/communication/src/main/java/datadog/communication/BackendApiFactory.java b/communication/src/main/java/datadog/communication/BackendApiFactory.java index f3382792baa..9ff1d78aba9 100644 --- a/communication/src/main/java/datadog/communication/BackendApiFactory.java +++ b/communication/src/main/java/datadog/communication/BackendApiFactory.java @@ -5,8 +5,8 @@ import datadog.communication.http.HttpRetryPolicy; import datadog.communication.http.OkHttpUtils; import datadog.trace.api.Config; +import datadog.trace.api.intake.AgentlessIntake; import datadog.trace.util.throwable.FatalAgentMisconfigurationError; -import java.util.function.Function; import javax.annotation.Nullable; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; @@ -25,11 +25,11 @@ public BackendApiFactory(Config config, SharedCommunicationObjects sharedCommuni this.sharedCommunicationObjects = sharedCommunicationObjects; } - public @Nullable BackendApi createBackendApi(Intake intake) { + public @Nullable BackendApi createBackendApi(AgentlessIntake intake) { HttpRetryPolicy.Factory retryPolicyFactory = new HttpRetryPolicy.Factory(5, 100, 2.0, true); - if (intake.agentlessModeEnabled.apply(config)) { - HttpUrl agentlessUrl = getAgentlessUrl(intake); + if (intake.isAgentlessEnabled(config)) { + HttpUrl agentlessUrl = HttpUrl.get(intake.getAgentlessUrl(config)); String apiKey = config.getApiKey(); if (apiKey == null || apiKey.isEmpty()) { throw new FatalAgentMisconfigurationError( @@ -58,41 +58,4 @@ public BackendApiFactory(Config config, SharedCommunicationObjects sharedCommuni + "and agent does not support EVP proxy"); return null; } - - private HttpUrl getAgentlessUrl(Intake intake) { - String customUrl = intake.customUrl.apply(config); - if (customUrl != null && !customUrl.isEmpty()) { - return HttpUrl.get(String.format("%s/api/%s/", customUrl, intake.version)); - } else { - String site = config.getSite(); - return HttpUrl.get( - String.format("https://%s.%s/api/%s/", intake.urlPrefix, site, intake.version)); - } - } - - public enum Intake { - API("api", "v2", Config::isCiVisibilityAgentlessEnabled, Config::getCiVisibilityAgentlessUrl), - LLMOBS_API("api", "v2", Config::isLlmObsAgentlessEnabled, Config::getLlMObsAgentlessUrl), - LOGS( - "http-intake.logs", - "v2", - Config::isAgentlessLogSubmissionEnabled, - Config::getAgentlessLogSubmissionUrl); - - public final String urlPrefix; - public final String version; - public final Function agentlessModeEnabled; - public final Function customUrl; - - Intake( - String urlPrefix, - String version, - Function agentlessModeEnabled, - Function customUrl) { - this.urlPrefix = urlPrefix; - this.version = version; - this.agentlessModeEnabled = agentlessModeEnabled; - this.customUrl = customUrl; - } - } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java index f46986f31a5..29948bf87b7 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java @@ -34,6 +34,7 @@ import datadog.trace.civisibility.source.LinesResolver; import datadog.trace.civisibility.source.index.*; import datadog.trace.civisibility.utils.ShellCommandExecutor; +import datadog.trace.api.intake.AgentlessIntake; import java.io.File; import java.lang.reflect.Type; import java.net.InetSocketAddress; @@ -84,7 +85,7 @@ public class CiVisibilityServices { this.config = config; this.metricCollector = metricCollector; this.backendApi = - new BackendApiFactory(config, sco).createBackendApi(BackendApiFactory.Intake.API); + new BackendApiFactory(config, sco).createBackendApi(AgentlessIntake.API); this.jvmInfoFactory = new CachingJvmInfoFactory(config, new JvmInfoFactoryImpl()); this.gitClientFactory = buildGitClientFactory(config, metricCollector); diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy index 2236988953c..92c19ca631c 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy @@ -1,7 +1,6 @@ package datadog.trace.civisibility.config import datadog.communication.BackendApi -import datadog.communication.BackendApiFactory import datadog.communication.EvpProxyApi import datadog.communication.IntakeApi import datadog.communication.http.HttpRetryPolicy @@ -12,6 +11,7 @@ import datadog.trace.api.civisibility.config.TestIdentifier import datadog.trace.api.civisibility.config.TestMetadata import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector import datadog.trace.civisibility.CiVisibilityTestUtils +import datadog.trace.api.intake.AgentlessIntake import okhttp3.HttpUrl import okhttp3.OkHttpClient import org.apache.commons.io.IOUtils @@ -273,7 +273,7 @@ class ConfigurationApiImplTest extends Specification { } private BackendApi givenIntakeApi(URI address, boolean responseCompression) { - HttpUrl intakeUrl = HttpUrl.get(String.format("%s/api/%s/", address.toString(), BackendApiFactory.Intake.API.version)) + HttpUrl intakeUrl = HttpUrl.get(String.format("%s/api/%s/", address.toString(), AgentlessIntake.API.getVersion())) String apiKey = "api-key" String traceId = "a-trace-id" diff --git a/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java b/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java index 44ef25c7c00..0f3d20b50c8 100644 --- a/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java +++ b/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java @@ -4,10 +4,10 @@ import datadog.communication.BackendApi; import datadog.communication.BackendApiFactory; -import datadog.communication.BackendApiFactory.Intake; import datadog.trace.api.Config; import datadog.trace.api.logging.intake.LogsWriter; import datadog.trace.util.AgentThreadFactory; +import datadog.trace.api.intake.AgentlessIntake; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -87,7 +87,7 @@ public void log(Map message) { } private void logPollingLoop() { - BackendApi backendApi = apiFactory.createBackendApi(Intake.LOGS); + BackendApi backendApi = apiFactory.createBackendApi(AgentlessIntake.LOGS); LogsDispatcher logsDispatcher = new LogsDispatcher(backendApi); while (!Thread.currentThread().isInterrupted()) { diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index f3853485e08..24da3c9fad5 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -640,6 +640,7 @@ import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.IastDetectionMode; import datadog.trace.api.iast.telemetry.Verbosity; +import datadog.trace.api.intake.AgentlessIntake; import datadog.trace.api.naming.SpanNaming; import datadog.trace.api.profiling.ProfilingEnablement; import datadog.trace.api.rum.RumInjectorConfig; @@ -4110,19 +4111,18 @@ private String getFinalDebuggerBaseUrl() { public String getFinalDebuggerSnapshotUrl() { if (isCiVisibilityFailedTestReplayEnabled() && isCiVisibilityAgentlessEnabled()) { - // Used in Failed Test Replay agentless - String agentlessUrl = getCiVisibilityAgentlessUrl(); - if (Strings.isBlank(agentlessUrl)) { - agentlessUrl = "https://http-intake.logs." + getSite(); - } - return agentlessUrl + "/api/v2/logs"; + return AgentlessIntake.LOGS.getAgentlessUrl(this) + "logs"; } else { return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; } } public String getFinalDebuggerSymDBUrl() { - return getFinalDebuggerBaseUrl() + "/symdb/v1/input"; + if (isCiVisibilityFailedTestReplayEnabled() && isCiVisibilityAgentlessEnabled()) { + return AgentlessIntake.LOGS.getAgentlessUrl(this) + "logs"; + } else { + return getFinalDebuggerBaseUrl() + "/symdb/v1/input"; + } } public String getDynamicInstrumentationProbeFile() { diff --git a/internal-api/src/main/java/datadog/trace/api/intake/AgentlessIntake.java b/internal-api/src/main/java/datadog/trace/api/intake/AgentlessIntake.java new file mode 100644 index 00000000000..ce870f9339f --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/intake/AgentlessIntake.java @@ -0,0 +1,52 @@ +package datadog.trace.api.intake; + +import datadog.trace.api.Config; +import java.util.function.Function; + +public enum AgentlessIntake { + API("api", "v2", Config::isCiVisibilityAgentlessEnabled, Config::getCiVisibilityAgentlessUrl), + LLMOBS_API("api", "v2", Config::isLlmObsAgentlessEnabled, Config::getLlMObsAgentlessUrl), + LOGS( + "http-intake.logs", + "v2", + Config::isAgentlessLogSubmissionEnabled, + Config::getAgentlessLogSubmissionUrl); + + public final String urlPrefix; + public final String version; + public final Function agentlessModeEnabled; + public final Function customUrl; + + AgentlessIntake( + String urlPrefix, + String version, + Function agentlessModeEnabled, + Function customUrl) { + this.urlPrefix = urlPrefix; + this.version = version; + this.agentlessModeEnabled = agentlessModeEnabled; + this.customUrl = customUrl; + } + + public String getUrlPrefix() { + return urlPrefix; + } + + public String getVersion() { + return version; + } + + public boolean isAgentlessEnabled(Config config) { + return agentlessModeEnabled.apply(config); + } + + public String getAgentlessUrl(Config config) { + String custom = customUrl.apply(config); + if (custom != null && !custom.isEmpty()) { + return String.format("%s/api/%s/", custom, version); + } else { + String site = config.getSite(); + return String.format("https://%s.%s/api/%s/", urlPrefix, site, version); + } + } +} From a87eff039462720dc0db5d664b094ad6fa8be05b Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 6 Aug 2025 10:10:07 +0200 Subject: [PATCH 09/24] chore: update smoke test fixtures --- .../test/resources/test-failed-flaky-retries/events.ftl | 5 +++++ .../test-failed-legacy-instrumentation/events.ftl | 1 + .../resources/test-failed-new-instrumentation/events.ftl | 1 + .../resources/test-succeed-gradle-plugin-test/events.ftl | 3 ++- .../src/test/resources/test-succeed-junit-5/events.ftl | 2 ++ .../test-succeed-legacy-instrumentation/events.ftl | 2 ++ .../events.ftl | 2 ++ .../events.ftl | 2 ++ .../events.ftl | 2 ++ .../events.ftl | 2 ++ .../resources/test-succeed-new-instrumentation/events.ftl | 2 ++ .../src/test/resources/test-succeed-old-gradle/events.ftl | 2 ++ .../test_failed_maven_run_flaky_retries/events.ftl | 5 +++++ .../test/resources/test_successful_maven_run/events.ftl | 2 ++ .../test_successful_maven_run_builtin_coverage/events.ftl | 2 ++ .../events.ftl | 1 + .../events.ftl | 3 ++- .../test_successful_maven_run_multiple_forks/events.ftl | 2 ++ .../test_successful_maven_run_surefire_3_0_0/events.ftl | 2 ++ .../test_successful_maven_run_surefire_3_5_0/events.ftl | 2 ++ .../test_successful_maven_run_test_management/events.ftl | 7 +++++++ .../events.ftl | 1 + .../test_successful_maven_run_with_cucumber/events.ftl | 1 + .../events.ftl | 2 ++ 24 files changed, 54 insertions(+), 2 deletions(-) diff --git a/dd-smoke-tests/gradle/src/test/resources/test-failed-flaky-retries/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-failed-flaky-retries/events.ftl index 8fb894266ae..89322d79f42 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-failed-flaky-retries/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-failed-flaky-retries/events.ftl @@ -241,6 +241,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -303,6 +304,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -367,6 +369,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -431,6 +434,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -495,6 +499,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/gradle/src/test/resources/test-failed-legacy-instrumentation/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-failed-legacy-instrumentation/events.ftl index 686f52c3af4..25b6900a1c9 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-failed-legacy-instrumentation/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-failed-legacy-instrumentation/events.ftl @@ -189,6 +189,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/gradle/src/test/resources/test-failed-new-instrumentation/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-failed-new-instrumentation/events.ftl index f60c9131589..2a1c6b15c38 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-failed-new-instrumentation/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-failed-new-instrumentation/events.ftl @@ -241,6 +241,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/gradle/src/test/resources/test-succeed-gradle-plugin-test/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-succeed-gradle-plugin-test/events.ftl index 527ee76d9dc..165a708f84f 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-succeed-gradle-plugin-test/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-succeed-gradle-plugin-test/events.ftl @@ -236,6 +236,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -464,4 +465,4 @@ }, "type" : "span", "version" : 1 -} ] +} ] \ No newline at end of file diff --git a/dd-smoke-tests/gradle/src/test/resources/test-succeed-junit-5/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-succeed-junit-5/events.ftl index d5db1127f85..8be83d86374 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-succeed-junit-5/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-succeed-junit-5/events.ftl @@ -238,6 +238,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -299,6 +300,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/gradle/src/test/resources/test-succeed-legacy-instrumentation/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-succeed-legacy-instrumentation/events.ftl index 9c28dbea433..1ba73ff6574 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-succeed-legacy-instrumentation/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-succeed-legacy-instrumentation/events.ftl @@ -187,6 +187,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -247,6 +248,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-forks-legacy-instrumentation/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-forks-legacy-instrumentation/events.ftl index 3e09ab667c8..4ac79928d3c 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-forks-legacy-instrumentation/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-forks-legacy-instrumentation/events.ftl @@ -187,6 +187,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -296,6 +297,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-forks-new-instrumentation/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-forks-new-instrumentation/events.ftl index a36672985a7..b80115a0c1e 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-forks-new-instrumentation/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-forks-new-instrumentation/events.ftl @@ -236,6 +236,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -345,6 +346,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-module-legacy-instrumentation/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-module-legacy-instrumentation/events.ftl index 999f7f04106..8c8b2158165 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-module-legacy-instrumentation/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-module-legacy-instrumentation/events.ftl @@ -193,6 +193,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -253,6 +254,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-module-new-instrumentation/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-module-new-instrumentation/events.ftl index 85a0f8abdad..3193d2dafd7 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-module-new-instrumentation/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-succeed-multi-module-new-instrumentation/events.ftl @@ -242,6 +242,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -302,6 +303,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/gradle/src/test/resources/test-succeed-new-instrumentation/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-succeed-new-instrumentation/events.ftl index 5cb427be12e..48269e43118 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-succeed-new-instrumentation/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-succeed-new-instrumentation/events.ftl @@ -237,6 +237,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -297,6 +298,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/gradle/src/test/resources/test-succeed-old-gradle/events.ftl b/dd-smoke-tests/gradle/src/test/resources/test-succeed-old-gradle/events.ftl index 5111b76f994..d5d26e85e58 100644 --- a/dd-smoke-tests/gradle/src/test/resources/test-succeed-old-gradle/events.ftl +++ b/dd-smoke-tests/gradle/src/test/resources/test-succeed-old-gradle/events.ftl @@ -187,6 +187,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -247,6 +248,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_run_flaky_retries/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_run_flaky_retries/events.ftl index 3663a2bd36e..0f15d93fddd 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_run_flaky_retries/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_run_flaky_retries/events.ftl @@ -308,6 +308,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -371,6 +372,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -436,6 +438,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -501,6 +504,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -566,6 +570,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run/events.ftl index 4c68a894536..3d1ee989665 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run/events.ftl @@ -312,6 +312,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -373,6 +374,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_builtin_coverage/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_builtin_coverage/events.ftl index 5f7cce4a596..f72903d44ea 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_builtin_coverage/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_builtin_coverage/events.ftl @@ -276,6 +276,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -337,6 +338,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_child_service_propagation/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_child_service_propagation/events.ftl index 3b8e1dbfb0f..e1fbc9b86bb 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_child_service_propagation/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_child_service_propagation/events.ftl @@ -274,6 +274,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_junit_platform_runner/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_junit_platform_runner/events.ftl index 822de2307e4..4233a8678f5 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_junit_platform_runner/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_junit_platform_runner/events.ftl @@ -328,6 +328,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -378,4 +379,4 @@ }, "type" : "test", "version" : 2 -} ] +} ] \ No newline at end of file diff --git a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_multiple_forks/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_multiple_forks/events.ftl index 5b1e5a20fa6..9d0971cf8f8 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_multiple_forks/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_multiple_forks/events.ftl @@ -312,6 +312,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -373,6 +374,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_surefire_3_0_0/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_surefire_3_0_0/events.ftl index 4c68a894536..3d1ee989665 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_surefire_3_0_0/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_surefire_3_0_0/events.ftl @@ -312,6 +312,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -373,6 +374,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_surefire_3_5_0/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_surefire_3_5_0/events.ftl index 4c68a894536..3d1ee989665 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_surefire_3_5_0/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_surefire_3_5_0/events.ftl @@ -312,6 +312,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -373,6 +374,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_test_management/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_test_management/events.ftl index 8add90e5a8d..5c517b22379 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_test_management/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_test_management/events.ftl @@ -276,6 +276,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -341,6 +342,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -408,6 +410,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -475,6 +478,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -542,6 +546,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -611,6 +616,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -723,6 +729,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_arg_line_property/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_arg_line_property/events.ftl index efac85e2237..fdd829eef04 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_arg_line_property/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_arg_line_property/events.ftl @@ -268,6 +268,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", diff --git a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_cucumber/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_cucumber/events.ftl index fa8d74caf57..065610b1b8a 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_cucumber/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_cucumber/events.ftl @@ -365,6 +365,7 @@ "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", "_dd.library_capabilities.test_management.disable" : "1", diff --git a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_jacoco_and_argline/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_jacoco_and_argline/events.ftl index 5d23ecff637..9fa02ca2a7b 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_jacoco_and_argline/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_with_jacoco_and_argline/events.ftl @@ -312,6 +312,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", @@ -373,6 +374,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.early_flake_detection" : "1", "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", "_dd.library_capabilities.impacted_tests" : "1", "_dd.library_capabilities.test_impact_analysis" : "1", "_dd.library_capabilities.test_management.attempt_to_fix" : "5", From 19b4edfc6b0da03b42f54f8ad2d5f54a66192af6 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 6 Aug 2025 10:30:50 +0200 Subject: [PATCH 10/24] test: add unit test for new Intake enum --- .../communication/BackendApiFactory.java | 4 +-- .../civisibility/CiVisibilityServices.java | 4 +-- .../config/ConfigurationApiImplTest.groovy | 4 +-- .../trace/logging/intake/LogsWriterImpl.java | 4 +-- .../main/java/datadog/trace/api/Config.java | 6 ++-- .../{AgentlessIntake.java => Intake.java} | 4 +-- .../trace/api/intake/IntakeTest.groovy | 34 +++++++++++++++++++ 7 files changed, 47 insertions(+), 13 deletions(-) rename internal-api/src/main/java/datadog/trace/api/intake/{AgentlessIntake.java => Intake.java} (96%) create mode 100644 internal-api/src/test/groovy/datadog/trace/api/intake/IntakeTest.groovy diff --git a/communication/src/main/java/datadog/communication/BackendApiFactory.java b/communication/src/main/java/datadog/communication/BackendApiFactory.java index 9ff1d78aba9..5cea26600de 100644 --- a/communication/src/main/java/datadog/communication/BackendApiFactory.java +++ b/communication/src/main/java/datadog/communication/BackendApiFactory.java @@ -5,7 +5,7 @@ import datadog.communication.http.HttpRetryPolicy; import datadog.communication.http.OkHttpUtils; import datadog.trace.api.Config; -import datadog.trace.api.intake.AgentlessIntake; +import datadog.trace.api.intake.Intake; import datadog.trace.util.throwable.FatalAgentMisconfigurationError; import javax.annotation.Nullable; import okhttp3.HttpUrl; @@ -25,7 +25,7 @@ public BackendApiFactory(Config config, SharedCommunicationObjects sharedCommuni this.sharedCommunicationObjects = sharedCommunicationObjects; } - public @Nullable BackendApi createBackendApi(AgentlessIntake intake) { + public @Nullable BackendApi createBackendApi(Intake intake) { HttpRetryPolicy.Factory retryPolicyFactory = new HttpRetryPolicy.Factory(5, 100, 2.0, true); if (intake.isAgentlessEnabled(config)) { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java index cb09dff879a..cc9003f7fc1 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java @@ -34,7 +34,7 @@ import datadog.trace.civisibility.source.LinesResolver; import datadog.trace.civisibility.source.index.*; import datadog.trace.civisibility.utils.ShellCommandExecutor; -import datadog.trace.api.intake.AgentlessIntake; +import datadog.trace.api.intake.Intake; import java.io.File; import java.lang.reflect.Type; import java.net.InetSocketAddress; @@ -85,7 +85,7 @@ public class CiVisibilityServices { this.config = config; this.metricCollector = metricCollector; this.backendApi = - new BackendApiFactory(config, sco).createBackendApi(AgentlessIntake.API); + new BackendApiFactory(config, sco).createBackendApi(Intake.API); this.jvmInfoFactory = new CachingJvmInfoFactory(config, new JvmInfoFactoryImpl()); this.gitClientFactory = buildGitClientFactory(config, metricCollector); diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy index 92c19ca631c..0c55870a6eb 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/ConfigurationApiImplTest.groovy @@ -11,7 +11,7 @@ import datadog.trace.api.civisibility.config.TestIdentifier import datadog.trace.api.civisibility.config.TestMetadata import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector import datadog.trace.civisibility.CiVisibilityTestUtils -import datadog.trace.api.intake.AgentlessIntake +import datadog.trace.api.intake.Intake import okhttp3.HttpUrl import okhttp3.OkHttpClient import org.apache.commons.io.IOUtils @@ -273,7 +273,7 @@ class ConfigurationApiImplTest extends Specification { } private BackendApi givenIntakeApi(URI address, boolean responseCompression) { - HttpUrl intakeUrl = HttpUrl.get(String.format("%s/api/%s/", address.toString(), AgentlessIntake.API.getVersion())) + HttpUrl intakeUrl = HttpUrl.get(String.format("%s/api/%s/", address.toString(), Intake.API.getVersion())) String apiKey = "api-key" String traceId = "a-trace-id" diff --git a/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java b/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java index 0f3d20b50c8..54f29eb9682 100644 --- a/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java +++ b/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java @@ -7,7 +7,7 @@ import datadog.trace.api.Config; import datadog.trace.api.logging.intake.LogsWriter; import datadog.trace.util.AgentThreadFactory; -import datadog.trace.api.intake.AgentlessIntake; +import datadog.trace.api.intake.Intake; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -87,7 +87,7 @@ public void log(Map message) { } private void logPollingLoop() { - BackendApi backendApi = apiFactory.createBackendApi(AgentlessIntake.LOGS); + BackendApi backendApi = apiFactory.createBackendApi(Intake.LOGS); LogsDispatcher logsDispatcher = new LogsDispatcher(backendApi); while (!Thread.currentThread().isInterrupted()) { diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 22c2c9c921f..9ab452dd4cf 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -644,7 +644,7 @@ import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.IastDetectionMode; import datadog.trace.api.iast.telemetry.Verbosity; -import datadog.trace.api.intake.AgentlessIntake; +import datadog.trace.api.intake.Intake; import datadog.trace.api.naming.SpanNaming; import datadog.trace.api.profiling.ProfilingEnablement; import datadog.trace.api.rum.RumInjectorConfig; @@ -4129,7 +4129,7 @@ private String getFinalDebuggerBaseUrl() { public String getFinalDebuggerSnapshotUrl() { if (isCiVisibilityFailedTestReplayEnabled() && isCiVisibilityAgentlessEnabled()) { - return AgentlessIntake.LOGS.getAgentlessUrl(this) + "logs"; + return Intake.LOGS.getAgentlessUrl(this) + "logs"; } else { return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; } @@ -4137,7 +4137,7 @@ public String getFinalDebuggerSnapshotUrl() { public String getFinalDebuggerSymDBUrl() { if (isCiVisibilityFailedTestReplayEnabled() && isCiVisibilityAgentlessEnabled()) { - return AgentlessIntake.LOGS.getAgentlessUrl(this) + "logs"; + return Intake.LOGS.getAgentlessUrl(this) + "logs"; } else { return getFinalDebuggerBaseUrl() + "/symdb/v1/input"; } diff --git a/internal-api/src/main/java/datadog/trace/api/intake/AgentlessIntake.java b/internal-api/src/main/java/datadog/trace/api/intake/Intake.java similarity index 96% rename from internal-api/src/main/java/datadog/trace/api/intake/AgentlessIntake.java rename to internal-api/src/main/java/datadog/trace/api/intake/Intake.java index ce870f9339f..1dab2795d07 100644 --- a/internal-api/src/main/java/datadog/trace/api/intake/AgentlessIntake.java +++ b/internal-api/src/main/java/datadog/trace/api/intake/Intake.java @@ -3,7 +3,7 @@ import datadog.trace.api.Config; import java.util.function.Function; -public enum AgentlessIntake { +public enum Intake { API("api", "v2", Config::isCiVisibilityAgentlessEnabled, Config::getCiVisibilityAgentlessUrl), LLMOBS_API("api", "v2", Config::isLlmObsAgentlessEnabled, Config::getLlMObsAgentlessUrl), LOGS( @@ -17,7 +17,7 @@ public enum AgentlessIntake { public final Function agentlessModeEnabled; public final Function customUrl; - AgentlessIntake( + Intake( String urlPrefix, String version, Function agentlessModeEnabled, diff --git a/internal-api/src/test/groovy/datadog/trace/api/intake/IntakeTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/intake/IntakeTest.groovy new file mode 100644 index 00000000000..d8e344feb90 --- /dev/null +++ b/internal-api/src/test/groovy/datadog/trace/api/intake/IntakeTest.groovy @@ -0,0 +1,34 @@ +package datadog.trace.api.intake + +import datadog.trace.api.Config +import datadog.trace.test.util.DDSpecification + +class IntakeTest extends DDSpecification { + def "intake URLs are generated correctly"() { + def config = Stub(Config) + config.getSite() >> "datadoghq.com" + + when: + def apiUrl = Intake.API.getAgentlessUrl(config) + def llmObsUrl = Intake.LLMOBS_API.getAgentlessUrl(config) + def logsUrl = Intake.LOGS.getAgentlessUrl(config) + + then: + apiUrl == "https://api.datadoghq.com/api/v2/" + llmObsUrl == "https://api.datadoghq.com/api/v2/" + logsUrl == "https://http-intake.logs.datadoghq.com/api/v2/" + + when: + config.getCiVisibilityAgentlessUrl() >> "agentless-civis" + config.getLlMObsAgentlessUrl() >> "agentless-llmobs" + config.getAgentlessLogSubmissionUrl() >> "agentless-log" + apiUrl = Intake.API.getAgentlessUrl(config) + llmObsUrl = Intake.LLMOBS_API.getAgentlessUrl(config) + logsUrl = Intake.LOGS.getAgentlessUrl(config) + + then: + apiUrl == "agentless-civis/api/v2/" + llmObsUrl == "agentless-llmobs/api/v2/" + logsUrl == "agentless-log/api/v2/" + } +} From 9e81cc7ceb558aac8cde71cf78c5feae56eeeec4 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 6 Aug 2025 12:46:31 +0200 Subject: [PATCH 11/24] test: remove ftr from instrumentation tests (not used) --- .../src/main/java/datadog/trace/bootstrap/Agent.java | 2 +- .../civisibility/CiVisibilityInstrumentationTest.groovy | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java index c121dfa3c84..97b72054861 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java @@ -632,7 +632,7 @@ public void execute() { } maybeStartAppSec(scoClass, sco); - // start before debugger to enable Failed Test Replay correctly + // start civisibility before debugger to enable Failed Test Replay correctly in headless mode maybeStartCiVisibility(instrumentation, scoClass, sco); maybeStartLLMObs(instrumentation, scoClass, sco); // start debugger before remote config to subscribe to it before starting to poll diff --git a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy index f2a4f7989ec..a8481ce3562 100644 --- a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityInstrumentationTest.groovy @@ -96,7 +96,6 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { injectSysConfig(CiVisibilityConfig.TEST_MANAGEMENT_ENABLED, "true") injectSysConfig(CiVisibilityConfig.TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES, "5") injectSysConfig(CiVisibilityConfig.CIVISIBILITY_TEST_ORDER, CIConstants.FAIL_FAST_TEST_ORDER) - injectSysConfig(CiVisibilityConfig.TEST_FAILED_TEST_REPLAY_ENABLED, "true") } private static final class Settings { @@ -113,7 +112,7 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { private volatile boolean earlyFlakinessDetectionEnabled private volatile boolean impactedTestsDetectionEnabled private volatile boolean testManagementEnabled - private volatile boolean failedTestReplayEnabled + private volatile boolean failedTestReplayEnabled = false } private final Settings settings = new Settings() @@ -349,10 +348,6 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { settings.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled } - def givenFailedTestReplayEnabled(boolean failedTestReplayEnabled) { - settings.failedTestReplayEnabled = failedTestReplayEnabled - } - def assertSpansData(String testcaseName, Map replacements = [:], List ignoredTags = []) { Predicate sessionSpan = span -> span.spanType == "test_session_end" spanFilter.waitForSpan(sessionSpan, TimeUnit.SECONDS.toMillis(20)) From 4e3deb4c087a8c23bd9bc806451266d1a496c062 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Mon, 11 Aug 2025 10:36:28 +0200 Subject: [PATCH 12/24] test: introduce FTR smoke tests for headfull and headless modes --- .github/CODEOWNERS | 1 + .../civisibility/CiVisibilitySystem.java | 3 +- .../buildsystem/BuildSystemModuleImpl.java | 9 +- .../exception/DefaultExceptionDebugger.java | 4 +- .../datadog/smoketest/MockBackend.groovy | 7 + dd-smoke-tests/junit-console/build.gradle | 20 + dd-smoke-tests/junit-console/gradle.lockfile | 166 +++++++ .../smoketest/JUnitConsoleSmokeTest.groovy | 285 ++++++++++++ .../src/test/resources/logback.xml | 3 + .../coverages.ftl | 19 + .../events.ftl | 271 ++++++++++++ .../src/main/java/com/example/Calculator.java | 11 + .../src/test/java/com/example/TestFailed.java | 13 + .../datadog/smoketest/MavenSmokeTest.groovy | 74 +++- .../coverages.ftl | 19 + .../events.ftl | 412 ++++++++++++++++++ .../pom.xml | 62 +++ .../src/main/java/com/example/Calculator.java | 11 + .../src/test/java/com/example/TestFailed.java | 14 + .../datadog/trace/api/ConfigDefaults.java | 1 + .../main/java/datadog/trace/api/Config.java | 36 +- settings.gradle.kts | 1 + 22 files changed, 1400 insertions(+), 42 deletions(-) create mode 100644 dd-smoke-tests/junit-console/build.gradle create mode 100644 dd-smoke-tests/junit-console/gradle.lockfile create mode 100644 dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy create mode 100644 dd-smoke-tests/junit-console/src/test/resources/logback.xml create mode 100644 dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/coverages.ftl create mode 100644 dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/events.ftl create mode 100644 dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/main/java/com/example/Calculator.java create mode 100644 dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/test/java/com/example/TestFailed.java create mode 100644 dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/coverages.ftl create mode 100644 dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/events.ftl create mode 100644 dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/pom.xml create mode 100644 dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/main/java/com/example/Calculator.java create mode 100644 dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/test/java/com/example/TestFailed.java diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ef437eebae2..62520f362fe 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -87,6 +87,7 @@ /dd-java-agent/instrumentation/maven-surefire/ @DataDog/ci-app-libraries-java /dd-java-agent/instrumentation/weaver/ @DataDog/ci-app-libraries-java /dd-smoke-tests/gradle/ @DataDog/ci-app-libraries-java +/dd-smoke-tests/junit-console/ @DataDog/ci-app-libraries-java /dd-smoke-tests/maven/ @DataDog/ci-app-libraries-java /internal-api/src/main/java/datadog/trace/api/git/ @DataDog/ci-app-libraries-java **/civisibility/ @DataDog/ci-app-libraries-java diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java index 50004983ed9..85b6e96b526 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java @@ -102,7 +102,8 @@ public static void start(Instrumentation inst, SharedCommunicationObjects sco) { } if (executionSettings.isFailedTestReplayEnabled()) { - config.setCiVisibilityFailedTestReplayEnabled(true); + // only marks the feature as active in child or headless processes + config.setCiVisibilityFailedTestReplayActive(true); } CiVisibilityCoverageServices.Child coverageServices = diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java index 2f220505512..f772e928be6 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java @@ -185,12 +185,13 @@ private Map getPropertiesPropagatedToChildProcess( Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.TEST_MANAGEMENT_ENABLED), Boolean.toString(executionSettings.getTestManagementSettings().isEnabled())); + propagatedSystemProperties.put( + Strings.propertyNameToSystemPropertyName( + CiVisibilityConfig.TEST_FAILED_TEST_REPLAY_ENABLED), + Boolean.toString(executionSettings.isFailedTestReplayEnabled())); + // enable exception replay if failed test replay is enabled if (executionSettings.isFailedTestReplayEnabled()) { - propagatedSystemProperties.put( - Strings.propertyNameToSystemPropertyName( - CiVisibilityConfig.TEST_FAILED_TEST_REPLAY_ENABLED), - "true"); propagatedSystemProperties.put( Strings.propertyNameToSystemPropertyName(DebuggerConfig.EXCEPTION_REPLAY_ENABLED), "true"); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java index 00e6bf60da8..5a748576dc2 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java @@ -75,7 +75,7 @@ public DefaultExceptionDebugger( @Override public void handleException(Throwable t, AgentSpan span) { // CIVIS Failed Test Replay acts on errors - if (t instanceof Error && !Config.get().isCiVisibilityFailedTestReplayEnabled()) { + if (t instanceof Error && !Config.get().isCiVisibilityFailedTestReplayActive()) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Skip handling error: {}", t.toString()); } @@ -114,7 +114,7 @@ public void handleException(Throwable t, AgentSpan span) { exceptionProbeManager.createProbesForException( throwable.getStackTrace(), chainedExceptionIdx); if (creationResult.probesCreated > 0) { - if (Config.get().isCiVisibilityFailedTestReplayEnabled()) { + if (Config.get().isCiVisibilityFailedTestReplayActive()) { // Assume Exception Replay is working under Failed Test Replay logic, // instrumentation applied sync for immediate test retries applyExceptionConfiguration(fingerprint); diff --git a/dd-smoke-tests/backend-mock/src/main/groovy/datadog/smoketest/MockBackend.groovy b/dd-smoke-tests/backend-mock/src/main/groovy/datadog/smoketest/MockBackend.groovy index e16b118f0be..426ea6c5615 100644 --- a/dd-smoke-tests/backend-mock/src/main/groovy/datadog/smoketest/MockBackend.groovy +++ b/dd-smoke-tests/backend-mock/src/main/groovy/datadog/smoketest/MockBackend.groovy @@ -40,6 +40,7 @@ class MockBackend implements AutoCloseable { private boolean knownTestsEnabled = false private boolean testManagementEnabled = false private int attemptToFixRetries = 0 + private boolean failedTestReplayEnabled = false void reset() { receivedTraces.clear() @@ -61,6 +62,7 @@ class MockBackend implements AutoCloseable { knownTestsEnabled = false testManagementEnabled = false attemptToFixRetries = 0 + failedTestReplayEnabled = false } @Override @@ -131,6 +133,10 @@ class MockBackend implements AutoCloseable { ]) } + void givenFailedTestReplay(boolean failedTestReplayEnabled) { + this.failedTestReplayEnabled = failedTestReplayEnabled + } + String getIntakeUrl() { return intakeServer.address.toString() } @@ -177,6 +183,7 @@ class MockBackend implements AutoCloseable { "flaky_test_retries_enabled": $flakyRetriesEnabled, "impacted_tests_enabled": $impactedTestsDetectionEnabled, "known_tests_enabled": $knownTestsEnabled, + "di_enabled": $failedTestReplayEnabled, "test_management": { "enabled": $testManagementEnabled, "attempt_to_fix_retries": $attemptToFixRetries diff --git a/dd-smoke-tests/junit-console/build.gradle b/dd-smoke-tests/junit-console/build.gradle new file mode 100644 index 00000000000..d81f14fcf48 --- /dev/null +++ b/dd-smoke-tests/junit-console/build.gradle @@ -0,0 +1,20 @@ +import java.time.Duration +import java.time.temporal.ChronoUnit + +apply from: "$rootDir/gradle/java.gradle" +description = 'JUnit Console Smoke Tests.' + +dependencies { + implementation group: 'org.junit.platform', name: 'junit-platform-console-standalone', version: '1.13.4' + + testImplementation project(':dd-smoke-tests:backend-mock') +} + +tasks.withType(Test).configureEach { + jvmArgs "-Ddatadog.smoketest.junit.console.jar.path=${configurations.runtimeClasspath.find { it.name.contains('junit-platform-console-standalone') }}" + + if (project.hasProperty("mavenRepositoryProxy")) { + // propagate proxy URL to tests, to then propagate it to nested Gradle builds + environment "MAVEN_REPOSITORY_PROXY", project.property("mavenRepositoryProxy") + } +} diff --git a/dd-smoke-tests/junit-console/gradle.lockfile b/dd-smoke-tests/junit-console/gradle.lockfile new file mode 100644 index 00000000000..ca5e4e49be2 --- /dev/null +++ b/dd-smoke-tests/junit-console/gradle.lockfile @@ -0,0 +1,166 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +cafe.cryptography:curve25519-elisabeth:0.1.0=testRuntimeClasspath +cafe.cryptography:ed25519-elisabeth:0.1.0=testRuntimeClasspath +ch.qos.logback:logback-classic:1.2.3=testCompileClasspath,testRuntimeClasspath +ch.qos.logback:logback-core:1.2.3=testCompileClasspath,testRuntimeClasspath +com.beust:jcommander:1.78=testRuntimeClasspath +com.blogspot.mydailyjava:weak-lock-free:0.17=testCompileClasspath,testRuntimeClasspath +com.datadoghq.okhttp3:okhttp:3.12.15=testCompileClasspath,testRuntimeClasspath +com.datadoghq.okio:okio:1.17.6=testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-javac-plugin-client:0.2.2=testCompileClasspath,testRuntimeClasspath +com.datadoghq:java-dogstatsd-client:4.4.3=testRuntimeClasspath +com.datadoghq:sketches-java:0.8.3=testRuntimeClasspath +com.fasterxml.jackson.core:jackson-annotations:2.16.0=testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-core:2.16.0=testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-databind:2.16.0=testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson:jackson-bom:2.16.0=testCompileClasspath,testRuntimeClasspath +com.github.javaparser:javaparser-core:3.25.6=testCompileClasspath,testRuntimeClasspath +com.github.jnr:jffi:1.3.13=testRuntimeClasspath +com.github.jnr:jnr-a64asm:1.0.0=testRuntimeClasspath +com.github.jnr:jnr-constants:0.10.4=testRuntimeClasspath +com.github.jnr:jnr-enxio:0.32.17=testRuntimeClasspath +com.github.jnr:jnr-ffi:2.2.16=testRuntimeClasspath +com.github.jnr:jnr-posix:3.1.19=testRuntimeClasspath +com.github.jnr:jnr-unixsocket:0.38.22=testRuntimeClasspath +com.github.jnr:jnr-x86asm:1.0.2=testRuntimeClasspath +com.github.spotbugs:spotbugs-annotations:4.2.0=compileClasspath,testCompileClasspath,testRuntimeClasspath +com.github.spotbugs:spotbugs-annotations:4.7.3=spotbugs +com.github.spotbugs:spotbugs:4.7.3=spotbugs +com.github.stefanbirkner:system-rules:1.19.0=testCompileClasspath,testRuntimeClasspath +com.google.code.findbugs:jsr305:3.0.2=compileClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath +com.google.code.gson:gson:2.9.1=spotbugs +com.google.guava:guava:20.0=testCompileClasspath,testRuntimeClasspath +com.google.re2j:re2j:1.7=testRuntimeClasspath +com.jayway.jsonpath:json-path:2.8.0=testCompileClasspath,testRuntimeClasspath +com.squareup.moshi:moshi:1.11.0=testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:logging-interceptor:3.12.12=testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp:3.12.12=testCompileClasspath,testRuntimeClasspath +com.squareup.okio:okio:1.17.5=testCompileClasspath,testRuntimeClasspath +com.thoughtworks.qdox:qdox:1.12.1=testRuntimeClasspath +com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath +commons-codec:commons-codec:1.15=spotbugs +commons-fileupload:commons-fileupload:1.5=testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.11.0=testCompileClasspath,testRuntimeClasspath +de.thetaphi:forbiddenapis:3.8=compileClasspath +info.picocli:picocli:4.6.3=testRuntimeClasspath +io.sqreen:libsqreen:15.0.1=testRuntimeClasspath +javax.servlet:javax.servlet-api:3.1.0=testCompileClasspath,testRuntimeClasspath +jaxen:jaxen:1.2.0=spotbugs +jline:jline:2.14.6=testRuntimeClasspath +junit:junit-dep:4.11=testCompileClasspath,testRuntimeClasspath +junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy-agent:1.17.5=testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy:1.17.5=testCompileClasspath,testRuntimeClasspath +net.java.dev.jna:jna-platform:5.8.0=testRuntimeClasspath +net.java.dev.jna:jna:5.8.0=testRuntimeClasspath +net.jcip:jcip-annotations:1.0=compileClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath +net.minidev:accessors-smart:2.4.9=testRuntimeClasspath +net.minidev:json-smart:2.4.10=testRuntimeClasspath +net.sf.saxon:Saxon-HE:11.4=spotbugs +org.apache.ant:ant-antlr:1.10.15=testRuntimeClasspath +org.apache.ant:ant-antlr:1.9.15=codenarc +org.apache.ant:ant-junit:1.10.15=testRuntimeClasspath +org.apache.ant:ant-junit:1.9.15=codenarc +org.apache.ant:ant-launcher:1.10.15=testRuntimeClasspath +org.apache.ant:ant:1.10.15=testCompileClasspath,testRuntimeClasspath +org.apache.bcel:bcel:6.5.0=spotbugs +org.apache.commons:commons-lang3:3.12.0=spotbugs +org.apache.commons:commons-text:1.10.0=spotbugs +org.apache.httpcomponents.client5:httpclient5:5.1.3=spotbugs +org.apache.httpcomponents.core5:httpcore5-h2:5.1.3=spotbugs +org.apache.httpcomponents.core5:httpcore5:5.1.3=spotbugs +org.apache.logging.log4j:log4j-api:2.19.0=spotbugs +org.apache.logging.log4j:log4j-core:2.19.0=spotbugs +org.apache.maven.wrapper:maven-wrapper:3.2.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath +org.codehaus.groovy:groovy-all:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-ant:2.5.14=codenarc +org.codehaus.groovy:groovy-ant:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-astbuilder:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-cli-picocli:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-console:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-datetime:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-docgenerator:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-groovydoc:2.5.14=codenarc +org.codehaus.groovy:groovy-groovydoc:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-groovysh:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-jmx:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-json:2.5.14=codenarc +org.codehaus.groovy:groovy-json:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-jsr223:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-macro:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-nio:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-servlet:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-sql:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-swing:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-templates:2.5.14=codenarc +org.codehaus.groovy:groovy-templates:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-test-junit5:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-test:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-testng:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-xml:2.5.14=codenarc +org.codehaus.groovy:groovy-xml:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy:2.5.14=codenarc +org.codehaus.groovy:groovy:3.0.24=testCompileClasspath,testRuntimeClasspath +org.codenarc:CodeNarc:2.2.0=codenarc +org.dom4j:dom4j:2.1.3=spotbugs +org.eclipse.jetty:jetty-http:9.4.56.v20240826=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-io:9.4.56.v20240826=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-server:9.4.56.v20240826=testCompileClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-util:9.4.56.v20240826=testCompileClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.31=testCompileClasspath,testRuntimeClasspath +org.gmetrics:GMetrics:1.1=codenarc +org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath +org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath +org.jacoco:org.jacoco.core:0.8.12=testRuntimeClasspath +org.jacoco:org.jacoco.report:0.8.12=testRuntimeClasspath +org.jctools:jctools-core:3.3.0=testRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-common:1.6.21=testCompileClasspath,testRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21=testCompileClasspath,testRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21=testCompileClasspath,testRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib:1.6.21=testCompileClasspath,testRuntimeClasspath +org.jetbrains:annotations:13.0=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-api:5.12.0=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-engine:5.12.0=testRuntimeClasspath +org.junit.jupiter:junit-jupiter-params:5.12.0=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter:5.12.0=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-commons:1.12.0=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-engine:1.12.0=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-launcher:1.12.0=testRuntimeClasspath +org.junit.platform:junit-platform-runner:1.12.0=testRuntimeClasspath +org.junit.platform:junit-platform-suite-api:1.12.0=testRuntimeClasspath +org.junit.platform:junit-platform-suite-commons:1.12.0=testRuntimeClasspath +org.junit:junit-bom:5.12.0=testCompileClasspath,testRuntimeClasspath +org.junit:junit-bom:5.9.1=spotbugs +org.msgpack:jackson-dataformat-msgpack:0.9.6=testCompileClasspath,testRuntimeClasspath +org.msgpack:msgpack-core:0.9.6=testCompileClasspath,testRuntimeClasspath +org.objenesis:objenesis:3.3=testCompileClasspath,testRuntimeClasspath +org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-analysis:9.2=testRuntimeClasspath +org.ow2.asm:asm-analysis:9.4=spotbugs +org.ow2.asm:asm-commons:9.4=spotbugs +org.ow2.asm:asm-commons:9.8=testRuntimeClasspath +org.ow2.asm:asm-tree:9.4=spotbugs +org.ow2.asm:asm-tree:9.8=testRuntimeClasspath +org.ow2.asm:asm-util:9.2=testRuntimeClasspath +org.ow2.asm:asm-util:9.4=spotbugs +org.ow2.asm:asm:9.4=spotbugs +org.ow2.asm:asm:9.8=testRuntimeClasspath +org.skyscreamer:jsonassert:1.5.1=testCompileClasspath,testRuntimeClasspath +org.slf4j:jcl-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:jul-to-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:log4j-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:1.7.30=testCompileClasspath +org.slf4j:slf4j-api:1.7.36=testRuntimeClasspath +org.slf4j:slf4j-api:2.0.0=spotbugs,spotbugsSlf4j +org.slf4j:slf4j-simple:2.0.0=spotbugsSlf4j +org.snakeyaml:snakeyaml-engine:2.9=testRuntimeClasspath +org.spockframework:spock-core:2.3-groovy-3.0=testCompileClasspath,testRuntimeClasspath +org.spockframework:spock-junit4:2.3-groovy-3.0=testCompileClasspath,testRuntimeClasspath +org.testng:testng:7.5.1=testRuntimeClasspath +org.webjars:jquery:3.5.1=testRuntimeClasspath +org.xmlresolver:xmlresolver:4.4.3=spotbugs +xml-apis:xml-apis:1.4.01=spotbugs +empty=annotationProcessor,shadow,spotbugsPlugins,testAnnotationProcessor diff --git a/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy new file mode 100644 index 00000000000..0168fe150d8 --- /dev/null +++ b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy @@ -0,0 +1,285 @@ +package datadog.smoketest + + +import datadog.trace.api.config.CiVisibilityConfig +import datadog.trace.api.config.GeneralConfig +import datadog.trace.civisibility.CiVisibilitySmokeTest +import datadog.trace.util.Strings +import java.nio.file.FileVisitResult +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.nio.file.SimpleFileVisitor +import java.nio.file.attribute.BasicFileAttributes +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import spock.lang.AutoCleanup +import spock.lang.Shared +import spock.lang.TempDir +import spock.util.environment.Jvm + +class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(JUnitConsoleSmokeTest.class) + + private static final String TEST_SERVICE_NAME = "test-headless-service" + private static final String TEST_ENVIRONMENT_NAME = "integration-test" + + private static final int PROCESS_TIMEOUT_SECS = 60 + private static final String JUNIT_CONSOLE_JAR_PATH = System.getProperty("datadog.smoketest.junit.console.jar.path") + private static final String JAVA_HOME = buildJavaHome() + + @TempDir + Path projectHome + + @Shared + @AutoCleanup + MockBackend mockBackend = new MockBackend() + + def setup() { + mockBackend.reset() + } + + def "test headless failed test replay"() { + givenProjectFiles(projectName) + + mockBackend.givenFlakyRetries(true) + mockBackend.givenFailedTestReplay(true) + + def compileCode = compileTestProject() + assert compileCode == 0 + + def exitCode = whenRunningJUnitConsole([ + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_FLAKY_RETRY_COUNT)}=2" as String, + "${Strings.propertyNameToSystemPropertyName(GeneralConfig.AGENTLESS_LOG_SUBMISSION_URL)}=${mockBackend.intakeUrl}" as String], + [:]) + + def additionalDynamicTags = ["content.meta.['_dd.debug.error.6.snapshot_id']", "content.meta.['_dd.debug.error.exception_id']"] + verifyEventsAndCoverages(projectName, "junit-console", "headless", mockBackend.waitForEvents(5), mockBackend.waitForCoverages(0), additionalDynamicTags) + //TODO: add verification of the logs payload + //mockBackend.waitForLogs(8) + + where: + projectName = "test_junit_console_failed_test_replay" + } + + private void givenProjectFiles(String projectFilesSources) { + def projectResourcesUri = this.getClass().getClassLoader().getResource(projectFilesSources).toURI() + def projectResourcesPath = Paths.get(projectResourcesUri) + copyFolder(projectResourcesPath, projectHome) + } + + private void copyFolder(Path src, Path dest) throws IOException { + Files.walkFileTree(src, new SimpleFileVisitor() { + @Override + FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException { + Files.createDirectories(dest.resolve(src.relativize(dir))) + return FileVisitResult.CONTINUE + } + + @Override + FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + Files.copy(file, dest.resolve(src.relativize(file))) + return FileVisitResult.CONTINUE + } + }) + + // creating empty .git directory so that the tracer could detect projectFolder as repo root + Files.createDirectory(projectHome.resolve(".git")) + } + + private int compileTestProject() { + def srcDir = projectHome.resolve("src/main/java") + def testSrcDir = projectHome.resolve("src/test/java") + def classesDir = projectHome.resolve("target/classes") + def testClassesDir = projectHome.resolve("target/test-classes") + + Files.createDirectories(classesDir) + Files.createDirectories(testClassesDir) + + // Compile main classes if they exist + if (Files.exists(srcDir)) { + def mainJavaFiles = findJavaFiles(srcDir) + if (!mainJavaFiles.isEmpty()) { + def result = runProcess(createCompilerProcessBuilder(classesDir.toString(), mainJavaFiles).start()) + if (result != 0) { + LOGGER.error("Error compiling source classes for JUnit Console smoke test") + return result + } + } + } + + // Compile test classes + def testJavaFiles = findJavaFiles(testSrcDir) + if (!testJavaFiles.isEmpty()) { + def result = runProcess(createCompilerProcessBuilder(testClassesDir.toString(), testJavaFiles, [classesDir.toString()]).start()) + if (result != 0) { + LOGGER.error("Error compiling source classes for JUnit Console smoke test") + return result + } + } + + return 0 + } + + private ProcessBuilder createCompilerProcessBuilder(String targetDir, List files, List additionalDeps = []) { + assert new File(JUNIT_CONSOLE_JAR_PATH).isFile() + + List deps = [JUNIT_CONSOLE_JAR_PATH] + deps.addAll(additionalDeps) + + List command = new ArrayList<>() + command.add(javacPath()) + command.addAll(["-cp", deps.join(":")]) + command.addAll(["-d", targetDir]) // TODO: check dir exists + command.addAll(files) + + ProcessBuilder processBuilder = new ProcessBuilder(command) + + processBuilder.directory(projectHome.toFile()) + processBuilder.environment().put("JAVA_HOME", JAVA_HOME) + + return processBuilder + } + + private static List findJavaFiles(Path directory) { + if (!Files.exists(directory)) { + return [] + } + + List javaFiles = [] + Files.walkFileTree(directory, new SimpleFileVisitor() { + @Override + FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (file.toString().endsWith(".java")) { + javaFiles.add(file.toString()) + } + return FileVisitResult.CONTINUE + } + }) + + return javaFiles + } + + private int whenRunningJUnitConsole(List additionalAgentArgs, Map additionalEnvVars) { + def processBuilder = createConsoleProcessBuilder(["execute"], additionalAgentArgs, additionalEnvVars) + + processBuilder.environment().put("DD_API_KEY", "01234567890abcdef123456789ABCDEF") + + return runProcess(processBuilder.start()) + } + + private static runProcess(Process p, int timeout_secs = PROCESS_TIMEOUT_SECS) { + StreamConsumer errorGobbler = new StreamConsumer(p.getErrorStream(), "ERROR") + StreamConsumer outputGobbler = new StreamConsumer(p.getInputStream(), "OUTPUT") + outputGobbler.start() + errorGobbler.start() + + if (!p.waitFor(timeout_secs, TimeUnit.SECONDS)) { + p.destroyForcibly() + throw new TimeoutException("Instrumented process failed to exit within $timeout_secs seconds") + } + + return p.exitValue() + } + + ProcessBuilder createConsoleProcessBuilder(List consoleCommand, List additionalAgentArgs, Map additionalEnvVars) { + assert new File(JUNIT_CONSOLE_JAR_PATH).isFile() + + List command = new ArrayList<>() + command.add(javaPath()) + command.addAll((String[]) ["-jar", JUNIT_CONSOLE_JAR_PATH]) + command.addAll(consoleCommand) + command.addAll(["--class-path", [projectHome.resolve("target/classes").toString(), projectHome.resolve("target/test-classes")].join(":")]) + command.add("--scan-class-path") + + ProcessBuilder processBuilder = new ProcessBuilder(command) + processBuilder.directory(projectHome.toFile()) + + processBuilder.environment().put("JAVA_HOME", JAVA_HOME) + processBuilder.environment().put("JAVA_TOOL_OPTIONS", javaToolOptions(additionalAgentArgs)) + for (envVar in additionalEnvVars) { + processBuilder.environment().put(envVar.key, envVar.value) + } + + def mavenRepositoryProxy = System.getenv("MAVEN_REPOSITORY_PROXY") + if (mavenRepositoryProxy != null) { + processBuilder.environment().put("MAVEN_REPOSITORY_PROXY", mavenRepositoryProxy) + } + + return processBuilder + } + + String javaPath() { + final String separator = System.getProperty("file.separator") + return JAVA_HOME + separator + "bin" + separator + "java" + } + + String javacPath() { + final String separator = System.getProperty("file.separator") + return JAVA_HOME + separator + "bin" + separator + "javac" + } + + String javaToolOptions(List additionalAgentArgs) { + def arguments = [] + + if (System.getenv("DD_CIVISIBILITY_SMOKETEST_DEBUG_PARENT") != null) { + // for convenience when debugging locally + arguments += "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" + } + + def agentShadowJar = System.getProperty("datadog.smoketest.agent.shadowJar.path") + def agentArgument = "-javaagent:${agentShadowJar}=" + + // for convenience when debugging locally + (System.getenv("DD_CIVISIBILITY_SMOKETEST_DEBUG_CHILD") != null ? "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_DEBUG_PORT)}=5055," : "") + + "${Strings.propertyNameToSystemPropertyName(GeneralConfig.TRACE_DEBUG)}=true," + + "${Strings.propertyNameToSystemPropertyName(GeneralConfig.ENV)}=${TEST_ENVIRONMENT_NAME}," + + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_ENABLED)}=true," + + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_AGENTLESS_ENABLED)}=true," + + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_CIPROVIDER_INTEGRATION_ENABLED)}=false," + + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_GIT_UPLOAD_ENABLED)}=false," + + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_AGENTLESS_URL)}=${mockBackend.intakeUrl}," + + "${Strings.propertyNameToSystemPropertyName(GeneralConfig.SERVICE_NAME)}=${TEST_SERVICE_NAME}," + + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_BUILD_INSTRUMENTATION_ENABLED)}=false," + + agentArgument += additionalAgentArgs.join(",") + + arguments += agentArgument.toString() + return arguments.join("\\ ") + } + + private static String buildJavaHome() { + if (Jvm.current.isJava8()) { + return System.getenv("JAVA_8_HOME") + } else { + return System.getenv("JAVA_" + Jvm.current.getJavaSpecificationVersion() + "_HOME") + } + } + + private static class StreamConsumer extends Thread { + final InputStream is + final String messagePrefix + + StreamConsumer(InputStream is, String messagePrefix) { + this.is = is + this.messagePrefix = messagePrefix + } + + @Override + void run() { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(is)) + String line + while ((line = br.readLine()) != null) { + System.out.println(messagePrefix + ": " + line) + } + } catch (IOException e) { + e.printStackTrace() + } + } + } +} diff --git a/dd-smoke-tests/junit-console/src/test/resources/logback.xml b/dd-smoke-tests/junit-console/src/test/resources/logback.xml new file mode 100644 index 00000000000..24d6bcd768a --- /dev/null +++ b/dd-smoke-tests/junit-console/src/test/resources/logback.xml @@ -0,0 +1,3 @@ + + + diff --git a/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/coverages.ftl b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/coverages.ftl new file mode 100644 index 00000000000..2f72ca231ee --- /dev/null +++ b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/coverages.ftl @@ -0,0 +1,19 @@ +[ { + "files" : [ { + "filename" : "src/main/java/com/example/Calculator.java" + }, { + "filename" : "src/test/java/com/example/TestFailed.java" + } ], + "span_id" : ${content_span_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} +}, { + "files" : [ { + "filename" : "src/main/java/com/example/Calculator.java" + }, { + "filename" : "src/test/java/com/example/TestFailed.java" + } ], + "span_id" : ${content_span_id_2}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} +} ] \ No newline at end of file diff --git a/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/events.ftl b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/events.ftl new file mode 100644 index 00000000000..36b7b6d98c3 --- /dev/null +++ b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/events.ftl @@ -0,0 +1,271 @@ +[ { + "content" : { + "duration" : ${content_duration}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid}, + "_dd.test.is_user_provided_service" : "true", + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit5", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test_suite_end", + "test.framework" : "junit5", + "test.framework_version" : "5.13.4", + "test.module" : "test-headless-service", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.status" : "fail", + "test.suite" : "com.example.TestFailed", + "test.type" : "test", + "test_session.name" : "test-headless-service" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count} + }, + "name" : "junit5.test_suite", + "resource" : "com.example.TestFailed", + "service" : "test-headless-service", + "start" : ${content_start}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} + }, + "type" : "test_suite_end", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_2}, + "error" : 1, + "meta" : { + "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", + "_dd.library_capabilities.impacted_tests" : "1", + "_dd.library_capabilities.test_impact_analysis" : "1", + "_dd.library_capabilities.test_management.attempt_to_fix" : "5", + "_dd.library_capabilities.test_management.disable" : "1", + "_dd.library_capabilities.test_management.quarantine" : "1", + "_dd.p.tid" : ${content_meta__dd_p_tid_2}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit5", + "env" : "integration-test", + "error.message" : ${content_meta_error_message}, + "error.stack" : ${content_meta_error_stack}, + "error.type" : "org.opentest4j.AssertionFailedError", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test", + "test.framework" : "junit5", + "test.framework_version" : "5.13.4", + "test.module" : "test-headless-service", + "test.name" : "test_failed", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.source.method" : "test_failed()V", + "test.status" : "fail", + "test.suite" : "com.example.TestFailed", + "test.type" : "test", + "test_session.name" : "test-headless-service" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_2}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id}, + "test.source.end" : 11, + "test.source.start" : 10 + }, + "name" : "junit5.test", + "parent_id" : ${content_parent_id}, + "resource" : "com.example.TestFailed.test_failed", + "service" : "test-headless-service", + "span_id" : ${content_span_id}, + "start" : ${content_start_2}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id}, + "trace_id" : ${content_trace_id} + }, + "type" : "test", + "version" : 2 +}, { + "content" : { + "duration" : ${content_duration_3}, + "error" : 1, + "meta" : { + "_dd.debug.error.6.file" : "TestFailed.java", + "_dd.debug.error.6.snapshot_id" : ${content_meta__dd_debug_error_6_snapshot_id}, + "_dd.debug.error.exception_hash" : "2959ee575767ef8f3474753647822b7aaf286fc0bf8e273117a7ceb47b525", + "_dd.debug.error.exception_id" : ${content_meta__dd_debug_error_exception_id}, + "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", + "_dd.library_capabilities.impacted_tests" : "1", + "_dd.library_capabilities.test_impact_analysis" : "1", + "_dd.library_capabilities.test_management.attempt_to_fix" : "5", + "_dd.library_capabilities.test_management.disable" : "1", + "_dd.library_capabilities.test_management.quarantine" : "1", + "_dd.p.tid" : ${content_meta__dd_p_tid_3}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit5", + "env" : "integration-test", + "error.debug_info_captured" : "true", + "error.message" : ${content_meta_error_message}, + "error.stack" : ${content_meta_error_stack_2}, + "error.type" : "org.opentest4j.AssertionFailedError", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test", + "test.framework" : "junit5", + "test.framework_version" : "5.13.4", + "test.has_failed_all_retries" : "true", + "test.is_retry" : "true", + "test.module" : "test-headless-service", + "test.name" : "test_failed", + "test.retry_reason" : "auto_test_retry", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.source.method" : "test_failed()V", + "test.status" : "fail", + "test.suite" : "com.example.TestFailed", + "test.type" : "test", + "test_session.name" : "test-headless-service" + }, + "metrics" : { + "_dd.debug.error.6.line" : 10, + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_3}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id}, + "test.source.end" : 11, + "test.source.start" : 10 + }, + "name" : "junit5.test", + "parent_id" : ${content_parent_id}, + "resource" : "com.example.TestFailed.test_failed", + "service" : "test-headless-service", + "span_id" : ${content_span_id_2}, + "start" : ${content_start_3}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id}, + "trace_id" : ${content_trace_id_2} + }, + "type" : "test", + "version" : 2 +}, { + "content" : { + "duration" : ${content_duration_4}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_4}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit5", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test_session_end", + "test.code_coverage.enabled" : "true", + "test.command" : "test-headless-service", + "test.framework" : "junit5", + "test.framework_version" : "5.13.4", + "test.itr.tests_skipping.enabled" : "true", + "test.itr.tests_skipping.type" : "test", + "test.status" : "fail", + "test.type" : "test", + "test_session.name" : "test-headless-service" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_4}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id}, + "test.itr.tests_skipping.count" : 0 + }, + "name" : "junit5.test_session", + "resource" : "test-headless-service", + "service" : "test-headless-service", + "start" : ${content_start_4}, + "test_session_id" : ${content_test_session_id} + }, + "type" : "test_session_end", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_5}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_5}, + "_dd.test.is_user_provided_service" : "true", + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit5", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test_module_end", + "test.code_coverage.enabled" : "true", + "test.framework" : "junit5", + "test.framework_version" : "5.13.4", + "test.itr.tests_skipping.enabled" : "true", + "test.itr.tests_skipping.type" : "test", + "test.module" : "test-headless-service", + "test.status" : "fail", + "test.type" : "test", + "test_session.name" : "test-headless-service" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_5}, + "test.itr.tests_skipping.count" : 0 + }, + "name" : "junit5.test_module", + "resource" : "test-headless-service", + "service" : "test-headless-service", + "start" : ${content_start_5}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id} + }, + "type" : "test_module_end", + "version" : 1 +} ] \ No newline at end of file diff --git a/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/main/java/com/example/Calculator.java b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/main/java/com/example/Calculator.java new file mode 100644 index 00000000000..3acf064086f --- /dev/null +++ b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/main/java/com/example/Calculator.java @@ -0,0 +1,11 @@ +package com.example; + +public class Calculator { + public static int add(int a, int b) { + return a + b; + } + + public static int subtract(int a, int b) { + return a - b; + } +} diff --git a/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/test/java/com/example/TestFailed.java b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/test/java/com/example/TestFailed.java new file mode 100644 index 00000000000..adcb8a03a9b --- /dev/null +++ b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/test/java/com/example/TestFailed.java @@ -0,0 +1,13 @@ +package com.example; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; + +public class TestFailed { + + @Test + public void test_failed() { + assertTrue(Calculator.add(2, 2) == 22); + } + +} diff --git a/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy b/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy index 83da2154689..44a86e3e2a7 100644 --- a/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy +++ b/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy @@ -164,55 +164,55 @@ class MavenSmokeTest extends CiVisibilitySmokeTest { verifyTestOrder(mockBackend.waitForEvents(eventsNumber), expectedOrder) where: - testcaseName | projectName | mavenVersion | surefireVersion | flakyTests | knownTests | expectedOrder | eventsNumber - "junit4-provider" | "test_successful_maven_run_junit4_class_ordering" | "3.9.9" | "3.0.0" | [ + testcaseName | projectName | mavenVersion | surefireVersion | flakyTests | knownTests | expectedOrder | eventsNumber + "junit4-provider" | "test_successful_maven_run_junit4_class_ordering" | "3.9.9" | "3.0.0" | [ test("datadog.smoke.TestSucceedB", "test_succeed"), test("datadog.smoke.TestSucceedB", "test_succeed_another"), test("datadog.smoke.TestSucceedA", "test_succeed") - ] | [ + ] | [ test("datadog.smoke.TestSucceedB", "test_succeed"), test("datadog.smoke.TestSucceedB", "test_succeed_another"), test("datadog.smoke.TestSucceedA", "test_succeed") - ] | [ + ] | [ test("datadog.smoke.TestSucceedC", "test_succeed"), test("datadog.smoke.TestSucceedC", "test_succeed_another"), test("datadog.smoke.TestSucceedA", "test_succeed_another"), test("datadog.smoke.TestSucceedA", "test_succeed"), test("datadog.smoke.TestSucceedB", "test_succeed"), test("datadog.smoke.TestSucceedB", "test_succeed_another") - ] | 15 - "junit47-provider" | "test_successful_maven_run_junit4_class_ordering_parallel" | "3.9.9" | "3.0.0" | [test("datadog.smoke.TestSucceedC", "test_succeed")] | [ + ] | 15 + "junit47-provider" | "test_successful_maven_run_junit4_class_ordering_parallel" | "3.9.9" | "3.0.0" | [test("datadog.smoke.TestSucceedC", "test_succeed")] | [ test("datadog.smoke.TestSucceedC", "test_succeed"), test("datadog.smoke.TestSucceedA", "test_succeed") - ] | [ + ] | [ test("datadog.smoke.TestSucceedB", "test_succeed"), test("datadog.smoke.TestSucceedC", "test_succeed"), test("datadog.smoke.TestSucceedA", "test_succeed") - ] | 12 - "junit4-provider-latest-surefire" | "test_successful_maven_run_junit4_class_ordering" | "3.9.9" | getLatestMavenSurefireVersion() | [ + ] | 12 + "junit4-provider-latest-surefire" | "test_successful_maven_run_junit4_class_ordering" | "3.9.9" | getLatestMavenSurefireVersion() | [ test("datadog.smoke.TestSucceedB", "test_succeed"), test("datadog.smoke.TestSucceedB", "test_succeed_another"), test("datadog.smoke.TestSucceedA", "test_succeed") - ] | [ + ] | [ test("datadog.smoke.TestSucceedB", "test_succeed"), test("datadog.smoke.TestSucceedB", "test_succeed_another"), test("datadog.smoke.TestSucceedA", "test_succeed") - ] | [ + ] | [ test("datadog.smoke.TestSucceedC", "test_succeed"), test("datadog.smoke.TestSucceedC", "test_succeed_another"), test("datadog.smoke.TestSucceedA", "test_succeed_another"), test("datadog.smoke.TestSucceedA", "test_succeed"), test("datadog.smoke.TestSucceedB", "test_succeed"), test("datadog.smoke.TestSucceedB", "test_succeed_another") - ] | 15 - "junit47-provider-latest-surefire" | "test_successful_maven_run_junit4_class_ordering_parallel" | "3.9.9" | getLatestMavenSurefireVersion() | [test("datadog.smoke.TestSucceedC", "test_succeed")] | [ + ] | 15 + "junit47-provider-latest-surefire" | "test_successful_maven_run_junit4_class_ordering_parallel" | "3.9.9" | getLatestMavenSurefireVersion() | [test("datadog.smoke.TestSucceedC", "test_succeed")] | [ test("datadog.smoke.TestSucceedC", "test_succeed"), test("datadog.smoke.TestSucceedA", "test_succeed") - ] | [ + ] | [ test("datadog.smoke.TestSucceedB", "test_succeed"), test("datadog.smoke.TestSucceedC", "test_succeed"), test("datadog.smoke.TestSucceedA", "test_succeed") - ] | 12 + ] | 12 } def "test service name is propagated to child processes"() { @@ -227,10 +227,36 @@ class MavenSmokeTest extends CiVisibilitySmokeTest { verifyEventsAndCoverages(projectName, "maven", mavenVersion, mockBackend.waitForEvents(5), mockBackend.waitForCoverages(1), additionalDynamicPaths) where: - projectName | mavenVersion + projectName | mavenVersion "test_successful_maven_run_child_service_propagation" | "3.9.9" } + def "test failed test replay"() { + givenWrapperPropertiesFile(mavenVersion) + givenMavenProjectFiles(projectName) + givenMavenDependenciesAreLoaded(projectName, mavenVersion) + + mockBackend.givenFlakyRetries(true) + mockBackend.givenFlakyTest("Maven Smoke Tests Project maven-surefire-plugin default-test", "com.example.TestFailed", "test_failed") + mockBackend.givenFailedTestReplay(true) + + def exitCode = whenRunningMavenBuild([ + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_FLAKY_RETRY_COUNT)}=2" as String, + "${Strings.propertyNameToSystemPropertyName(GeneralConfig.AGENTLESS_LOG_SUBMISSION_URL)}=${mockBackend.intakeUrl}" as String], + [], + [:]) + assert exitCode == 1 + + def additionalDynamicTags = ["content.meta.['_dd.debug.error.3.snapshot_id']", "content.meta.['_dd.debug.error.exception_id']"] + verifyEventsAndCoverages(projectName, "maven", mavenVersion, mockBackend.waitForEvents(5), mockBackend.waitForCoverages(0), additionalDynamicTags) + //TODO: add verification of the logs payload + //mockBackend.waitForLogs(8) + + where: + projectName | mavenVersion + "test_failed_maven_failed_test_replay" | "3.9.9" + } + private void givenWrapperPropertiesFile(String mavenVersion) { def distributionUrl = "https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/${mavenVersion}/apache-maven-${mavenVersion}-bin.zip" @@ -467,10 +493,10 @@ class MavenSmokeTest extends CiVisibilitySmokeTest { private static String getLatestMavenSurefireVersion() { OkHttpClient client = new OkHttpClient() Request request = - new Request.Builder() - .url( - "https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-surefire-plugin/maven-metadata.xml") - .build() + new Request.Builder() + .url( + "https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-surefire-plugin/maven-metadata.xml") + .build() try (Response response = client.newCall(request).execute()) { if (response.isSuccessful()) { DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance() @@ -488,10 +514,10 @@ class MavenSmokeTest extends CiVisibilitySmokeTest { } } else { LOGGER.warn( - "Could not get latest Maven Surefire version, response from repo.maven.apache.org is " - + response.code() - + ":" - + response.body().string()) + "Could not get latest Maven Surefire version, response from repo.maven.apache.org is " + + response.code() + + ":" + + response.body().string()) } } catch (Exception e) { LOGGER.warn("Could not get latest Maven Surefire version", e) diff --git a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/coverages.ftl b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/coverages.ftl new file mode 100644 index 00000000000..0ccad374ea0 --- /dev/null +++ b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/coverages.ftl @@ -0,0 +1,19 @@ +[ { + "files" : [ { + "filename" : "src/main/java/com/example/Calculator.java" + }, { + "filename" : "src/test/java/com/example/TestFailed.java" + } ], + "span_id" : ${content_span_id_5}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} +}, { + "files" : [ { + "filename" : "src/main/java/com/example/Calculator.java" + }, { + "filename" : "src/test/java/com/example/TestFailed.java" + } ], + "span_id" : ${content_span_id_6}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} +} ] \ No newline at end of file diff --git a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/events.ftl new file mode 100644 index 00000000000..b10b68bb6b9 --- /dev/null +++ b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/events.ftl @@ -0,0 +1,412 @@ +[ { + "content" : { + "duration" : ${content_duration}, + "error" : 1, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "maven", + "env" : "integration-test", + "error.message" : ${content_meta_error_message}, + "error.stack" : ${content_meta_error_stack}, + "error.type" : "org.apache.maven.lifecycle.LifecycleExecutionException", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test_session_end", + "test.code_coverage.enabled" : "true", + "test.command" : "mvn -B test", + "test.framework" : "junit4", + "test.framework_version" : "4.13.2", + "test.itr.tests_skipping.enabled" : "true", + "test.itr.tests_skipping.type" : "test", + "test.status" : "fail", + "test.toolchain" : ${content_meta_test_toolchain}, + "test.type" : "test", + "test_session.name" : "mvn -B test" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id}, + "test.itr.tests_skipping.count" : 0 + }, + "name" : "maven.test_session", + "resource" : "Maven Smoke Tests Project", + "service" : "test-maven-service", + "start" : ${content_start}, + "test_session_id" : ${content_test_session_id} + }, + "type" : "test_session_end", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_2}, + "error" : 1, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_2}, + "_dd.test.is_user_provided_service" : "true", + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "maven", + "env" : "integration-test", + "error.message" : ${content_meta_error_message}, + "error.stack" : ${content_meta_error_stack_2}, + "error.type" : "org.apache.maven.lifecycle.LifecycleExecutionException", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test_module_end", + "test.code_coverage.enabled" : "true", + "test.command" : "mvn -B test", + "test.execution" : "maven-surefire-plugin:test:default-test", + "test.framework" : "junit4", + "test.framework_version" : "4.13.2", + "test.itr.tests_skipping.enabled" : "true", + "test.itr.tests_skipping.type" : "test", + "test.module" : "Maven Smoke Tests Project maven-surefire-plugin default-test", + "test.status" : "fail", + "test.type" : "test", + "test_session.name" : "mvn -B test" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_2}, + "test.itr.tests_skipping.count" : 0 + }, + "name" : "maven.test_module", + "resource" : "Maven Smoke Tests Project maven-surefire-plugin default-test", + "service" : "test-maven-service", + "start" : ${content_start_2}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id} + }, + "type" : "test_module_end", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_3}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_3}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "execution" : "default-compile", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "plugin" : "maven-compiler-plugin", + "project" : "Maven Smoke Tests Project", + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "Maven_Smoke_Tests_Project_maven_compiler_plugin_default_compile", + "parent_id" : ${content_test_session_id}, + "resource" : "Maven_Smoke_Tests_Project_maven_compiler_plugin_default_compile", + "service" : "test-maven-service", + "span_id" : ${content_span_id}, + "start" : ${content_start_3}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_4}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_4}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "execution" : "default-testCompile", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "plugin" : "maven-compiler-plugin", + "project" : "Maven Smoke Tests Project", + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "Maven_Smoke_Tests_Project_maven_compiler_plugin_default_testCompile", + "parent_id" : ${content_test_session_id}, + "resource" : "Maven_Smoke_Tests_Project_maven_compiler_plugin_default_testCompile", + "service" : "test-maven-service", + "span_id" : ${content_span_id_2}, + "start" : ${content_start_4}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_5}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_5}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "execution" : "default-resources", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "plugin" : "maven-resources-plugin", + "project" : "Maven Smoke Tests Project", + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "Maven_Smoke_Tests_Project_maven_resources_plugin_default_resources", + "parent_id" : ${content_test_session_id}, + "resource" : "Maven_Smoke_Tests_Project_maven_resources_plugin_default_resources", + "service" : "test-maven-service", + "span_id" : ${content_span_id_3}, + "start" : ${content_start_5}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_6}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_6}, + "_dd.test.is_user_provided_service" : "true", + "env" : "integration-test", + "execution" : "default-testResources", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "plugin" : "maven-resources-plugin", + "project" : "Maven Smoke Tests Project", + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version} + }, + "metrics" : { }, + "name" : "Maven_Smoke_Tests_Project_maven_resources_plugin_default_testResources", + "parent_id" : ${content_test_session_id}, + "resource" : "Maven_Smoke_Tests_Project_maven_resources_plugin_default_testResources", + "service" : "test-maven-service", + "span_id" : ${content_span_id_4}, + "start" : ${content_start_6}, + "trace_id" : ${content_test_session_id} + }, + "type" : "span", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_7}, + "error" : 0, + "meta" : { + "_dd.p.tid" : ${content_meta__dd_p_tid_7}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit4", + "env" : "integration-test", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id_2}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test_suite_end", + "test.framework" : "junit4", + "test.framework_version" : "4.13.2", + "test.module" : "Maven Smoke Tests Project maven-surefire-plugin default-test", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.status" : "fail", + "test.suite" : "com.example.TestFailed", + "test.type" : "test", + "test_session.name" : "mvn -B test" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_3}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id_2}, + "test.source.end" : 14, + "test.source.start" : 7 + }, + "name" : "junit4.test_suite", + "resource" : "com.example.TestFailed", + "service" : "test-maven-service", + "start" : ${content_start_7}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} + }, + "type" : "test_suite_end", + "version" : 1 +}, { + "content" : { + "duration" : ${content_duration_8}, + "error" : 1, + "meta" : { + "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", + "_dd.library_capabilities.impacted_tests" : "1", + "_dd.library_capabilities.test_impact_analysis" : "1", + "_dd.library_capabilities.test_management.attempt_to_fix" : "5", + "_dd.library_capabilities.test_management.disable" : "1", + "_dd.library_capabilities.test_management.quarantine" : "1", + "_dd.p.tid" : ${content_meta__dd_p_tid_8}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit4", + "env" : "integration-test", + "error.stack" : ${content_meta_error_stack_3}, + "error.type" : "java.lang.AssertionError", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id_2}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test", + "test.framework" : "junit4", + "test.framework_version" : "4.13.2", + "test.module" : "Maven Smoke Tests Project maven-surefire-plugin default-test", + "test.name" : "test_failed", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.source.method" : "test_failed()V", + "test.status" : "fail", + "test.suite" : "com.example.TestFailed", + "test.type" : "test", + "test_session.name" : "mvn -B test" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_4}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id_2}, + "test.source.end" : 12, + "test.source.start" : 9 + }, + "name" : "junit4.test", + "parent_id" : ${content_parent_id}, + "resource" : "com.example.TestFailed.test_failed", + "service" : "test-maven-service", + "span_id" : ${content_span_id_5}, + "start" : ${content_start_8}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id}, + "trace_id" : ${content_trace_id} + }, + "type" : "test", + "version" : 2 +}, { + "content" : { + "duration" : ${content_duration_9}, + "error" : 1, + "meta" : { + "_dd.debug.error.3.file" : "TestFailed.java", + "_dd.debug.error.3.snapshot_id" : ${content_meta__dd_debug_error_3_snapshot_id}, + "_dd.debug.error.exception_hash" : "4e99779ed72c43d4b87387ee7cf4695cd904d2c4853b6054adcacfcfeffa", + "_dd.debug.error.exception_id" : ${content_meta__dd_debug_error_exception_id}, + "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", + "_dd.library_capabilities.impacted_tests" : "1", + "_dd.library_capabilities.test_impact_analysis" : "1", + "_dd.library_capabilities.test_management.attempt_to_fix" : "5", + "_dd.library_capabilities.test_management.disable" : "1", + "_dd.library_capabilities.test_management.quarantine" : "1", + "_dd.p.tid" : ${content_meta__dd_p_tid_9}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit4", + "env" : "integration-test", + "error.debug_info_captured" : "true", + "error.stack" : ${content_meta_error_stack_4}, + "error.type" : "java.lang.AssertionError", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id_2}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test", + "test.framework" : "junit4", + "test.framework_version" : "4.13.2", + "test.has_failed_all_retries" : "true", + "test.is_retry" : "true", + "test.module" : "Maven Smoke Tests Project maven-surefire-plugin default-test", + "test.name" : "test_failed", + "test.retry_reason" : "auto_test_retry", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.source.method" : "test_failed()V", + "test.status" : "fail", + "test.suite" : "com.example.TestFailed", + "test.type" : "test", + "test_session.name" : "mvn -B test" + }, + "metrics" : { + "_dd.debug.error.3.line" : 11, + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_5}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id_2}, + "test.source.end" : 12, + "test.source.start" : 9 + }, + "name" : "junit4.test", + "parent_id" : ${content_parent_id}, + "resource" : "com.example.TestFailed.test_failed", + "service" : "test-maven-service", + "span_id" : ${content_span_id_6}, + "start" : ${content_start_9}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id}, + "trace_id" : ${content_trace_id_2} + }, + "type" : "test", + "version" : 2 +} ] \ No newline at end of file diff --git a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/pom.xml b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/pom.xml new file mode 100644 index 00000000000..48f92df3632 --- /dev/null +++ b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/pom.xml @@ -0,0 +1,62 @@ + + + + 4.0.0 + com.datadog.ci.test + maven-smoke-test + 1.0-SNAPSHOT + Maven Smoke Tests Project + + + 8 + 8 + UTF-8 + + + + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + junit + junit + 4.13.2 + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0 + + + + + diff --git a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/main/java/com/example/Calculator.java b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/main/java/com/example/Calculator.java new file mode 100644 index 00000000000..3acf064086f --- /dev/null +++ b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/main/java/com/example/Calculator.java @@ -0,0 +1,11 @@ +package com.example; + +public class Calculator { + public static int add(int a, int b) { + return a + b; + } + + public static int subtract(int a, int b) { + return a - b; + } +} diff --git a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/test/java/com/example/TestFailed.java b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/test/java/com/example/TestFailed.java new file mode 100644 index 00000000000..7371566479a --- /dev/null +++ b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/test/java/com/example/TestFailed.java @@ -0,0 +1,14 @@ +package com.example; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class TestFailed { + + @Test + public void test_failed() { + assertTrue(Calculator.add(2, 2) == 22); + } + +} diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index ad94c6bed52..3a77390c417 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -189,6 +189,7 @@ public final class ConfigDefaults { static final boolean DEFAULT_DYNAMIC_INSTRUMENTATION_ENABLED = false; static final int DEFAULT_DYNAMIC_INSTRUMENTATION_UPLOAD_TIMEOUT = 30; // seconds static final int DEFAULT_DYNAMIC_INSTRUMENTATION_UPLOAD_FLUSH_INTERVAL = 0; // ms, 0 = dynamic + static final int DEFAULT_FAILED_TEST_REPLAY_UPLOAD_FLUSH_INTERVAL = 200; // ms, 0 = dynamic static final boolean DEFAULT_DYNAMIC_INSTRUMENTATION_CLASSFILE_DUMP_ENABLED = false; static final int DEFAULT_DYNAMIC_INSTRUMENTATION_POLL_INTERVAL = 1; // seconds static final int DEFAULT_DYNAMIC_INSTRUMENTATION_DIAGNOSTICS_INTERVAL = 60 * 60; // seconds diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 9ab452dd4cf..0501b6a3650 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -80,6 +80,7 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_BODY_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_PARAMS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_EXPERIMENTATAL_JEE_SPLIT_BY_DEPLOYMENT; +import static datadog.trace.api.ConfigDefaults.DEFAULT_FAILED_TEST_REPLAY_UPLOAD_FLUSH_INTERVAL; import static datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_CLIENT_ERROR_STATUSES; import static datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_SERVER_ERROR_STATUSES; import static datadog.trace.api.ConfigDefaults.DEFAULT_HEALTH_METRICS_ENABLED; @@ -1031,7 +1032,8 @@ public static String getHostName() { private final String gitPullRequestBaseBranch; private final String gitPullRequestBaseBranchSha; private final String gitCommitHeadSha; - private boolean ciVisibilityFailedTestReplayEnabled; + private final boolean ciVisibilityFailedTestReplayEnabled; + private boolean ciVisibilityFailedTestReplayActive = false; // propagates setting to DI private final boolean remoteConfigEnabled; private final boolean remoteConfigIntegrityCheckEnabled; @@ -2311,7 +2313,7 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) gitPullRequestBaseBranchSha = configProvider.getString(GIT_PULL_REQUEST_BASE_BRANCH_SHA); gitCommitHeadSha = configProvider.getString(GIT_COMMIT_HEAD_SHA); ciVisibilityFailedTestReplayEnabled = - configProvider.getBoolean(TEST_FAILED_TEST_REPLAY_ENABLED, false); + configProvider.getBoolean(TEST_FAILED_TEST_REPLAY_ENABLED, true); remoteConfigEnabled = configProvider.getBoolean( @@ -3715,12 +3717,16 @@ public boolean isCiVisibilityCodeCoverageEnabled() { return ciVisibilityCodeCoverageEnabled; } - /** @return {@code true} if code coverage line-granularity is explicitly enabled */ + /** + * @return {@code true} if code coverage line-granularity is explicitly enabled + */ public boolean isCiVisibilityCoverageLinesEnabled() { return ciVisibilityCoverageLinesEnabled != null && ciVisibilityCoverageLinesEnabled; } - /** @return {@code true} if code coverage line-granularity is explicitly disabled */ + /** + * @return {@code true} if code coverage line-granularity is explicitly disabled + */ public boolean isCiVisibilityCoverageLinesDisabled() { return ciVisibilityCoverageLinesEnabled != null && !ciVisibilityCoverageLinesEnabled; } @@ -3942,8 +3948,12 @@ public boolean isCiVisibilityFailedTestReplayEnabled() { return ciVisibilityFailedTestReplayEnabled; } - public void setCiVisibilityFailedTestReplayEnabled(boolean enabled) { - ciVisibilityFailedTestReplayEnabled = enabled; + public boolean isCiVisibilityFailedTestReplayActive() { + return ciVisibilityFailedTestReplayActive; + } + + public void setCiVisibilityFailedTestReplayActive(boolean enabled) { + ciVisibilityFailedTestReplayActive = enabled; } public String getGitPullRequestBaseBranch() { @@ -4003,7 +4013,9 @@ public int getDynamicInstrumentationUploadTimeout() { } public int getDynamicInstrumentationUploadFlushInterval() { - return dynamicInstrumentationUploadFlushInterval; + return isCiVisibilityFailedTestReplayActive() + ? DEFAULT_FAILED_TEST_REPLAY_UPLOAD_FLUSH_INTERVAL + : dynamicInstrumentationUploadFlushInterval; } public boolean isDynamicInstrumentationClassFileDumpEnabled() { @@ -4067,7 +4079,7 @@ public boolean isSymbolDatabaseCompressed() { } public boolean isDebuggerExceptionEnabled() { - return debuggerExceptionEnabled || ciVisibilityFailedTestReplayEnabled; + return debuggerExceptionEnabled || isCiVisibilityFailedTestReplayActive(); } public int getDebuggerMaxExceptionPerSecond() { @@ -4128,7 +4140,7 @@ private String getFinalDebuggerBaseUrl() { } public String getFinalDebuggerSnapshotUrl() { - if (isCiVisibilityFailedTestReplayEnabled() && isCiVisibilityAgentlessEnabled()) { + if (isCiVisibilityFailedTestReplayActive() && isCiVisibilityAgentlessEnabled()) { return Intake.LOGS.getAgentlessUrl(this) + "logs"; } else { return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; @@ -4136,7 +4148,7 @@ public String getFinalDebuggerSnapshotUrl() { } public String getFinalDebuggerSymDBUrl() { - if (isCiVisibilityFailedTestReplayEnabled() && isCiVisibilityAgentlessEnabled()) { + if (isCiVisibilityFailedTestReplayActive() && isCiVisibilityAgentlessEnabled()) { return Intake.LOGS.getAgentlessUrl(this) + "logs"; } else { return getFinalDebuggerBaseUrl() + "/symdb/v1/input"; @@ -4441,7 +4453,9 @@ public int getStackTraceLengthLimit() { return stackTraceLengthLimit; } - /** @return A map of tags to be applied only to the local application root span. */ + /** + * @return A map of tags to be applied only to the local application root span. + */ public TagMap getLocalRootSpanTags() { final Map runtimeTags = getRuntimeTags(); diff --git a/settings.gradle.kts b/settings.gradle.kts index 70bbfb5679f..6a35ad1a354 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -163,6 +163,7 @@ include( ":dd-smoke-tests:jersey-2", ":dd-smoke-tests:jersey-3", ":dd-smoke-tests:jboss-modules", + ":dd-smoke-tests:junit-console", ":dd-smoke-tests:kafka-2", ":dd-smoke-tests:kafka-3", ":dd-smoke-tests:lib-injection", From f93165495325233d6c1f7129c5ab80df0bce593a Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Mon, 11 Aug 2025 10:59:36 +0200 Subject: [PATCH 13/24] style: spotless and codenarc --- .../civisibility/CiVisibilityServices.java | 5 +- .../trace/logging/intake/LogsWriterImpl.java | 2 +- .../smoketest/JUnitConsoleSmokeTest.groovy | 59 ++++++++++--------- .../datadog/smoketest/MavenSmokeTest.groovy | 23 ++++---- .../main/java/datadog/trace/api/Config.java | 12 +--- 5 files changed, 50 insertions(+), 51 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java index cc9003f7fc1..095215cf44e 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java @@ -14,6 +14,7 @@ import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector; import datadog.trace.api.civisibility.telemetry.tag.Command; import datadog.trace.api.git.GitInfoProvider; +import datadog.trace.api.intake.Intake; import datadog.trace.civisibility.ci.CIProviderInfoFactory; import datadog.trace.civisibility.ci.env.CiEnvironment; import datadog.trace.civisibility.ci.env.CiEnvironmentImpl; @@ -34,7 +35,6 @@ import datadog.trace.civisibility.source.LinesResolver; import datadog.trace.civisibility.source.index.*; import datadog.trace.civisibility.utils.ShellCommandExecutor; -import datadog.trace.api.intake.Intake; import java.io.File; import java.lang.reflect.Type; import java.net.InetSocketAddress; @@ -84,8 +84,7 @@ public class CiVisibilityServices { this.processHierarchy = new ProcessHierarchy(); this.config = config; this.metricCollector = metricCollector; - this.backendApi = - new BackendApiFactory(config, sco).createBackendApi(Intake.API); + this.backendApi = new BackendApiFactory(config, sco).createBackendApi(Intake.API); this.jvmInfoFactory = new CachingJvmInfoFactory(config, new JvmInfoFactoryImpl()); this.gitClientFactory = buildGitClientFactory(config, metricCollector); diff --git a/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java b/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java index 54f29eb9682..c36950f422c 100644 --- a/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java +++ b/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java @@ -5,9 +5,9 @@ import datadog.communication.BackendApi; import datadog.communication.BackendApiFactory; import datadog.trace.api.Config; +import datadog.trace.api.intake.Intake; import datadog.trace.api.logging.intake.LogsWriter; import datadog.trace.util.AgentThreadFactory; -import datadog.trace.api.intake.Intake; import java.util.ArrayList; import java.util.HashMap; import java.util.List; diff --git a/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy index 0168fe150d8..a228365e17f 100644 --- a/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy +++ b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy @@ -21,7 +21,8 @@ import spock.lang.TempDir import spock.util.environment.Jvm class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { - + // CodeNarc incorrectly thinks ".class" is unnecessary in getLogger + @SuppressWarnings('UnnecessaryDotClass') private static final Logger LOGGER = LoggerFactory.getLogger(JUnitConsoleSmokeTest.class) private static final String TEST_SERVICE_NAME = "test-headless-service" @@ -53,8 +54,10 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { def exitCode = whenRunningJUnitConsole([ "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_FLAKY_RETRY_COUNT)}=2" as String, - "${Strings.propertyNameToSystemPropertyName(GeneralConfig.AGENTLESS_LOG_SUBMISSION_URL)}=${mockBackend.intakeUrl}" as String], - [:]) + "${Strings.propertyNameToSystemPropertyName(GeneralConfig.AGENTLESS_LOG_SUBMISSION_URL)}=${mockBackend.intakeUrl}" as String + ], + [:]) + assert exitCode == 1 def additionalDynamicTags = ["content.meta.['_dd.debug.error.6.snapshot_id']", "content.meta.['_dd.debug.error.exception_id']"] verifyEventsAndCoverages(projectName, "junit-console", "headless", mockBackend.waitForEvents(5), mockBackend.waitForCoverages(0), additionalDynamicTags) @@ -73,20 +76,20 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { private void copyFolder(Path src, Path dest) throws IOException { Files.walkFileTree(src, new SimpleFileVisitor() { - @Override - FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + @Override + FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - Files.createDirectories(dest.resolve(src.relativize(dir))) - return FileVisitResult.CONTINUE - } + Files.createDirectories(dest.resolve(src.relativize(dir))) + return FileVisitResult.CONTINUE + } - @Override - FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + @Override + FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Files.copy(file, dest.resolve(src.relativize(file))) - return FileVisitResult.CONTINUE - } - }) + Files.copy(file, dest.resolve(src.relativize(file))) + return FileVisitResult.CONTINUE + } + }) // creating empty .git directory so that the tracer could detect projectFolder as repo root Files.createDirectory(projectHome.resolve(".git")) @@ -153,14 +156,14 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { List javaFiles = [] Files.walkFileTree(directory, new SimpleFileVisitor() { - @Override - FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (file.toString().endsWith(".java")) { - javaFiles.add(file.toString()) + @Override + FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (file.toString().endsWith(".java")) { + javaFiles.add(file.toString()) + } + return FileVisitResult.CONTINUE } - return FileVisitResult.CONTINUE - } - }) + }) return javaFiles } @@ -173,15 +176,15 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { return runProcess(processBuilder.start()) } - private static runProcess(Process p, int timeout_secs = PROCESS_TIMEOUT_SECS) { + private static runProcess(Process p, int timeoutSecs = PROCESS_TIMEOUT_SECS) { StreamConsumer errorGobbler = new StreamConsumer(p.getErrorStream(), "ERROR") StreamConsumer outputGobbler = new StreamConsumer(p.getInputStream(), "OUTPUT") outputGobbler.start() errorGobbler.start() - if (!p.waitFor(timeout_secs, TimeUnit.SECONDS)) { + if (!p.waitFor(timeoutSecs , TimeUnit.SECONDS)) { p.destroyForcibly() - throw new TimeoutException("Instrumented process failed to exit within $timeout_secs seconds") + throw new TimeoutException("Instrumented process failed to exit within $timeoutSecs seconds") } return p.exitValue() @@ -194,7 +197,10 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { command.add(javaPath()) command.addAll((String[]) ["-jar", JUNIT_CONSOLE_JAR_PATH]) command.addAll(consoleCommand) - command.addAll(["--class-path", [projectHome.resolve("target/classes").toString(), projectHome.resolve("target/test-classes")].join(":")]) + command.addAll([ + "--class-path", + [projectHome.resolve("target/classes").toString(), projectHome.resolve("target/test-classes")].join(":") + ]) command.add("--scan-class-path") ProcessBuilder processBuilder = new ProcessBuilder(command) @@ -255,9 +261,8 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { private static String buildJavaHome() { if (Jvm.current.isJava8()) { return System.getenv("JAVA_8_HOME") - } else { - return System.getenv("JAVA_" + Jvm.current.getJavaSpecificationVersion() + "_HOME") } + return System.getenv("JAVA_" + Jvm.current.getJavaSpecificationVersion() + "_HOME") } private static class StreamConsumer extends Thread { diff --git a/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy b/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy index 44a86e3e2a7..25e357ffa0b 100644 --- a/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy +++ b/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy @@ -242,9 +242,10 @@ class MavenSmokeTest extends CiVisibilitySmokeTest { def exitCode = whenRunningMavenBuild([ "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_FLAKY_RETRY_COUNT)}=2" as String, - "${Strings.propertyNameToSystemPropertyName(GeneralConfig.AGENTLESS_LOG_SUBMISSION_URL)}=${mockBackend.intakeUrl}" as String], - [], - [:]) + "${Strings.propertyNameToSystemPropertyName(GeneralConfig.AGENTLESS_LOG_SUBMISSION_URL)}=${mockBackend.intakeUrl}" as String + ], + [], + [:]) assert exitCode == 1 def additionalDynamicTags = ["content.meta.['_dd.debug.error.3.snapshot_id']", "content.meta.['_dd.debug.error.exception_id']"] @@ -493,10 +494,10 @@ class MavenSmokeTest extends CiVisibilitySmokeTest { private static String getLatestMavenSurefireVersion() { OkHttpClient client = new OkHttpClient() Request request = - new Request.Builder() - .url( - "https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-surefire-plugin/maven-metadata.xml") - .build() + new Request.Builder() + .url( + "https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-surefire-plugin/maven-metadata.xml") + .build() try (Response response = client.newCall(request).execute()) { if (response.isSuccessful()) { DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance() @@ -514,10 +515,10 @@ class MavenSmokeTest extends CiVisibilitySmokeTest { } } else { LOGGER.warn( - "Could not get latest Maven Surefire version, response from repo.maven.apache.org is " - + response.code() - + ":" - + response.body().string()) + "Could not get latest Maven Surefire version, response from repo.maven.apache.org is " + + response.code() + + ":" + + response.body().string()) } } catch (Exception e) { LOGGER.warn("Could not get latest Maven Surefire version", e) diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 0501b6a3650..176749e228f 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -3717,16 +3717,12 @@ public boolean isCiVisibilityCodeCoverageEnabled() { return ciVisibilityCodeCoverageEnabled; } - /** - * @return {@code true} if code coverage line-granularity is explicitly enabled - */ + /** @return {@code true} if code coverage line-granularity is explicitly enabled */ public boolean isCiVisibilityCoverageLinesEnabled() { return ciVisibilityCoverageLinesEnabled != null && ciVisibilityCoverageLinesEnabled; } - /** - * @return {@code true} if code coverage line-granularity is explicitly disabled - */ + /** @return {@code true} if code coverage line-granularity is explicitly disabled */ public boolean isCiVisibilityCoverageLinesDisabled() { return ciVisibilityCoverageLinesEnabled != null && !ciVisibilityCoverageLinesEnabled; } @@ -4453,9 +4449,7 @@ public int getStackTraceLengthLimit() { return stackTraceLengthLimit; } - /** - * @return A map of tags to be applied only to the local application root span. - */ + /** @return A map of tags to be applied only to the local application root span. */ public TagMap getLocalRootSpanTags() { final Map runtimeTags = getRuntimeTags(); From eacde603406e5771c06e26ac2fd3a02245ff5e85 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Mon, 11 Aug 2025 12:31:30 +0200 Subject: [PATCH 14/24] feat: add test event finished FTR telemetry --- .../domain/AbstractTestSession.java | 3 +- .../trace/civisibility/domain/TestImpl.java | 5 +++- .../exception/DefaultExceptionDebugger.java | 30 ++++++++++--------- .../ExceptionProbeInstrumentationTest.java | 8 ++--- .../telemetry/CiVisibilityCountMetric.java | 3 +- .../bootstrap/instrumentation/api/Tags.java | 2 ++ 6 files changed, 28 insertions(+), 23 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java index f864df5f6e0..15c3979104f 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java @@ -132,8 +132,7 @@ public AbstractTestSession( config.isAgentlessLogSubmissionEnabled() ? AgentlessLogSubmissionEnabled.TRUE : null, CIConstants.FAIL_FAST_TEST_ORDER.equalsIgnoreCase(config.getCiVisibilityTestOrder()) ? FailFastTestOrderEnabled.TRUE - : null, - null); + : null); if (instrumentationType == InstrumentationType.MANUAL_API) { metricCollector.add(CiVisibilityCountMetric.MANUAL_API_EVENTS, 1, EventType.SESSION); diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java index d1fbe49ac06..8e04e5527f9 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java @@ -20,6 +20,7 @@ import datadog.trace.api.civisibility.telemetry.TagValue; import datadog.trace.api.civisibility.telemetry.tag.BrowserDriver; import datadog.trace.api.civisibility.telemetry.tag.EventType; +import datadog.trace.api.civisibility.telemetry.tag.FailedTestReplayEnabled; import datadog.trace.api.civisibility.telemetry.tag.HasFailedAllRetries; import datadog.trace.api.civisibility.telemetry.tag.IsAttemptToFix; import datadog.trace.api.civisibility.telemetry.tag.IsDisabled; @@ -305,7 +306,9 @@ public void end(@Nullable Long endTime) { span.getTag(Tags.TEST_IS_RETRY) != null ? IsRetry.TRUE : null, span.getTag(Tags.TEST_HAS_FAILED_ALL_RETRIES) != null ? HasFailedAllRetries.TRUE : null, retryReason instanceof TagValue ? (TagValue) retryReason : null, - null, + span.getTag(Tags.ERROR_DEBUG_INFO_CAPTURED) != null + ? FailedTestReplayEnabled.TestMetric.TRUE + : null, span.getTag(Tags.TEST_IS_RUM_ACTIVE) != null ? IsRum.TRUE : null, CIConstants.SELENIUM_BROWSER_DRIVER.equals(span.getTag(Tags.TEST_BROWSER_DRIVER)) ? BrowserDriver.SELENIUM diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java index 5a748576dc2..76d661f8a63 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java @@ -13,6 +13,7 @@ import datadog.trace.bootstrap.debugger.DebuggerContext; import datadog.trace.bootstrap.debugger.DebuggerContext.ClassNameFilter; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.Tags; import datadog.trace.util.AgentTaskScheduler; import java.time.Duration; import java.util.ArrayDeque; @@ -32,7 +33,6 @@ public class DefaultExceptionDebugger implements DebuggerContext.ExceptionDebugg public static final String DD_DEBUG_ERROR_EXCEPTION_ID = DD_DEBUG_ERROR_PREFIX + "exception_id"; public static final String DD_DEBUG_ERROR_EXCEPTION_HASH = DD_DEBUG_ERROR_PREFIX + "exception_hash"; - public static final String ERROR_DEBUG_INFO_CAPTURED = "error.debug_info_captured"; public static final String SNAPSHOT_ID_TAG_FMT = DD_DEBUG_ERROR_PREFIX + "%d.snapshot_id"; // Test Optimization / Failed Test Replay specific @@ -179,19 +179,21 @@ private static void processSnapshotsAndSetTags( span.setTag(tagName, snapshot.getId()); LOGGER.debug("add tag to span[{}]: {}: {}", span.getSpanId(), tagName, snapshot.getId()); - StackTraceElement stackFrame = innerTrace[currentIdx]; - String fileTag = String.format(TEST_DEBUG_ERROR_FILE_TAG_FMT, frameIndex); - String lineTag = String.format(TEST_DEBUG_ERROR_LINE_TAG_FMT, frameIndex); - span.setTag(fileTag, stackFrame.getFileName()); - span.setTag(lineTag, stackFrame.getLineNumber()); + if (Config.get().isCiVisibilityFailedTestReplayActive()) { + StackTraceElement stackFrame = innerTrace[currentIdx]; + String fileTag = String.format(TEST_DEBUG_ERROR_FILE_TAG_FMT, frameIndex); + String lineTag = String.format(TEST_DEBUG_ERROR_LINE_TAG_FMT, frameIndex); + span.setTag(fileTag, stackFrame.getFileName()); + span.setTag(lineTag, stackFrame.getLineNumber()); - LOGGER.debug( - "add ftr debug tags to span[{}]: {}={}, {}={}", - span.getSpanId(), - fileTag, - stackFrame.getFileName(), - lineTag, - stackFrame.getLineNumber()); + LOGGER.debug( + "add ftr debug tags to span[{}]: {}={}, {}={}", + span.getSpanId(), + fileTag, + stackFrame.getFileName(), + lineTag, + stackFrame.getLineNumber()); + } if (!state.isSnapshotSent()) { DebuggerAgent.getSink().addSnapshot(snapshot); @@ -206,7 +208,7 @@ private static void processSnapshotsAndSetTags( span.getSpanId(), DD_DEBUG_ERROR_EXCEPTION_ID, state.getExceptionId()); - span.setTag(ERROR_DEBUG_INFO_CAPTURED, true); + span.setTag(Tags.ERROR_DEBUG_INFO_CAPTURED, true); span.setTag(DD_DEBUG_ERROR_EXCEPTION_HASH, fingerprint); } } diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java index f5266fc8d5f..6ecc0472da2 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java @@ -3,7 +3,6 @@ import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.REMOTE_CONFIG; import static com.datadog.debugger.exception.DefaultExceptionDebugger.DD_DEBUG_ERROR_EXCEPTION_HASH; import static com.datadog.debugger.exception.DefaultExceptionDebugger.DD_DEBUG_ERROR_EXCEPTION_ID; -import static com.datadog.debugger.exception.DefaultExceptionDebugger.ERROR_DEBUG_INFO_CAPTURED; import static com.datadog.debugger.exception.DefaultExceptionDebugger.SNAPSHOT_ID_TAG_FMT; import static com.datadog.debugger.util.MoshiSnapshotTestHelper.getValue; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -38,6 +37,7 @@ import datadog.trace.bootstrap.debugger.ProbeId; import datadog.trace.bootstrap.debugger.ProbeLocation; import datadog.trace.bootstrap.debugger.ProbeRateLimiter; +import datadog.trace.bootstrap.instrumentation.api.Tags; import datadog.trace.core.CoreTracer; import java.io.IOException; import java.lang.instrument.ClassFileTransformer; @@ -157,7 +157,7 @@ public void instrumentAndCaptureSnapshots() throws Exception { MutableSpan span = traceInterceptor.getFirstSpan(); assertEquals(snapshot0.getExceptionId(), span.getTags().get(DD_DEBUG_ERROR_EXCEPTION_ID)); assertEquals(fingerprint, span.getTags().get(DD_DEBUG_ERROR_EXCEPTION_HASH)); - assertEquals(Boolean.TRUE, span.getTags().get(ERROR_DEBUG_INFO_CAPTURED)); + assertEquals(Boolean.TRUE, span.getTags().get(Tags.ERROR_DEBUG_INFO_CAPTURED)); assertEquals(snapshot0.getId(), span.getTags().get(String.format(SNAPSHOT_ID_TAG_FMT, 0))); assertEquals(1, probeSampler.getCallCount()); assertEquals(1, globalSampler.getCallCount()); @@ -198,11 +198,11 @@ public void differentExceptionsSameStack() throws Exception { assertExceptionMsg("illegal argument", snapshot1); MutableSpan span0 = traceInterceptor.getAllTraces().get(0).get(0); assertEquals(snapshot0.getExceptionId(), span0.getTags().get(DD_DEBUG_ERROR_EXCEPTION_ID)); - assertEquals(Boolean.TRUE, span0.getTags().get(ERROR_DEBUG_INFO_CAPTURED)); + assertEquals(Boolean.TRUE, span0.getTags().get(Tags.ERROR_DEBUG_INFO_CAPTURED)); assertEquals(snapshot0.getId(), span0.getTags().get(String.format(SNAPSHOT_ID_TAG_FMT, 0))); MutableSpan span1 = traceInterceptor.getAllTraces().get(1).get(0); assertEquals(snapshot1.getExceptionId(), span1.getTags().get(DD_DEBUG_ERROR_EXCEPTION_ID)); - assertEquals(Boolean.TRUE, span1.getTags().get(ERROR_DEBUG_INFO_CAPTURED)); + assertEquals(Boolean.TRUE, span1.getTags().get(Tags.ERROR_DEBUG_INFO_CAPTURED)); assertEquals(snapshot1.getId(), span1.getTags().get(String.format(SNAPSHOT_ID_TAG_FMT, 0))); } diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java b/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java index d214f775ac8..ccc9d790d68 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java @@ -54,8 +54,7 @@ public enum CiVisibilityCountMetric { Provider.class, AutoInjected.class, AgentlessLogSubmissionEnabled.class, - FailFastTestOrderEnabled.class, - FailedTestReplayEnabled.SessionMetric.class), + FailFastTestOrderEnabled.class), /** The number of events created */ EVENT_CREATED( "event_created", diff --git a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/Tags.java b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/Tags.java index c4bf7527906..573d9026ea0 100644 --- a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/Tags.java +++ b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/Tags.java @@ -107,6 +107,8 @@ public class Tags { public static final String TEST_TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED = "test.test_management.attempt_to_fix_passed"; + public static final String ERROR_DEBUG_INFO_CAPTURED = "error.debug_info_captured"; + public static final String CI_PROVIDER_NAME = "ci.provider.name"; public static final String CI_PIPELINE_ID = "ci.pipeline.id"; public static final String CI_PIPELINE_NAME = "ci.pipeline.name"; From 0228f71b973ee7c9d612e6f33e7e39684d67a558 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Mon, 11 Aug 2025 13:02:31 +0200 Subject: [PATCH 15/24] feat: add `product` field to snapshots --- .../agent/JsonSnapshotSerializer.java | 14 ++++++++++--- .../datadog/debugger/sink/SnapshotSink.java | 5 ++++- .../debugger/sink/DebuggerSinkTest.java | 21 +++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java index 1fbeddfad49..8dd27ffc590 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java @@ -14,13 +14,15 @@ public class JsonSnapshotSerializer implements DebuggerContext.ValueSerializer { private static final String DD_TRACE_ID = "dd.trace_id"; private static final String DD_SPAN_ID = "dd.span_id"; + public static final String TEST_OPT_PRODUCT = "test_optimization"; private static final JsonAdapter ADAPTER = MoshiHelper.createMoshiSnapshot().adapter(IntakeRequest.class); private static final JsonAdapter VALUE_ADAPTER = new MoshiSnapshotHelper.CapturedValueAdapter(); - public String serializeSnapshot(String serviceName, Snapshot snapshot) { - IntakeRequest request = new IntakeRequest(serviceName, new DebuggerIntakeRequestData(snapshot)); + public String serializeSnapshot(String serviceName, Snapshot snapshot, Config config) { + IntakeRequest request = + new IntakeRequest(serviceName, new DebuggerIntakeRequestData(snapshot), config); handleCorrelationFields(snapshot, request); handleDuration(snapshot, request); handlerLogger(snapshot, request); @@ -53,6 +55,7 @@ public static class IntakeRequest { private final String service; private final DebuggerIntakeRequestData debugger; private final String ddsource = "dd_debugger"; + private final String product; private final String message; private final String ddtags; @@ -85,7 +88,7 @@ public static class IntakeRequest { @Json(name = "logger.thread_name") private String loggerThreadName; - public IntakeRequest(String service, DebuggerIntakeRequestData debugger) { + public IntakeRequest(String service, DebuggerIntakeRequestData debugger, Config config) { this.service = service; this.debugger = debugger; this.message = debugger.snapshot.getMessage(); @@ -93,6 +96,7 @@ public IntakeRequest(String service, DebuggerIntakeRequestData debugger) { this.timestamp = debugger.snapshot.getTimestamp(); final CharSequence pt = ProcessTags.getTagsForSerialization(); this.processTags = pt != null ? pt.toString() : null; + this.product = config.isCiVisibilityFailedTestReplayActive() ? TEST_OPT_PRODUCT : null; } public String getService() { @@ -107,6 +111,10 @@ public String getDdsource() { return ddsource; } + public String getProduct() { + return product; + } + public String getMessage() { return message; } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/SnapshotSink.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/SnapshotSink.java index 7eefe2ff296..73190fbb3ae 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/SnapshotSink.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/SnapshotSink.java @@ -45,10 +45,12 @@ public class SnapshotSink { private final AtomicBoolean started = new AtomicBoolean(); private volatile AgentTaskScheduler.Scheduled highRateScheduled; private volatile long currentHighRateFlushInterval = HIGH_RATE_MAX_FLUSH_INTERVAL_MS; + private final Config config; public SnapshotSink(Config config, String tags, BatchUploader snapshotUploader) { this.serviceName = TagsHelper.sanitize(config.getServiceName()); this.batchSize = config.getDynamicInstrumentationUploadBatchSize(); + this.config = config; this.tags = tags; this.snapshotUploader = snapshotUploader; } @@ -183,7 +185,8 @@ private List getSerializedSnapshots(BlockingQueue queue, int l private String serializeSnapshot(String serviceName, Snapshot snapshot) { snapshot.getId(); // Ensure id is generated - String str = DebuggerAgent.getSnapshotSerializer().serializeSnapshot(serviceName, snapshot); + String str = + DebuggerAgent.getSnapshotSerializer().serializeSnapshot(serviceName, snapshot, config); String prunedStr = SnapshotPruner.prune(str, MAX_SNAPSHOT_SIZE, 4); if (prunedStr.length() != str.length()) { LOGGER.debug( diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java index 9d9cd95ca05..74feb5ced69 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java @@ -124,6 +124,27 @@ public void addSnapshot(boolean processTagsEnabled) throws IOException { } } + @ParameterizedTest(name = "Add Failed Test Replay product ''{0}''") + @ValueSource(booleans = {true, false}) + public void addProductTag(boolean failedTestReplayActive) throws IOException { + when(config.isCiVisibilityFailedTestReplayActive()).thenReturn(failedTestReplayActive); + ProcessTags.reset(config); + DebuggerSink sink = createDefaultDebuggerSink(); + DebuggerAgentHelper.injectSerializer(new JsonSnapshotSerializer()); + Snapshot snapshot = createSnapshot(); + sink.addSnapshot(snapshot); + sink.lowRateFlush(sink); + verify(batchUploader).upload(payloadCaptor.capture(), matches(EXPECTED_SNAPSHOT_TAGS)); + String strPayload = new String(payloadCaptor.getValue(), StandardCharsets.UTF_8); + System.out.println(strPayload); + JsonSnapshotSerializer.IntakeRequest intakeRequest = assertOneIntakeRequest(strPayload); + if (failedTestReplayActive) { + assertEquals(JsonSnapshotSerializer.TEST_OPT_PRODUCT, intakeRequest.getProduct()); + } else { + assertNull(intakeRequest.getProduct()); + } + } + @Test public void addMultipleSnapshots() throws IOException { when(config.getDynamicInstrumentationUploadBatchSize()).thenReturn(2); From 88fd66546701d1f69ad395f51182944cac334584 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Mon, 11 Aug 2025 16:20:17 +0200 Subject: [PATCH 16/24] feat: implement SuiteEnd listener for sink flushing --- .../civisibility/domain/TestSuiteImpl.java | 3 ++ .../civisibility/CiVisibilitySmokeTest.groovy | 34 +++++++++++++++++++ .../datadog/debugger/sink/DebuggerSink.java | 26 ++++++++++++++ .../selenium/SeleniumTestListener.java | 5 +++ .../smoketest/JUnitConsoleSmokeTest.groovy | 3 +- .../datadog/smoketest/MavenSmokeTest.groovy | 3 +- .../datadog/trace/api/ConfigDefaults.java | 2 +- .../InstrumentationTestBridge.java | 8 +++++ 8 files changed, 79 insertions(+), 5 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java index c29fa175229..89f509ad4cd 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java @@ -6,6 +6,7 @@ import datadog.trace.api.Config; import datadog.trace.api.civisibility.DDTestSuite; +import datadog.trace.api.civisibility.InstrumentationTestBridge; import datadog.trace.api.civisibility.config.LibraryCapability; import datadog.trace.api.civisibility.coverage.CoverageStore; import datadog.trace.api.civisibility.execution.TestStatus; @@ -220,6 +221,8 @@ public void end(@Nullable Long endTime) { AgentTracer.closeActive(); } + InstrumentationTestBridge.fireBeforeSuiteEnd(); + onSpanFinish.accept(span); if (endTime != null) { diff --git a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy index 4251f586597..220c864a1b9 100644 --- a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy @@ -52,4 +52,38 @@ abstract class CiVisibilitySmokeTest extends Specification { // an even more basic smoke check for distributions: assert that we received some assert !receivedTelemetryDistributions.isEmpty() } + + protected static verifySnapshotLogs(List> receivedLogs, int expectedSnapshots) { + def logsPerSnapshot = 4 // 3 probe statuses + 1 snapshot log are expected per snapshot + + assert receivedLogs.size() == logsPerSnapshot * expectedSnapshots + + def probeStatusLogs = receivedLogs.findAll { it.containsKey("message") } + def snapshotLogs = receivedLogs.findAll { !it.containsKey("message") } + + verifyProbeStatuses(probeStatusLogs, expectedSnapshots) + verifySnapshots(snapshotLogs) + } + + private static verifyProbeStatuses(List> logs, int expectedCount) { + assert logs.findAll { log -> ((String) log.message).startsWith("Received probe") }.size() == expectedCount + assert logs.findAll { log -> ((String) log.message).startsWith("Installed probe") }.size() == expectedCount + assert logs.findAll { log -> ((String) log.message).endsWith("is emitting.") }.size() == expectedCount + } + + private static verifySnapshots(List> logs) { + def requiredLogFields = ["logger.name", "logger.method", "dd.spanid", "dd.traceid"] + def requiredSnapshotFields = ["captures", "exceptionId", "probe", "stack"] + + logs.each { log -> + assert log.product == "test_optimization" + requiredLogFields.each { field -> log.containsKey(field) } + + Map debuggerMap = log.debugger as Map + Map snapshotContent = debuggerMap.snapshot as Map + + assert snapshotContent != null + requiredSnapshotFields.each { field -> snapshotContent.containsKey(field) } + } + } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/DebuggerSink.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/DebuggerSink.java index f2cee0e4940..d22516ae122 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/DebuggerSink.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/DebuggerSink.java @@ -4,6 +4,8 @@ import com.datadog.debugger.uploader.BatchUploader; import com.datadog.debugger.util.DebuggerMetrics; import datadog.trace.api.Config; +import datadog.trace.api.civisibility.InstrumentationTestBridge; +import datadog.trace.api.civisibility.domain.TestContext; import datadog.trace.bootstrap.debugger.DebuggerContext.SkipCause; import datadog.trace.bootstrap.debugger.ProbeId; import datadog.trace.util.AgentTaskScheduler; @@ -68,6 +70,11 @@ public DebuggerSink( this.snapshotSink = snapshotSink; this.symbolSink = symbolSink; this.uploadFlushInterval = config.getDynamicInstrumentationUploadFlushInterval(); + + if (config.isCiVisibilityFailedTestReplayActive()) { + // register test listener to flush snapshots on suite end + InstrumentationTestBridge.registerListener(new DebuggerTestListener(this)); + } } public void start() { @@ -235,4 +242,23 @@ public void skipSnapshot(String probeId, SkipCause cause) { long getCurrentLowRateFlushInterval() { return currentLowRateFlushInterval; } + + static class DebuggerTestListener implements InstrumentationTestBridge.TestListener { + private final DebuggerSink sink; + + DebuggerTestListener(DebuggerSink sink) { + this.sink = sink; + } + + @Override + public void beforeTestEnd(TestContext ignored) { + // noop + } + + @Override + public void beforeSuiteEnd() { + LOGGER.debug("CiVisibility BeforeSuiteEnd fired, flushing sink"); + sink.lowRateFlush(sink); + } + } } diff --git a/dd-java-agent/instrumentation/selenium/src/main/java/datadog/trace/instrumentation/selenium/SeleniumTestListener.java b/dd-java-agent/instrumentation/selenium/src/main/java/datadog/trace/instrumentation/selenium/SeleniumTestListener.java index 9b2d93af88b..d9c10f8f0f5 100644 --- a/dd-java-agent/instrumentation/selenium/src/main/java/datadog/trace/instrumentation/selenium/SeleniumTestListener.java +++ b/dd-java-agent/instrumentation/selenium/src/main/java/datadog/trace/instrumentation/selenium/SeleniumTestListener.java @@ -25,4 +25,9 @@ public void beforeTestEnd(TestContext testContext) { SeleniumUtils.beforePageClose(driver); } + + @Override + public void beforeSuiteEnd() { + // noop + } } diff --git a/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy index a228365e17f..17bf1d15bc2 100644 --- a/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy +++ b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy @@ -61,8 +61,7 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { def additionalDynamicTags = ["content.meta.['_dd.debug.error.6.snapshot_id']", "content.meta.['_dd.debug.error.exception_id']"] verifyEventsAndCoverages(projectName, "junit-console", "headless", mockBackend.waitForEvents(5), mockBackend.waitForCoverages(0), additionalDynamicTags) - //TODO: add verification of the logs payload - //mockBackend.waitForLogs(8) + verifySnapshotLogs(mockBackend.waitForLogs(4), 1) where: projectName = "test_junit_console_failed_test_replay" diff --git a/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy b/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy index 25e357ffa0b..f3a9ee21634 100644 --- a/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy +++ b/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy @@ -250,8 +250,7 @@ class MavenSmokeTest extends CiVisibilitySmokeTest { def additionalDynamicTags = ["content.meta.['_dd.debug.error.3.snapshot_id']", "content.meta.['_dd.debug.error.exception_id']"] verifyEventsAndCoverages(projectName, "maven", mavenVersion, mockBackend.waitForEvents(5), mockBackend.waitForCoverages(0), additionalDynamicTags) - //TODO: add verification of the logs payload - //mockBackend.waitForLogs(8) + verifySnapshotLogs(mockBackend.waitForLogs(4), 1) where: projectName | mavenVersion diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index 3a77390c417..593bf40107d 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -189,7 +189,7 @@ public final class ConfigDefaults { static final boolean DEFAULT_DYNAMIC_INSTRUMENTATION_ENABLED = false; static final int DEFAULT_DYNAMIC_INSTRUMENTATION_UPLOAD_TIMEOUT = 30; // seconds static final int DEFAULT_DYNAMIC_INSTRUMENTATION_UPLOAD_FLUSH_INTERVAL = 0; // ms, 0 = dynamic - static final int DEFAULT_FAILED_TEST_REPLAY_UPLOAD_FLUSH_INTERVAL = 200; // ms, 0 = dynamic + static final int DEFAULT_FAILED_TEST_REPLAY_UPLOAD_FLUSH_INTERVAL = 1000; // ms, 0 = dynamic static final boolean DEFAULT_DYNAMIC_INSTRUMENTATION_CLASSFILE_DUMP_ENABLED = false; static final int DEFAULT_DYNAMIC_INSTRUMENTATION_POLL_INTERVAL = 1; // seconds static final int DEFAULT_DYNAMIC_INSTRUMENTATION_DIAGNOSTICS_INTERVAL = 60 * 60; // seconds diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/InstrumentationTestBridge.java b/internal-api/src/main/java/datadog/trace/api/civisibility/InstrumentationTestBridge.java index 084cc1a2926..e69f69ac390 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/InstrumentationTestBridge.java +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/InstrumentationTestBridge.java @@ -38,11 +38,19 @@ public static void fireBeforeTestEnd(TestContext testContext) { } } + public static void fireBeforeSuiteEnd() { + for (TestListener testListener : TEST_LISTENERS) { + testListener.beforeSuiteEnd(); + } + } + public static void registerListener(TestListener listener) { TEST_LISTENERS.addIfAbsent(listener); } public interface TestListener { void beforeTestEnd(TestContext testContext); + + void beforeSuiteEnd(); } } From ec9c54d63eec9c4529d77b2de4fb74db3cbd1c0f Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Tue, 12 Aug 2025 16:12:02 +0200 Subject: [PATCH 17/24] feat: introduce new config variables for debugger --- .../datadog/debugger/agent/DebuggerAgent.java | 9 +---- .../exception/DefaultExceptionDebugger.java | 40 ++++++++++++------- .../DefaultExceptionDebuggerTest.java | 22 ++++++++-- .../ExceptionProbeInstrumentationTest.java | 2 +- .../debugger/sink/DebuggerSinkTest.java | 16 +++----- .../datadog/trace/api/ConfigDefaults.java | 2 +- .../trace/api/config/DebuggerConfig.java | 2 + .../main/java/datadog/trace/api/Config.java | 23 ++++++++--- 8 files changed, 74 insertions(+), 42 deletions(-) diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java index 4de8622576a..eabf4752fca 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java @@ -40,7 +40,6 @@ import java.lang.ref.WeakReference; import java.nio.file.Path; import java.nio.file.Paths; -import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -210,13 +209,7 @@ public static void startExceptionReplay() { Config config = Config.get(); commonInit(config); initClassNameFilter(); - exceptionDebugger = - new DefaultExceptionDebugger( - configurationUpdater, - classNameFilter, - Duration.ofSeconds(config.getDebuggerExceptionCaptureInterval()), - config.getDebuggerMaxExceptionPerSecond(), - config.getDebuggerExceptionMaxCapturedFrames()); + exceptionDebugger = new DefaultExceptionDebugger(configurationUpdater, classNameFilter, config); DebuggerContext.initExceptionDebugger(exceptionDebugger); LOGGER.info("Started Exception Replay"); } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java index 76d661f8a63..98653d9f997 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java @@ -44,19 +44,22 @@ public class DefaultExceptionDebugger implements DebuggerContext.ExceptionDebugg private final ClassNameFilter classNameFiltering; private final CircuitBreaker circuitBreaker; private final int maxCapturedFrames; + private final boolean applyConfigAsync; + private final boolean isFailedTestReplayActive; public DefaultExceptionDebugger( ConfigurationUpdater configurationUpdater, ClassNameFilter classNameFiltering, - Duration captureInterval, - int maxExceptionPerSecond, - int maxCapturedFrames) { + Config config) { this( - new ExceptionProbeManager(classNameFiltering, captureInterval), + new ExceptionProbeManager( + classNameFiltering, Duration.ofSeconds(config.getDebuggerExceptionCaptureInterval())), configurationUpdater, classNameFiltering, - maxExceptionPerSecond, - maxCapturedFrames); + config.getDebuggerMaxExceptionPerSecond(), + config.getDebuggerExceptionMaxCapturedFrames(), + config.isDebuggerExceptionAsyncConfig(), + config.isCiVisibilityFailedTestReplayActive()); } DefaultExceptionDebugger( @@ -64,18 +67,22 @@ public DefaultExceptionDebugger( ConfigurationUpdater configurationUpdater, ClassNameFilter classNameFiltering, int maxExceptionPerSecond, - int maxCapturedFrames) { + int maxCapturedFrames, + boolean applyConfigAsync, + boolean isFailedTestReplayActive) { this.exceptionProbeManager = exceptionProbeManager; this.configurationUpdater = configurationUpdater; this.classNameFiltering = classNameFiltering; this.circuitBreaker = new CircuitBreaker(maxExceptionPerSecond, Duration.ofSeconds(1)); this.maxCapturedFrames = maxCapturedFrames; + this.applyConfigAsync = applyConfigAsync; + this.isFailedTestReplayActive = isFailedTestReplayActive; } @Override public void handleException(Throwable t, AgentSpan span) { // CIVIS Failed Test Replay acts on errors - if (t instanceof Error && !Config.get().isCiVisibilityFailedTestReplayActive()) { + if (t instanceof Error && !isFailedTestReplayActive) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Skip handling error: {}", t.toString()); } @@ -103,7 +110,13 @@ public void handleException(Throwable t, AgentSpan span) { return; } processSnapshotsAndSetTags( - t, span, state, chainedExceptionsList, fingerprint, maxCapturedFrames); + t, + span, + state, + chainedExceptionsList, + fingerprint, + maxCapturedFrames, + isFailedTestReplayActive); exceptionProbeManager.updateLastCapture(fingerprint); } else { // climb up the exception chain to find the first exception that has instrumented frames @@ -114,9 +127,7 @@ public void handleException(Throwable t, AgentSpan span) { exceptionProbeManager.createProbesForException( throwable.getStackTrace(), chainedExceptionIdx); if (creationResult.probesCreated > 0) { - if (Config.get().isCiVisibilityFailedTestReplayActive()) { - // Assume Exception Replay is working under Failed Test Replay logic, - // instrumentation applied sync for immediate test retries + if (isFailedTestReplayActive || !applyConfigAsync) { applyExceptionConfiguration(fingerprint); } else { AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); @@ -147,7 +158,8 @@ private static void processSnapshotsAndSetTags( ThrowableState state, List chainedExceptions, String fingerprint, - int maxCapturedFrames) { + int maxCapturedFrames, + boolean isFailedTestReplayActive) { if (span.getTag(DD_DEBUG_ERROR_EXCEPTION_ID) != null) { LOGGER.debug("Clear previous frame tags"); // already set for this span, clear the frame tags @@ -179,7 +191,7 @@ private static void processSnapshotsAndSetTags( span.setTag(tagName, snapshot.getId()); LOGGER.debug("add tag to span[{}]: {}: {}", span.getSpanId(), tagName, snapshot.getId()); - if (Config.get().isCiVisibilityFailedTestReplayActive()) { + if (isFailedTestReplayActive) { StackTraceElement stackFrame = innerTrace[currentIdx]; String fileTag = String.format(TEST_DEBUG_ERROR_FILE_TAG_FMT, frameIndex); String lineTag = String.format(TEST_DEBUG_ERROR_LINE_TAG_FMT, frameIndex); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java index 09dd8917dd6..76912eafdcc 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java @@ -65,9 +65,9 @@ public void setUp() { classNameFiltering = new ClassNameFiltering( new HashSet<>(singletonList("com.datadog.debugger.exception.ThirdPartyCode"))); + Config config = createConfig(); exceptionDebugger = - new DefaultExceptionDebugger( - configurationUpdater, classNameFiltering, Duration.ofHours(1), 100, 3); + new DefaultExceptionDebugger(configurationUpdater, classNameFiltering, config); listener = new TestSnapshotListener(createConfig(), mock(ProbeStatusSink.class)); DebuggerAgentHelper.injectSink(listener); } @@ -275,11 +275,24 @@ public void nestedExceptionFullThirdParty() { public void filteringOutErrors() { ExceptionProbeManager manager = mock(ExceptionProbeManager.class); exceptionDebugger = - new DefaultExceptionDebugger(manager, configurationUpdater, classNameFiltering, 100, 3); + new DefaultExceptionDebugger( + manager, configurationUpdater, classNameFiltering, 100, 3, true, false); exceptionDebugger.handleException(new AssertionError("test"), mock(AgentSpan.class)); verify(manager, times(0)).isAlreadyInstrumented(any()); } + @Test + public void syncConfig() { + RuntimeException exception = new RuntimeException("test"); + String fingerprint = Fingerprinter.fingerprint(exception, classNameFiltering); + AgentSpan span = mock(AgentSpan.class); + exceptionDebugger.handleException(exception, span); + // instrumentation should be applied synchronously + assertTrue(exceptionDebugger.getExceptionProbeManager().isAlreadyInstrumented(fingerprint)); + exceptionDebugger.handleException(exception, span); + verify(configurationUpdater).accept(eq(ConfigurationAcceptor.Source.EXCEPTION), any()); + } + private Object recordTags(InvocationOnMock invocationOnMock) { Object[] args = invocationOnMock.getArguments(); String key = (String) args[0]; @@ -414,6 +427,9 @@ public static Config createConfig() { .thenReturn("http://localhost:8126/debugger/v1/input"); when(config.getFinalDebuggerSymDBUrl()).thenReturn("http://localhost:8126/symdb/v1/input"); when(config.getDynamicInstrumentationUploadBatchSize()).thenReturn(100); + when(config.getDebuggerExceptionCaptureInterval()).thenReturn(3600); + when(config.getDebuggerMaxExceptionPerSecond()).thenReturn(100); + when(config.getDebuggerExceptionMaxCapturedFrames()).thenReturn(3); return config; } } diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java index 6ecc0472da2..976287d0cb4 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java @@ -383,7 +383,7 @@ private TestSnapshotListener setupExceptionDebugging( DebuggerContext.initValueSerializer(new JsonSnapshotSerializer()); DefaultExceptionDebugger exceptionDebugger = new DefaultExceptionDebugger( - exceptionProbeManager, configurationUpdater, classNameFiltering, 100, 3); + exceptionProbeManager, configurationUpdater, classNameFiltering, 100, 3, true, false); DebuggerContext.initExceptionDebugger(exceptionDebugger); configurationUpdater.accept(REMOTE_CONFIG, definitions); return listener; diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java index 74feb5ced69..37cac100c6f 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java @@ -27,6 +27,7 @@ import com.squareup.moshi.Types; import datadog.trace.api.Config; import datadog.trace.api.ProcessTags; +import datadog.trace.api.civisibility.InstrumentationTestBridge; import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.CapturedContext.CapturedValue; import datadog.trace.bootstrap.debugger.CapturedStackFrame; @@ -124,25 +125,20 @@ public void addSnapshot(boolean processTagsEnabled) throws IOException { } } - @ParameterizedTest(name = "Add Failed Test Replay product ''{0}''") - @ValueSource(booleans = {true, false}) - public void addProductTag(boolean failedTestReplayActive) throws IOException { - when(config.isCiVisibilityFailedTestReplayActive()).thenReturn(failedTestReplayActive); + @Test + public void addFailedTestReplaySnapshot() throws IOException { + when(config.isCiVisibilityFailedTestReplayActive()).thenReturn(true); ProcessTags.reset(config); DebuggerSink sink = createDefaultDebuggerSink(); DebuggerAgentHelper.injectSerializer(new JsonSnapshotSerializer()); Snapshot snapshot = createSnapshot(); sink.addSnapshot(snapshot); - sink.lowRateFlush(sink); + InstrumentationTestBridge.fireBeforeSuiteEnd(); // flush on suite end verify(batchUploader).upload(payloadCaptor.capture(), matches(EXPECTED_SNAPSHOT_TAGS)); String strPayload = new String(payloadCaptor.getValue(), StandardCharsets.UTF_8); System.out.println(strPayload); JsonSnapshotSerializer.IntakeRequest intakeRequest = assertOneIntakeRequest(strPayload); - if (failedTestReplayActive) { - assertEquals(JsonSnapshotSerializer.TEST_OPT_PRODUCT, intakeRequest.getProduct()); - } else { - assertNull(intakeRequest.getProduct()); - } + assertEquals(JsonSnapshotSerializer.TEST_OPT_PRODUCT, intakeRequest.getProduct()); } @Test diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index 593bf40107d..bb3218eafb3 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -189,7 +189,6 @@ public final class ConfigDefaults { static final boolean DEFAULT_DYNAMIC_INSTRUMENTATION_ENABLED = false; static final int DEFAULT_DYNAMIC_INSTRUMENTATION_UPLOAD_TIMEOUT = 30; // seconds static final int DEFAULT_DYNAMIC_INSTRUMENTATION_UPLOAD_FLUSH_INTERVAL = 0; // ms, 0 = dynamic - static final int DEFAULT_FAILED_TEST_REPLAY_UPLOAD_FLUSH_INTERVAL = 1000; // ms, 0 = dynamic static final boolean DEFAULT_DYNAMIC_INSTRUMENTATION_CLASSFILE_DUMP_ENABLED = false; static final int DEFAULT_DYNAMIC_INSTRUMENTATION_POLL_INTERVAL = 1; // seconds static final int DEFAULT_DYNAMIC_INSTRUMENTATION_DIAGNOSTICS_INTERVAL = 60 * 60; // seconds @@ -209,6 +208,7 @@ public final class ConfigDefaults { static final boolean DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED = true; static final int DEFAULT_DEBUGGER_EXCEPTION_MAX_CAPTURED_FRAMES = 3; static final int DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS = 60 * 60; + static final boolean DEFAULT_DEBUGGER_EXCEPTION_ASYNC_CONFIG = true; static final boolean DEFAULT_DISTRIBUTED_DEBUGGER_ENABLED = false; static final boolean DEFAULT_DEBUGGER_SOURCE_FILE_TRACKING_ENABLED = true; diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java index 536821c9a25..f9695695ee0 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java @@ -58,6 +58,8 @@ public final class DebuggerConfig { "exception.replay.capture.max.frames"; public static final String DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS = "exception.replay.capture.interval.seconds"; + public static final String DEBUGGER_EXCEPTION_ASYNC_CONFIG = + "internal.exception.replay.async.config"; public static final String DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED = "exception.replay.capture.intermediate.spans.enabled"; public static final String DISTRIBUTED_DEBUGGER_ENABLED = "distributed.debugger.enabled"; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 176749e228f..5aae96ac738 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -54,6 +54,7 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX; import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_PROPAGATION_MODE_MODE; import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_TRACE_PREPARED_STATEMENTS; +import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_ASYNC_CONFIG; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_ENABLED; @@ -80,7 +81,6 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_BODY_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_PARAMS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_EXPERIMENTATAL_JEE_SPLIT_BY_DEPLOYMENT; -import static datadog.trace.api.ConfigDefaults.DEFAULT_FAILED_TEST_REPLAY_UPLOAD_FLUSH_INTERVAL; import static datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_CLIENT_ERROR_STATUSES; import static datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_SERVER_ERROR_STATUSES; import static datadog.trace.api.ConfigDefaults.DEFAULT_HEALTH_METRICS_ENABLED; @@ -280,6 +280,7 @@ import static datadog.trace.api.config.CrashTrackingConfig.CRASH_TRACKING_TAGS; import static datadog.trace.api.config.CwsConfig.CWS_ENABLED; import static datadog.trace.api.config.CwsConfig.CWS_TLS_REFRESH; +import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_EXCEPTION_ASYNC_CONFIG; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_EXCEPTION_CAPTURE_MAX_FRAMES; @@ -304,6 +305,7 @@ import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_REDACTED_TYPES; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS; +import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_SNAPSHOT_URL; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_UPLOAD_BATCH_SIZE; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_UPLOAD_FLUSH_INTERVAL; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS; @@ -1049,6 +1051,7 @@ public static String getHostName() { private final boolean DBMTracePreparedStatements; private final boolean dynamicInstrumentationEnabled; + private final String dynamicInstrumentationSnapshotUrl; private final int dynamicInstrumentationUploadTimeout; private final int dynamicInstrumentationUploadFlushInterval; private final boolean dynamicInstrumentationClassFileDumpEnabled; @@ -1077,6 +1080,7 @@ public static String getHostName() { private final boolean debuggerExceptionCaptureIntermediateSpansEnabled; private final int debuggerExceptionMaxCapturedFrames; private final int debuggerExceptionCaptureInterval; + private final boolean debuggerExceptionAsyncConfig; private final boolean debuggerCodeOriginEnabled; private final int debuggerCodeOriginMaxUserFrames; private final boolean distributedDebuggerEnabled; @@ -2342,6 +2346,8 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) dynamicInstrumentationEnabled = configProvider.getBoolean( DYNAMIC_INSTRUMENTATION_ENABLED, DEFAULT_DYNAMIC_INSTRUMENTATION_ENABLED); + dynamicInstrumentationSnapshotUrl = + configProvider.getString(DYNAMIC_INSTRUMENTATION_SNAPSHOT_URL); distributedDebuggerEnabled = configProvider.getBoolean( DISTRIBUTED_DEBUGGER_ENABLED, DEFAULT_DISTRIBUTED_DEBUGGER_ENABLED); @@ -2451,6 +2457,9 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) configProvider.getInteger( DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS, DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS); + debuggerExceptionAsyncConfig = + configProvider.getBoolean( + DEBUGGER_EXCEPTION_ASYNC_CONFIG, DEFAULT_DEBUGGER_EXCEPTION_ASYNC_CONFIG); debuggerSourceFileTrackingEnabled = configProvider.getBoolean( DEBUGGER_SOURCE_FILE_TRACKING_ENABLED, DEFAULT_DEBUGGER_SOURCE_FILE_TRACKING_ENABLED); @@ -4009,9 +4018,7 @@ public int getDynamicInstrumentationUploadTimeout() { } public int getDynamicInstrumentationUploadFlushInterval() { - return isCiVisibilityFailedTestReplayActive() - ? DEFAULT_FAILED_TEST_REPLAY_UPLOAD_FLUSH_INTERVAL - : dynamicInstrumentationUploadFlushInterval; + return dynamicInstrumentationUploadFlushInterval; } public boolean isDynamicInstrumentationClassFileDumpEnabled() { @@ -4098,6 +4105,10 @@ public int getDebuggerExceptionCaptureInterval() { return debuggerExceptionCaptureInterval; } + public boolean isDebuggerExceptionAsyncConfig() { + return debuggerExceptionAsyncConfig; + } + public boolean isDebuggerCodeOriginEnabled() { return debuggerCodeOriginEnabled; } @@ -4136,7 +4147,9 @@ private String getFinalDebuggerBaseUrl() { } public String getFinalDebuggerSnapshotUrl() { - if (isCiVisibilityFailedTestReplayActive() && isCiVisibilityAgentlessEnabled()) { + if (Strings.isNotBlank(dynamicInstrumentationSnapshotUrl)) { + return dynamicInstrumentationSnapshotUrl; + } else if (isCiVisibilityFailedTestReplayActive() && isCiVisibilityAgentlessEnabled()) { return Intake.LOGS.getAgentlessUrl(this) + "logs"; } else { return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; From d4d2432176c605dcc164a2ab4a6e78f79623e15a Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Tue, 19 Aug 2025 10:29:22 +0200 Subject: [PATCH 18/24] chore: remove todo --- .../test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy index 17bf1d15bc2..a08881172fe 100644 --- a/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy +++ b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy @@ -137,7 +137,7 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { List command = new ArrayList<>() command.add(javacPath()) command.addAll(["-cp", deps.join(":")]) - command.addAll(["-d", targetDir]) // TODO: check dir exists + command.addAll(["-d", targetDir]) command.addAll(files) ProcessBuilder processBuilder = new ProcessBuilder(command) From 49eaebf239be93661235ed23fc92fff3034530fc Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Thu, 21 Aug 2025 15:36:54 +0200 Subject: [PATCH 19/24] feat: align FTR settings with JS' implementation --- .../events/TestEventsHandlerImpl.java | 9 + .../civisibility/CiVisibilitySmokeTest.groovy | 14 +- .../exception/DefaultExceptionDebugger.java | 30 ++- .../smoketest/JUnitConsoleSmokeTest.groovy | 11 +- .../coverages.ftl | 18 ++ .../events.ftl | 176 ++++++++++++++++-- .../src/test/java/com/example/TestFailed.java | 5 + .../datadog/smoketest/MavenSmokeTest.groovy | 6 +- .../coverages.ftl | 18 ++ .../events.ftl | 160 ++++++++++++++-- .../src/test/java/com/example/TestFailed.java | 5 + .../main/java/datadog/trace/api/DDTags.java | 1 + 12 files changed, 400 insertions(+), 53 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java index 4dfe62b24bf..eb10963913d 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java @@ -1,6 +1,7 @@ package datadog.trace.civisibility.events; import datadog.json.JsonWriter; +import datadog.trace.api.DDTags; import datadog.trace.api.DisableTestTrace; import datadog.trace.api.civisibility.CIConstants; import datadog.trace.api.civisibility.DDTest; @@ -23,6 +24,7 @@ import datadog.trace.civisibility.domain.TestFrameworkSession; import datadog.trace.civisibility.domain.TestImpl; import datadog.trace.civisibility.domain.TestSuiteImpl; +import datadog.trace.civisibility.execution.RetryUntilSuccessful; import java.util.Collection; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -181,6 +183,10 @@ public void onTestStart( } if (testExecutionHistory != null) { + if (testExecutionHistory instanceof RetryUntilSuccessful) { + // Used by FailedTestReplay to limit the instrumentation to AutoTestRetries + test.setTag(DDTags.TEST_STRATEGY, RetryReason.atr.toString()); + } RetryReason retryReason = testExecutionHistory.currentExecutionRetryReason(); if (retryReason != null) { test.setTag(Tags.TEST_IS_RETRY, true); @@ -271,6 +277,9 @@ public void onTestFinish( } } + // Remove strategy tag used by FTR if it was set + test.setTag(DDTags.TEST_STRATEGY, null); + test.end(endTime); } diff --git a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy index 220c864a1b9..cbc573b4f90 100644 --- a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy @@ -53,16 +53,16 @@ abstract class CiVisibilitySmokeTest extends Specification { assert !receivedTelemetryDistributions.isEmpty() } - protected static verifySnapshotLogs(List> receivedLogs, int expectedSnapshots) { - def logsPerSnapshot = 4 // 3 probe statuses + 1 snapshot log are expected per snapshot + protected static verifySnapshotLogs(List> receivedLogs, int expectedProbes, int expectedSnapshots) { + def logsPerProbe = 3 // 3 probe statuses per probe -> received, installed, emitting - assert receivedLogs.size() == logsPerSnapshot * expectedSnapshots + assert receivedLogs.size() == logsPerProbe * expectedProbes + expectedSnapshots def probeStatusLogs = receivedLogs.findAll { it.containsKey("message") } def snapshotLogs = receivedLogs.findAll { !it.containsKey("message") } - verifyProbeStatuses(probeStatusLogs, expectedSnapshots) - verifySnapshots(snapshotLogs) + verifyProbeStatuses(probeStatusLogs, expectedProbes) + verifySnapshots(snapshotLogs, expectedSnapshots) } private static verifyProbeStatuses(List> logs, int expectedCount) { @@ -71,7 +71,9 @@ abstract class CiVisibilitySmokeTest extends Specification { assert logs.findAll { log -> ((String) log.message).endsWith("is emitting.") }.size() == expectedCount } - private static verifySnapshots(List> logs) { + private static verifySnapshots(List> logs, expectedCount) { + assert logs.size() == expectedCount + def requiredLogFields = ["logger.name", "logger.method", "dd.spanid", "dd.traceid"] def requiredSnapshotFields = ["captures", "exceptionId", "probe", "stack"] diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java index 98653d9f997..475ff2011fd 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java @@ -10,6 +10,8 @@ import com.datadog.debugger.util.CircuitBreaker; import com.datadog.debugger.util.ExceptionHelper; import datadog.trace.api.Config; +import datadog.trace.api.DDTags; +import datadog.trace.api.civisibility.telemetry.tag.RetryReason; import datadog.trace.bootstrap.debugger.DebuggerContext; import datadog.trace.bootstrap.debugger.DebuggerContext.ClassNameFilter; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -53,7 +55,10 @@ public DefaultExceptionDebugger( Config config) { this( new ExceptionProbeManager( - classNameFiltering, Duration.ofSeconds(config.getDebuggerExceptionCaptureInterval())), + classNameFiltering, + config.isCiVisibilityFailedTestReplayActive() + ? Duration.ofSeconds(0) + : Duration.ofSeconds(config.getDebuggerExceptionCaptureInterval())), configurationUpdater, classNameFiltering, config.getDebuggerMaxExceptionPerSecond(), @@ -81,15 +86,22 @@ public DefaultExceptionDebugger( @Override public void handleException(Throwable t, AgentSpan span) { - // CIVIS Failed Test Replay acts on errors - if (t instanceof Error && !isFailedTestReplayActive) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Skip handling error: {}", t.toString()); + if (isFailedTestReplayActive) { + String strategy = (String) span.getTag(DDTags.TEST_STRATEGY); + if (strategy == null || !strategy.equals(RetryReason.atr.toString())) { + // FTR is only applied to tests subject to Auto Test Retries + return; + } + } else { + if (t instanceof Error) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Skip handling error: {}", t.toString()); + } + return; + } + if (!circuitBreaker.trip()) { + return; } - return; - } - if (!circuitBreaker.trip()) { - return; } String fingerprint = Fingerprinter.fingerprint(t, classNameFiltering); if (fingerprint == null) { diff --git a/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy index a08881172fe..d1c9322bd84 100644 --- a/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy +++ b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy @@ -47,21 +47,22 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { givenProjectFiles(projectName) mockBackend.givenFlakyRetries(true) + mockBackend.givenFlakyTest("test-headless-service", "com.example.TestFailed", "test_failed") mockBackend.givenFailedTestReplay(true) def compileCode = compileTestProject() assert compileCode == 0 def exitCode = whenRunningJUnitConsole([ - "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_FLAKY_RETRY_COUNT)}=2" as String, + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_FLAKY_RETRY_COUNT)}=3" as String, "${Strings.propertyNameToSystemPropertyName(GeneralConfig.AGENTLESS_LOG_SUBMISSION_URL)}=${mockBackend.intakeUrl}" as String ], [:]) assert exitCode == 1 def additionalDynamicTags = ["content.meta.['_dd.debug.error.6.snapshot_id']", "content.meta.['_dd.debug.error.exception_id']"] - verifyEventsAndCoverages(projectName, "junit-console", "headless", mockBackend.waitForEvents(5), mockBackend.waitForCoverages(0), additionalDynamicTags) - verifySnapshotLogs(mockBackend.waitForLogs(4), 1) + verifyEventsAndCoverages(projectName, "junit-console", "headless", mockBackend.waitForEvents(7), mockBackend.waitForCoverages(0), additionalDynamicTags) + verifySnapshotLogs(mockBackend.waitForLogs(5), 1, 2) where: projectName = "test_junit_console_failed_test_replay" @@ -249,7 +250,9 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_GIT_UPLOAD_ENABLED)}=false," + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_AGENTLESS_URL)}=${mockBackend.intakeUrl}," + "${Strings.propertyNameToSystemPropertyName(GeneralConfig.SERVICE_NAME)}=${TEST_SERVICE_NAME}," + - "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_BUILD_INSTRUMENTATION_ENABLED)}=false," + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_BUILD_INSTRUMENTATION_ENABLED)}=false," + + "${Strings.propertyNameToSystemPropertyName(GeneralConfig.TRACE_DEBUG)}=true," + + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_FLAKY_RETRY_ONLY_KNOWN_FLAKES)}=true," agentArgument += additionalAgentArgs.join(",") diff --git a/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/coverages.ftl b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/coverages.ftl index 2f72ca231ee..e1e9336592a 100644 --- a/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/coverages.ftl +++ b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/coverages.ftl @@ -16,4 +16,22 @@ "span_id" : ${content_span_id_2}, "test_session_id" : ${content_test_session_id}, "test_suite_id" : ${content_test_suite_id} +}, { + "files" : [ { + "filename" : "src/main/java/com/example/Calculator.java" + }, { + "filename" : "src/test/java/com/example/TestFailed.java" + } ], + "span_id" : ${content_span_id_3}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} +}, { + "files" : [ { + "filename" : "src/main/java/com/example/Calculator.java" + }, { + "filename" : "src/test/java/com/example/TestFailed.java" + } ], + "span_id" : ${content_span_id_4}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} } ] \ No newline at end of file diff --git a/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/events.ftl b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/events.ftl index 36b7b6d98c3..b63697c6059 100644 --- a/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/events.ftl +++ b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/events.ftl @@ -76,9 +76,9 @@ "test.framework" : "junit5", "test.framework_version" : "5.13.4", "test.module" : "test-headless-service", - "test.name" : "test_failed", + "test.name" : "test_another_failed", "test.source.file" : "src/test/java/com/example/TestFailed.java", - "test.source.method" : "test_failed()V", + "test.source.method" : "test_another_failed()V", "test.status" : "fail", "test.suite" : "com.example.TestFailed", "test.type" : "test", @@ -89,12 +89,12 @@ "_dd.profiling.enabled" : 0, "_dd.trace_span_attribute_schema" : 0, "process_id" : ${content_metrics_process_id}, - "test.source.end" : 11, - "test.source.start" : 10 + "test.source.end" : 16, + "test.source.start" : 15 }, "name" : "junit5.test", "parent_id" : ${content_parent_id}, - "resource" : "com.example.TestFailed.test_failed", + "resource" : "com.example.TestFailed.test_another_failed", "service" : "test-headless-service", "span_id" : ${content_span_id}, "start" : ${content_start_2}, @@ -109,6 +109,71 @@ "content" : { "duration" : ${content_duration_3}, "error" : 1, + "meta" : { + "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", + "_dd.library_capabilities.impacted_tests" : "1", + "_dd.library_capabilities.test_impact_analysis" : "1", + "_dd.library_capabilities.test_management.attempt_to_fix" : "5", + "_dd.library_capabilities.test_management.disable" : "1", + "_dd.library_capabilities.test_management.quarantine" : "1", + "_dd.p.tid" : ${content_meta__dd_p_tid_3}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit5", + "env" : "integration-test", + "error.message" : ${content_meta_error_message}, + "error.stack" : ${content_meta_error_stack_2}, + "error.type" : "org.opentest4j.AssertionFailedError", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test", + "test.framework" : "junit5", + "test.framework_version" : "5.13.4", + "test.module" : "test-headless-service", + "test.name" : "test_failed", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.source.method" : "test_failed()V", + "test.status" : "fail", + "test.suite" : "com.example.TestFailed", + "test.type" : "test", + "test_session.name" : "test-headless-service" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_3}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id}, + "test.source.end" : 11, + "test.source.start" : 10 + }, + "name" : "junit5.test", + "parent_id" : ${content_parent_id}, + "resource" : "com.example.TestFailed.test_failed", + "service" : "test-headless-service", + "span_id" : ${content_span_id_2}, + "start" : ${content_start_3}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id}, + "trace_id" : ${content_trace_id_2} + }, + "type" : "test", + "version" : 2 +}, { + "content" : { + "duration" : ${content_duration_4}, + "error" : 1, "meta" : { "_dd.debug.error.6.file" : "TestFailed.java", "_dd.debug.error.6.snapshot_id" : ${content_meta__dd_debug_error_6_snapshot_id}, @@ -123,7 +188,7 @@ "_dd.library_capabilities.test_management.attempt_to_fix" : "5", "_dd.library_capabilities.test_management.disable" : "1", "_dd.library_capabilities.test_management.quarantine" : "1", - "_dd.p.tid" : ${content_meta__dd_p_tid_3}, + "_dd.p.tid" : ${content_meta__dd_p_tid_4}, "_dd.test.is_user_provided_service" : "true", "_dd.tracer_host" : ${content_meta__dd_tracer_host}, "ci.workspace_path" : ${content_meta_ci_workspace_path}, @@ -131,7 +196,80 @@ "env" : "integration-test", "error.debug_info_captured" : "true", "error.message" : ${content_meta_error_message}, - "error.stack" : ${content_meta_error_stack_2}, + "error.stack" : ${content_meta_error_stack_3}, + "error.type" : "org.opentest4j.AssertionFailedError", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test", + "test.framework" : "junit5", + "test.framework_version" : "5.13.4", + "test.is_retry" : "true", + "test.module" : "test-headless-service", + "test.name" : "test_failed", + "test.retry_reason" : "auto_test_retry", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.source.method" : "test_failed()V", + "test.status" : "fail", + "test.suite" : "com.example.TestFailed", + "test.type" : "test", + "test_session.name" : "test-headless-service" + }, + "metrics" : { + "_dd.debug.error.6.line" : 10, + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_4}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id}, + "test.source.end" : 11, + "test.source.start" : 10 + }, + "name" : "junit5.test", + "parent_id" : ${content_parent_id}, + "resource" : "com.example.TestFailed.test_failed", + "service" : "test-headless-service", + "span_id" : ${content_span_id_3}, + "start" : ${content_start_4}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id}, + "trace_id" : ${content_trace_id_3} + }, + "type" : "test", + "version" : 2 +}, { + "content" : { + "duration" : ${content_duration_5}, + "error" : 1, + "meta" : { + "_dd.debug.error.6.file" : "TestFailed.java", + "_dd.debug.error.6.snapshot_id" : ${content_meta__dd_debug_error_6_snapshot_id_2}, + "_dd.debug.error.exception_hash" : "2959ee575767ef8f3474753647822b7aaf286fc0bf8e273117a7ceb47b525", + "_dd.debug.error.exception_id" : ${content_meta__dd_debug_error_exception_id_2}, + "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", + "_dd.library_capabilities.impacted_tests" : "1", + "_dd.library_capabilities.test_impact_analysis" : "1", + "_dd.library_capabilities.test_management.attempt_to_fix" : "5", + "_dd.library_capabilities.test_management.disable" : "1", + "_dd.library_capabilities.test_management.quarantine" : "1", + "_dd.p.tid" : ${content_meta__dd_p_tid_5}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit5", + "env" : "integration-test", + "error.debug_info_captured" : "true", + "error.message" : ${content_meta_error_message}, + "error.stack" : ${content_meta_error_stack_4}, "error.type" : "org.opentest4j.AssertionFailedError", "language" : "jvm", "library_version" : ${content_meta_library_version}, @@ -159,7 +297,7 @@ }, "metrics" : { "_dd.debug.error.6.line" : 10, - "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_3}, + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_5}, "_dd.profiling.enabled" : 0, "_dd.trace_span_attribute_schema" : 0, "process_id" : ${content_metrics_process_id}, @@ -170,21 +308,21 @@ "parent_id" : ${content_parent_id}, "resource" : "com.example.TestFailed.test_failed", "service" : "test-headless-service", - "span_id" : ${content_span_id_2}, - "start" : ${content_start_3}, + "span_id" : ${content_span_id_4}, + "start" : ${content_start_5}, "test_module_id" : ${content_test_module_id}, "test_session_id" : ${content_test_session_id}, "test_suite_id" : ${content_test_suite_id}, - "trace_id" : ${content_trace_id_2} + "trace_id" : ${content_trace_id_4} }, "type" : "test", "version" : 2 }, { "content" : { - "duration" : ${content_duration_4}, + "duration" : ${content_duration_6}, "error" : 0, "meta" : { - "_dd.p.tid" : ${content_meta__dd_p_tid_4}, + "_dd.p.tid" : ${content_meta__dd_p_tid_6}, "_dd.test.is_user_provided_service" : "true", "_dd.tracer_host" : ${content_meta__dd_tracer_host}, "ci.workspace_path" : ${content_meta_ci_workspace_path}, @@ -211,7 +349,7 @@ "test_session.name" : "test-headless-service" }, "metrics" : { - "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_4}, + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_6}, "_dd.profiling.enabled" : 0, "_dd.trace_span_attribute_schema" : 0, "process_id" : ${content_metrics_process_id}, @@ -220,17 +358,17 @@ "name" : "junit5.test_session", "resource" : "test-headless-service", "service" : "test-headless-service", - "start" : ${content_start_4}, + "start" : ${content_start_6}, "test_session_id" : ${content_test_session_id} }, "type" : "test_session_end", "version" : 1 }, { "content" : { - "duration" : ${content_duration_5}, + "duration" : ${content_duration_7}, "error" : 0, "meta" : { - "_dd.p.tid" : ${content_meta__dd_p_tid_5}, + "_dd.p.tid" : ${content_meta__dd_p_tid_7}, "_dd.test.is_user_provided_service" : "true", "ci.workspace_path" : ${content_meta_ci_workspace_path}, "component" : "junit5", @@ -256,13 +394,13 @@ "test_session.name" : "test-headless-service" }, "metrics" : { - "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_5}, + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_7}, "test.itr.tests_skipping.count" : 0 }, "name" : "junit5.test_module", "resource" : "test-headless-service", "service" : "test-headless-service", - "start" : ${content_start_5}, + "start" : ${content_start_7}, "test_module_id" : ${content_test_module_id}, "test_session_id" : ${content_test_session_id} }, diff --git a/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/test/java/com/example/TestFailed.java b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/test/java/com/example/TestFailed.java index adcb8a03a9b..e768c32ea2d 100644 --- a/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/test/java/com/example/TestFailed.java +++ b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/src/test/java/com/example/TestFailed.java @@ -10,4 +10,9 @@ public void test_failed() { assertTrue(Calculator.add(2, 2) == 22); } + @Test + public void test_another_failed(){ + assertTrue(Calculator.add(1, 1) == 11); + } + } diff --git a/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy b/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy index f3a9ee21634..70d74b405e5 100644 --- a/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy +++ b/dd-smoke-tests/maven/src/test/groovy/datadog/smoketest/MavenSmokeTest.groovy @@ -241,7 +241,7 @@ class MavenSmokeTest extends CiVisibilitySmokeTest { mockBackend.givenFailedTestReplay(true) def exitCode = whenRunningMavenBuild([ - "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_FLAKY_RETRY_COUNT)}=2" as String, + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_FLAKY_RETRY_COUNT)}=3" as String, "${Strings.propertyNameToSystemPropertyName(GeneralConfig.AGENTLESS_LOG_SUBMISSION_URL)}=${mockBackend.intakeUrl}" as String ], [], @@ -249,8 +249,8 @@ class MavenSmokeTest extends CiVisibilitySmokeTest { assert exitCode == 1 def additionalDynamicTags = ["content.meta.['_dd.debug.error.3.snapshot_id']", "content.meta.['_dd.debug.error.exception_id']"] - verifyEventsAndCoverages(projectName, "maven", mavenVersion, mockBackend.waitForEvents(5), mockBackend.waitForCoverages(0), additionalDynamicTags) - verifySnapshotLogs(mockBackend.waitForLogs(4), 1) + verifyEventsAndCoverages(projectName, "maven", mavenVersion, mockBackend.waitForEvents(7), mockBackend.waitForCoverages(0), additionalDynamicTags) + verifySnapshotLogs(mockBackend.waitForLogs(5), 1, 2) where: projectName | mavenVersion diff --git a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/coverages.ftl b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/coverages.ftl index 0ccad374ea0..0c4d6ef93a3 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/coverages.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/coverages.ftl @@ -16,4 +16,22 @@ "span_id" : ${content_span_id_6}, "test_session_id" : ${content_test_session_id}, "test_suite_id" : ${content_test_suite_id} +}, { + "files" : [ { + "filename" : "src/main/java/com/example/Calculator.java" + }, { + "filename" : "src/test/java/com/example/TestFailed.java" + } ], + "span_id" : ${content_span_id_7}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} +}, { + "files" : [ { + "filename" : "src/main/java/com/example/Calculator.java" + }, { + "filename" : "src/test/java/com/example/TestFailed.java" + } ], + "span_id" : ${content_span_id_8}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id} } ] \ No newline at end of file diff --git a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/events.ftl index b10b68bb6b9..f7fa14e2e46 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/events.ftl +++ b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/events.ftl @@ -259,7 +259,7 @@ "_dd.profiling.enabled" : 0, "_dd.trace_span_attribute_schema" : 0, "process_id" : ${content_metrics_process_id_2}, - "test.source.end" : 14, + "test.source.end" : 19, "test.source.start" : 7 }, "name" : "junit4.test_suite", @@ -307,9 +307,9 @@ "test.framework" : "junit4", "test.framework_version" : "4.13.2", "test.module" : "Maven Smoke Tests Project maven-surefire-plugin default-test", - "test.name" : "test_failed", + "test.name" : "test_another_failed", "test.source.file" : "src/test/java/com/example/TestFailed.java", - "test.source.method" : "test_failed()V", + "test.source.method" : "test_another_failed()V", "test.status" : "fail", "test.suite" : "com.example.TestFailed", "test.type" : "test", @@ -320,12 +320,12 @@ "_dd.profiling.enabled" : 0, "_dd.trace_span_attribute_schema" : 0, "process_id" : ${content_metrics_process_id_2}, - "test.source.end" : 12, - "test.source.start" : 9 + "test.source.end" : 17, + "test.source.start" : 14 }, "name" : "junit4.test", "parent_id" : ${content_parent_id}, - "resource" : "com.example.TestFailed.test_failed", + "resource" : "com.example.TestFailed.test_another_failed", "service" : "test-maven-service", "span_id" : ${content_span_id_5}, "start" : ${content_start_8}, @@ -340,6 +340,70 @@ "content" : { "duration" : ${content_duration_9}, "error" : 1, + "meta" : { + "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", + "_dd.library_capabilities.impacted_tests" : "1", + "_dd.library_capabilities.test_impact_analysis" : "1", + "_dd.library_capabilities.test_management.attempt_to_fix" : "5", + "_dd.library_capabilities.test_management.disable" : "1", + "_dd.library_capabilities.test_management.quarantine" : "1", + "_dd.p.tid" : ${content_meta__dd_p_tid_9}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit4", + "env" : "integration-test", + "error.stack" : ${content_meta_error_stack_4}, + "error.type" : "java.lang.AssertionError", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id_2}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test", + "test.framework" : "junit4", + "test.framework_version" : "4.13.2", + "test.module" : "Maven Smoke Tests Project maven-surefire-plugin default-test", + "test.name" : "test_failed", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.source.method" : "test_failed()V", + "test.status" : "fail", + "test.suite" : "com.example.TestFailed", + "test.type" : "test", + "test_session.name" : "mvn -B test" + }, + "metrics" : { + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_5}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id_2}, + "test.source.end" : 12, + "test.source.start" : 9 + }, + "name" : "junit4.test", + "parent_id" : ${content_parent_id}, + "resource" : "com.example.TestFailed.test_failed", + "service" : "test-maven-service", + "span_id" : ${content_span_id_6}, + "start" : ${content_start_9}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id}, + "trace_id" : ${content_trace_id_2} + }, + "type" : "test", + "version" : 2 +}, { + "content" : { + "duration" : ${content_duration_10}, + "error" : 1, "meta" : { "_dd.debug.error.3.file" : "TestFailed.java", "_dd.debug.error.3.snapshot_id" : ${content_meta__dd_debug_error_3_snapshot_id}, @@ -354,14 +418,86 @@ "_dd.library_capabilities.test_management.attempt_to_fix" : "5", "_dd.library_capabilities.test_management.disable" : "1", "_dd.library_capabilities.test_management.quarantine" : "1", - "_dd.p.tid" : ${content_meta__dd_p_tid_9}, + "_dd.p.tid" : ${content_meta__dd_p_tid_10}, "_dd.test.is_user_provided_service" : "true", "_dd.tracer_host" : ${content_meta__dd_tracer_host}, "ci.workspace_path" : ${content_meta_ci_workspace_path}, "component" : "junit4", "env" : "integration-test", "error.debug_info_captured" : "true", - "error.stack" : ${content_meta_error_stack_4}, + "error.stack" : ${content_meta_error_stack_5}, + "error.type" : "java.lang.AssertionError", + "language" : "jvm", + "library_version" : ${content_meta_library_version}, + "os.architecture" : ${content_meta_os_architecture}, + "os.platform" : ${content_meta_os_platform}, + "os.version" : ${content_meta_os_version}, + "runtime-id" : ${content_meta_runtime_id_2}, + "runtime.name" : ${content_meta_runtime_name}, + "runtime.vendor" : ${content_meta_runtime_vendor}, + "runtime.version" : ${content_meta_runtime_version}, + "span.kind" : "test", + "test.framework" : "junit4", + "test.framework_version" : "4.13.2", + "test.is_retry" : "true", + "test.module" : "Maven Smoke Tests Project maven-surefire-plugin default-test", + "test.name" : "test_failed", + "test.retry_reason" : "auto_test_retry", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.source.method" : "test_failed()V", + "test.status" : "fail", + "test.suite" : "com.example.TestFailed", + "test.type" : "test", + "test_session.name" : "mvn -B test" + }, + "metrics" : { + "_dd.debug.error.3.line" : 11, + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_6}, + "_dd.profiling.enabled" : 0, + "_dd.trace_span_attribute_schema" : 0, + "process_id" : ${content_metrics_process_id_2}, + "test.source.end" : 12, + "test.source.start" : 9 + }, + "name" : "junit4.test", + "parent_id" : ${content_parent_id}, + "resource" : "com.example.TestFailed.test_failed", + "service" : "test-maven-service", + "span_id" : ${content_span_id_7}, + "start" : ${content_start_10}, + "test_module_id" : ${content_test_module_id}, + "test_session_id" : ${content_test_session_id}, + "test_suite_id" : ${content_test_suite_id}, + "trace_id" : ${content_trace_id_3} + }, + "type" : "test", + "version" : 2 +}, { + "content" : { + "duration" : ${content_duration_11}, + "error" : 1, + "meta" : { + "_dd.debug.error.3.file" : "TestFailed.java", + "_dd.debug.error.3.snapshot_id" : ${content_meta__dd_debug_error_3_snapshot_id_2}, + "_dd.debug.error.exception_hash" : "4e99779ed72c43d4b87387ee7cf4695cd904d2c4853b6054adcacfcfeffa", + "_dd.debug.error.exception_id" : ${content_meta__dd_debug_error_exception_id_2}, + "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.early_flake_detection" : "1", + "_dd.library_capabilities.fail_fast_test_order" : "1", + "_dd.library_capabilities.failed_test_replay" : "1", + "_dd.library_capabilities.impacted_tests" : "1", + "_dd.library_capabilities.test_impact_analysis" : "1", + "_dd.library_capabilities.test_management.attempt_to_fix" : "5", + "_dd.library_capabilities.test_management.disable" : "1", + "_dd.library_capabilities.test_management.quarantine" : "1", + "_dd.p.tid" : ${content_meta__dd_p_tid_11}, + "_dd.test.is_user_provided_service" : "true", + "_dd.tracer_host" : ${content_meta__dd_tracer_host}, + "ci.workspace_path" : ${content_meta_ci_workspace_path}, + "component" : "junit4", + "env" : "integration-test", + "error.debug_info_captured" : "true", + "error.stack" : ${content_meta_error_stack_6}, "error.type" : "java.lang.AssertionError", "language" : "jvm", "library_version" : ${content_meta_library_version}, @@ -389,7 +525,7 @@ }, "metrics" : { "_dd.debug.error.3.line" : 11, - "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_5}, + "_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_7}, "_dd.profiling.enabled" : 0, "_dd.trace_span_attribute_schema" : 0, "process_id" : ${content_metrics_process_id_2}, @@ -400,12 +536,12 @@ "parent_id" : ${content_parent_id}, "resource" : "com.example.TestFailed.test_failed", "service" : "test-maven-service", - "span_id" : ${content_span_id_6}, - "start" : ${content_start_9}, + "span_id" : ${content_span_id_8}, + "start" : ${content_start_11}, "test_module_id" : ${content_test_module_id}, "test_session_id" : ${content_test_session_id}, "test_suite_id" : ${content_test_suite_id}, - "trace_id" : ${content_trace_id_2} + "trace_id" : ${content_trace_id_4} }, "type" : "test", "version" : 2 diff --git a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/test/java/com/example/TestFailed.java b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/test/java/com/example/TestFailed.java index 7371566479a..503fae292a1 100644 --- a/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/test/java/com/example/TestFailed.java +++ b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/src/test/java/com/example/TestFailed.java @@ -11,4 +11,9 @@ public void test_failed() { assertTrue(Calculator.add(2, 2) == 22); } + @Test + public void test_another_failed() { + assertTrue(Calculator.add(1,1) == 11); + } + } diff --git a/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java b/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java index 8df03a13011..e00542ffc98 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java @@ -78,6 +78,7 @@ public class DDTags { public static final String CI_ENV_VARS = "_dd.ci.env_vars"; public static final String CI_ITR_TESTS_SKIPPED = "_dd.ci.itr.tests_skipped"; public static final String TEST_IS_USER_PROVIDED_SERVICE = "_dd.test.is_user_provided_service"; + public static final String TEST_STRATEGY = "_dd.test.strategy"; public static final String MEASURED = "_dd.measured"; public static final String PID_TAG = "process_id"; public static final String SCHEMA_VERSION_TAG_KEY = "_dd.trace_span_attribute_schema"; From b4cef2cf24d76fa9962593cf7745d40404e6e8fc Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Fri, 22 Aug 2025 12:25:40 +0200 Subject: [PATCH 20/24] feat: PR suggestions --- .../civisibility/CiVisibilitySystem.java | 5 ---- .../trace/civisibility/domain/TestImpl.java | 4 +++ .../civisibility/domain/TestSuiteImpl.java | 3 -- .../events/TestEventsHandlerImpl.java | 12 ++------ .../trace/civisibility/execution/Regular.java | 5 ++++ .../execution/RetryUntilSuccessful.java | 5 ++++ .../civisibility/execution/RunNTimes.java | 5 ++++ .../execution/RunOnceIgnoreOutcome.java | 5 ++++ .../agent/JsonSnapshotSerializer.java | 2 +- .../exception/DefaultExceptionDebugger.java | 30 +++++++++++-------- .../datadog/debugger/sink/DebuggerSink.java | 27 +---------------- .../debugger/sink/DebuggerSinkTest.java | 5 ++-- .../selenium/SeleniumTestListener.java | 5 ---- .../main/java/datadog/trace/api/DDTags.java | 1 - .../main/java/datadog/trace/api/Config.java | 15 ++-------- .../InstrumentationTestBridge.java | 8 ----- .../execution/TestExecutionPolicy.java | 3 ++ 17 files changed, 54 insertions(+), 86 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java index 85b6e96b526..575e9a38442 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java @@ -101,11 +101,6 @@ public static void start(Instrumentation inst, SharedCommunicationObjects sco) { inst.addTransformer(new CoverageClassTransformer(instrumentationFilter)); } - if (executionSettings.isFailedTestReplayEnabled()) { - // only marks the feature as active in child or headless processes - config.setCiVisibilityFailedTestReplayActive(true); - } - CiVisibilityCoverageServices.Child coverageServices = new CiVisibilityCoverageServices.Child(services, repoServices, executionSettings); TestEventsHandlerFactory testEventsHandlerFactory = diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java index 8e04e5527f9..bd5513a2821 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestImpl.java @@ -244,6 +244,10 @@ public long getDuration(@Nullable Long endMicros) { return TimeUnit.MICROSECONDS.toMillis(endMicros - startMicros); } + public TestContext getContext() { + return context; + } + @Override public void end(@Nullable Long endTime) { closeOutstandingSpans(); diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java index 89f509ad4cd..c29fa175229 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java @@ -6,7 +6,6 @@ import datadog.trace.api.Config; import datadog.trace.api.civisibility.DDTestSuite; -import datadog.trace.api.civisibility.InstrumentationTestBridge; import datadog.trace.api.civisibility.config.LibraryCapability; import datadog.trace.api.civisibility.coverage.CoverageStore; import datadog.trace.api.civisibility.execution.TestStatus; @@ -221,8 +220,6 @@ public void end(@Nullable Long endTime) { AgentTracer.closeActive(); } - InstrumentationTestBridge.fireBeforeSuiteEnd(); - onSpanFinish.accept(span); if (endTime != null) { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java index eb10963913d..e7ee215b33f 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java @@ -1,7 +1,6 @@ package datadog.trace.civisibility.events; import datadog.json.JsonWriter; -import datadog.trace.api.DDTags; import datadog.trace.api.DisableTestTrace; import datadog.trace.api.civisibility.CIConstants; import datadog.trace.api.civisibility.DDTest; @@ -24,7 +23,6 @@ import datadog.trace.civisibility.domain.TestFrameworkSession; import datadog.trace.civisibility.domain.TestImpl; import datadog.trace.civisibility.domain.TestSuiteImpl; -import datadog.trace.civisibility.execution.RetryUntilSuccessful; import java.util.Collection; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -162,6 +160,9 @@ public void onTestStart( testSuite.testStart(testName, testParameters, testSourceData.getTestMethod(), startTime); TestIdentifier thisTest = test.getIdentifier(); + test.getContext() + .set(TestExecutionPolicy.class, executionPolicy(thisTest, testSourceData, categories)); + if (testModule.isNew(thisTest)) { test.setTag(Tags.TEST_IS_NEW, true); } @@ -183,10 +184,6 @@ public void onTestStart( } if (testExecutionHistory != null) { - if (testExecutionHistory instanceof RetryUntilSuccessful) { - // Used by FailedTestReplay to limit the instrumentation to AutoTestRetries - test.setTag(DDTags.TEST_STRATEGY, RetryReason.atr.toString()); - } RetryReason retryReason = testExecutionHistory.currentExecutionRetryReason(); if (retryReason != null) { test.setTag(Tags.TEST_IS_RETRY, true); @@ -277,9 +274,6 @@ public void onTestFinish( } } - // Remove strategy tag used by FTR if it was set - test.setTag(DDTags.TEST_STRATEGY, null); - test.end(endTime); } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/Regular.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/Regular.java index 434892bc98f..2883f592d5f 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/Regular.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/Regular.java @@ -45,4 +45,9 @@ public boolean hasFailedAllRetries() { public boolean hasSucceededAllRetries() { return false; } + + @Override + public boolean failedTestReplayApplicable() { + return false; + } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RetryUntilSuccessful.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RetryUntilSuccessful.java index 81fdeaa6c2d..2e6b0e05a0a 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RetryUntilSuccessful.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RetryUntilSuccessful.java @@ -76,4 +76,9 @@ public boolean hasFailedAllRetries() { public boolean hasSucceededAllRetries() { return false; } + + @Override + public boolean failedTestReplayApplicable() { + return true; + } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RunNTimes.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RunNTimes.java index 7201bb99c02..9210ba97c56 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RunNTimes.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RunNTimes.java @@ -87,4 +87,9 @@ public boolean hasFailedAllRetries() { public boolean hasSucceededAllRetries() { return wasLastExecution() && successfulExecutionsSeen == executions; } + + @Override + public boolean failedTestReplayApplicable() { + return false; + } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RunOnceIgnoreOutcome.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RunOnceIgnoreOutcome.java index 83c25e98514..e6b20f2d1b0 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RunOnceIgnoreOutcome.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RunOnceIgnoreOutcome.java @@ -48,4 +48,9 @@ public boolean hasFailedAllRetries() { public boolean hasSucceededAllRetries() { return false; } + + @Override + public boolean failedTestReplayApplicable() { + return false; + } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java index 8dd27ffc590..b4dc8eb60b9 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java @@ -96,7 +96,7 @@ public IntakeRequest(String service, DebuggerIntakeRequestData debugger, Config this.timestamp = debugger.snapshot.getTimestamp(); final CharSequence pt = ProcessTags.getTagsForSerialization(); this.processTags = pt != null ? pt.toString() : null; - this.product = config.isCiVisibilityFailedTestReplayActive() ? TEST_OPT_PRODUCT : null; + this.product = config.isCiVisibilityEnabled() ? TEST_OPT_PRODUCT : null; } public String getService() { diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java index 475ff2011fd..7b5966e848f 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java @@ -10,8 +10,9 @@ import com.datadog.debugger.util.CircuitBreaker; import com.datadog.debugger.util.ExceptionHelper; import datadog.trace.api.Config; -import datadog.trace.api.DDTags; -import datadog.trace.api.civisibility.telemetry.tag.RetryReason; +import datadog.trace.api.civisibility.InstrumentationTestBridge; +import datadog.trace.api.civisibility.domain.TestContext; +import datadog.trace.api.civisibility.execution.TestExecutionPolicy; import datadog.trace.bootstrap.debugger.DebuggerContext; import datadog.trace.bootstrap.debugger.DebuggerContext.ClassNameFilter; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -47,7 +48,7 @@ public class DefaultExceptionDebugger implements DebuggerContext.ExceptionDebugg private final CircuitBreaker circuitBreaker; private final int maxCapturedFrames; private final boolean applyConfigAsync; - private final boolean isFailedTestReplayActive; + private final boolean failedTestReplayMode; public DefaultExceptionDebugger( ConfigurationUpdater configurationUpdater, @@ -56,7 +57,7 @@ public DefaultExceptionDebugger( this( new ExceptionProbeManager( classNameFiltering, - config.isCiVisibilityFailedTestReplayActive() + config.isCiVisibilityEnabled() ? Duration.ofSeconds(0) : Duration.ofSeconds(config.getDebuggerExceptionCaptureInterval())), configurationUpdater, @@ -64,7 +65,7 @@ public DefaultExceptionDebugger( config.getDebuggerMaxExceptionPerSecond(), config.getDebuggerExceptionMaxCapturedFrames(), config.isDebuggerExceptionAsyncConfig(), - config.isCiVisibilityFailedTestReplayActive()); + config.isCiVisibilityEnabled()); } DefaultExceptionDebugger( @@ -74,22 +75,25 @@ public DefaultExceptionDebugger( int maxExceptionPerSecond, int maxCapturedFrames, boolean applyConfigAsync, - boolean isFailedTestReplayActive) { + boolean failedTestReplayMode) { this.exceptionProbeManager = exceptionProbeManager; this.configurationUpdater = configurationUpdater; this.classNameFiltering = classNameFiltering; this.circuitBreaker = new CircuitBreaker(maxExceptionPerSecond, Duration.ofSeconds(1)); this.maxCapturedFrames = maxCapturedFrames; this.applyConfigAsync = applyConfigAsync; - this.isFailedTestReplayActive = isFailedTestReplayActive; + this.failedTestReplayMode = failedTestReplayMode; } @Override public void handleException(Throwable t, AgentSpan span) { - if (isFailedTestReplayActive) { - String strategy = (String) span.getTag(DDTags.TEST_STRATEGY); - if (strategy == null || !strategy.equals(RetryReason.atr.toString())) { - // FTR is only applied to tests subject to Auto Test Retries + if (failedTestReplayMode) { + TestContext testContext = InstrumentationTestBridge.getCurrentTestContext(); + if (testContext == null) { + return; + } + TestExecutionPolicy executionPolicy = testContext.get(TestExecutionPolicy.class); + if (executionPolicy == null || !executionPolicy.failedTestReplayApplicable()) { return; } } else { @@ -128,7 +132,7 @@ public void handleException(Throwable t, AgentSpan span) { chainedExceptionsList, fingerprint, maxCapturedFrames, - isFailedTestReplayActive); + failedTestReplayMode); exceptionProbeManager.updateLastCapture(fingerprint); } else { // climb up the exception chain to find the first exception that has instrumented frames @@ -139,7 +143,7 @@ public void handleException(Throwable t, AgentSpan span) { exceptionProbeManager.createProbesForException( throwable.getStackTrace(), chainedExceptionIdx); if (creationResult.probesCreated > 0) { - if (isFailedTestReplayActive || !applyConfigAsync) { + if (failedTestReplayMode || !applyConfigAsync) { applyExceptionConfiguration(fingerprint); } else { AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/DebuggerSink.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/DebuggerSink.java index d22516ae122..fb0b6950a46 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/DebuggerSink.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/sink/DebuggerSink.java @@ -4,8 +4,6 @@ import com.datadog.debugger.uploader.BatchUploader; import com.datadog.debugger.util.DebuggerMetrics; import datadog.trace.api.Config; -import datadog.trace.api.civisibility.InstrumentationTestBridge; -import datadog.trace.api.civisibility.domain.TestContext; import datadog.trace.bootstrap.debugger.DebuggerContext.SkipCause; import datadog.trace.bootstrap.debugger.ProbeId; import datadog.trace.util.AgentTaskScheduler; @@ -70,11 +68,6 @@ public DebuggerSink( this.snapshotSink = snapshotSink; this.symbolSink = symbolSink; this.uploadFlushInterval = config.getDynamicInstrumentationUploadFlushInterval(); - - if (config.isCiVisibilityFailedTestReplayActive()) { - // register test listener to flush snapshots on suite end - InstrumentationTestBridge.registerListener(new DebuggerTestListener(this)); - } } public void start() { @@ -93,6 +86,7 @@ public void start() { } public void stop() { + lowRateFlush(this); cancelSchedule(this.flushIntervalScheduled); cancelSchedule(this.lowRateScheduled); probeStatusSink.stop(); @@ -242,23 +236,4 @@ public void skipSnapshot(String probeId, SkipCause cause) { long getCurrentLowRateFlushInterval() { return currentLowRateFlushInterval; } - - static class DebuggerTestListener implements InstrumentationTestBridge.TestListener { - private final DebuggerSink sink; - - DebuggerTestListener(DebuggerSink sink) { - this.sink = sink; - } - - @Override - public void beforeTestEnd(TestContext ignored) { - // noop - } - - @Override - public void beforeSuiteEnd() { - LOGGER.debug("CiVisibility BeforeSuiteEnd fired, flushing sink"); - sink.lowRateFlush(sink); - } - } } diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java index 37cac100c6f..eb7bc09a81d 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/DebuggerSinkTest.java @@ -27,7 +27,6 @@ import com.squareup.moshi.Types; import datadog.trace.api.Config; import datadog.trace.api.ProcessTags; -import datadog.trace.api.civisibility.InstrumentationTestBridge; import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.CapturedContext.CapturedValue; import datadog.trace.bootstrap.debugger.CapturedStackFrame; @@ -127,13 +126,13 @@ public void addSnapshot(boolean processTagsEnabled) throws IOException { @Test public void addFailedTestReplaySnapshot() throws IOException { - when(config.isCiVisibilityFailedTestReplayActive()).thenReturn(true); + when(config.isCiVisibilityEnabled()).thenReturn(true); ProcessTags.reset(config); DebuggerSink sink = createDefaultDebuggerSink(); DebuggerAgentHelper.injectSerializer(new JsonSnapshotSerializer()); Snapshot snapshot = createSnapshot(); sink.addSnapshot(snapshot); - InstrumentationTestBridge.fireBeforeSuiteEnd(); // flush on suite end + sink.lowRateFlush(sink); verify(batchUploader).upload(payloadCaptor.capture(), matches(EXPECTED_SNAPSHOT_TAGS)); String strPayload = new String(payloadCaptor.getValue(), StandardCharsets.UTF_8); System.out.println(strPayload); diff --git a/dd-java-agent/instrumentation/selenium/src/main/java/datadog/trace/instrumentation/selenium/SeleniumTestListener.java b/dd-java-agent/instrumentation/selenium/src/main/java/datadog/trace/instrumentation/selenium/SeleniumTestListener.java index d9c10f8f0f5..9b2d93af88b 100644 --- a/dd-java-agent/instrumentation/selenium/src/main/java/datadog/trace/instrumentation/selenium/SeleniumTestListener.java +++ b/dd-java-agent/instrumentation/selenium/src/main/java/datadog/trace/instrumentation/selenium/SeleniumTestListener.java @@ -25,9 +25,4 @@ public void beforeTestEnd(TestContext testContext) { SeleniumUtils.beforePageClose(driver); } - - @Override - public void beforeSuiteEnd() { - // noop - } } diff --git a/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java b/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java index e00542ffc98..8df03a13011 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java @@ -78,7 +78,6 @@ public class DDTags { public static final String CI_ENV_VARS = "_dd.ci.env_vars"; public static final String CI_ITR_TESTS_SKIPPED = "_dd.ci.itr.tests_skipped"; public static final String TEST_IS_USER_PROVIDED_SERVICE = "_dd.test.is_user_provided_service"; - public static final String TEST_STRATEGY = "_dd.test.strategy"; public static final String MEASURED = "_dd.measured"; public static final String PID_TAG = "process_id"; public static final String SCHEMA_VERSION_TAG_KEY = "_dd.trace_span_attribute_schema"; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index a888d614bfa..4054527b46c 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -1035,7 +1035,6 @@ public static String getHostName() { private final String gitPullRequestBaseBranchSha; private final String gitCommitHeadSha; private final boolean ciVisibilityFailedTestReplayEnabled; - private boolean ciVisibilityFailedTestReplayActive = false; // propagates setting to DI private final boolean remoteConfigEnabled; private final boolean remoteConfigIntegrityCheckEnabled; @@ -3957,14 +3956,6 @@ public boolean isCiVisibilityFailedTestReplayEnabled() { return ciVisibilityFailedTestReplayEnabled; } - public boolean isCiVisibilityFailedTestReplayActive() { - return ciVisibilityFailedTestReplayActive; - } - - public void setCiVisibilityFailedTestReplayActive(boolean enabled) { - ciVisibilityFailedTestReplayActive = enabled; - } - public String getGitPullRequestBaseBranch() { return gitPullRequestBaseBranch; } @@ -4086,7 +4077,7 @@ public boolean isSymbolDatabaseCompressed() { } public boolean isDebuggerExceptionEnabled() { - return debuggerExceptionEnabled || isCiVisibilityFailedTestReplayActive(); + return debuggerExceptionEnabled; } public int getDebuggerMaxExceptionPerSecond() { @@ -4153,7 +4144,7 @@ private String getFinalDebuggerBaseUrl() { public String getFinalDebuggerSnapshotUrl() { if (Strings.isNotBlank(dynamicInstrumentationSnapshotUrl)) { return dynamicInstrumentationSnapshotUrl; - } else if (isCiVisibilityFailedTestReplayActive() && isCiVisibilityAgentlessEnabled()) { + } else if (isCiVisibilityAgentlessEnabled()) { return Intake.LOGS.getAgentlessUrl(this) + "logs"; } else { return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; @@ -4161,7 +4152,7 @@ public String getFinalDebuggerSnapshotUrl() { } public String getFinalDebuggerSymDBUrl() { - if (isCiVisibilityFailedTestReplayActive() && isCiVisibilityAgentlessEnabled()) { + if (isCiVisibilityAgentlessEnabled()) { return Intake.LOGS.getAgentlessUrl(this) + "logs"; } else { return getFinalDebuggerBaseUrl() + "/symdb/v1/input"; diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/InstrumentationTestBridge.java b/internal-api/src/main/java/datadog/trace/api/civisibility/InstrumentationTestBridge.java index e69f69ac390..084cc1a2926 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/InstrumentationTestBridge.java +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/InstrumentationTestBridge.java @@ -38,19 +38,11 @@ public static void fireBeforeTestEnd(TestContext testContext) { } } - public static void fireBeforeSuiteEnd() { - for (TestListener testListener : TEST_LISTENERS) { - testListener.beforeSuiteEnd(); - } - } - public static void registerListener(TestListener listener) { TEST_LISTENERS.addIfAbsent(listener); } public interface TestListener { void beforeTestEnd(TestContext testContext); - - void beforeSuiteEnd(); } } diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionPolicy.java b/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionPolicy.java index 00de8d306e4..c40bca00c3b 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionPolicy.java +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionPolicy.java @@ -20,4 +20,7 @@ public interface TestExecutionPolicy extends TestExecutionHistory { * result */ boolean suppressFailures(); + + /** @return {@code true} if the test should be instrumented by FTR */ + boolean failedTestReplayApplicable(); } From d82a7fa8fd480437c9325bb44f1532bdf1962959 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 27 Aug 2025 10:50:14 +0200 Subject: [PATCH 21/24] feat: implement debugger config bridge --- .../civisibility/CiVisibilitySystem.java | 7 ++ .../buildsystem/BuildSystemModuleImpl.java | 8 -- .../bootstrap/debugger/DebuggerContext.java | 74 ------------- .../datadog/debugger/agent/DebuggerAgent.java | 4 +- ...java => DefaultDebuggerConfigUpdater.java} | 21 ++-- ... => DefaultDebuggerConfigUpdaterTest.java} | 12 +- .../ExceptionProbeInstrumentationTest.java | 7 +- .../smoketest/JUnitConsoleSmokeTest.groovy | 3 +- .../main/java/datadog/trace/core/DDSpan.java | 3 +- .../trace/core/TracingConfigPoller.java | 15 ++- .../api/debugger/DebuggerConfigBridge.java | 83 ++++++++++++++ .../api/debugger/DebuggerConfigUpdate.java | 104 ++++++++++++++++++ .../api/debugger/DebuggerConfigUpdater.java | 13 +++ .../debugger/DebuggerConfigBridgeTest.groovy | 57 ++++++++++ 14 files changed, 298 insertions(+), 113 deletions(-) rename dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/{DefaultProductConfigUpdater.java => DefaultDebuggerConfigUpdater.java} (81%) rename dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/{DefaultProductConfigUpdaterTest.java => DefaultDebuggerConfigUpdaterTest.java} (79%) create mode 100644 internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigBridge.java create mode 100644 internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdate.java create mode 100644 internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdater.java create mode 100644 internal-api/src/test/groovy/datadog/trace/api/debugger/DebuggerConfigBridgeTest.groovy diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java index 575e9a38442..a9844c5cf23 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java @@ -13,6 +13,8 @@ import datadog.trace.api.civisibility.events.TestEventsHandler; import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector; import datadog.trace.api.civisibility.telemetry.NoOpMetricCollector; +import datadog.trace.api.debugger.DebuggerConfigBridge; +import datadog.trace.api.debugger.DebuggerConfigUpdate; import datadog.trace.api.git.GitInfoProvider; import datadog.trace.bootstrap.ContextStore; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; @@ -101,6 +103,11 @@ public static void start(Instrumentation inst, SharedCommunicationObjects sco) { inst.addTransformer(new CoverageClassTransformer(instrumentationFilter)); } + if (executionSettings.isFailedTestReplayEnabled()) { + DebuggerConfigBridge.updateConfig( + new DebuggerConfigUpdate.Builder().setExceptionReplayEnabled(true).build()); + } + CiVisibilityCoverageServices.Child coverageServices = new CiVisibilityCoverageServices.Child(services, repoServices, executionSettings); TestEventsHandlerFactory testEventsHandlerFactory = diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java index 8e5c3bd19cd..0b23df19e48 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java @@ -13,7 +13,6 @@ import datadog.trace.api.civisibility.domain.JavaAgent; import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector; import datadog.trace.api.config.CiVisibilityConfig; -import datadog.trace.api.config.DebuggerConfig; import datadog.trace.api.config.GeneralConfig; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; @@ -186,13 +185,6 @@ private Map getPropertiesPropagatedToChildProcess( CiVisibilityConfig.TEST_FAILED_TEST_REPLAY_ENABLED), Boolean.toString(executionSettings.isFailedTestReplayEnabled())); - // enable exception replay if failed test replay is enabled - if (executionSettings.isFailedTestReplayEnabled()) { - propagatedSystemProperties.put( - Strings.propertyNameToSystemPropertyName(DebuggerConfig.EXCEPTION_REPLAY_ENABLED), - "true"); - } - // explicitly disable build instrumentation in child processes, // because some projects run "embedded" Maven/Gradle builds as part of their integration tests, // and we don't want to show those as if they were regular build executions diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java index e5a6926baab..12ae1b21f7e 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java @@ -50,22 +50,6 @@ public String tag() { public abstract String tag(); } - public interface ProductConfigUpdater { - void updateConfig( - Boolean dynamicInstrumentationEnabled, - Boolean exceptionReplayEnabled, - Boolean codeOriginEnabled, - Boolean liveDebuggingEnabled); - - boolean isDynamicInstrumentationEnabled(); - - boolean isExceptionReplayEnabled(); - - boolean isCodeOriginEnabled(); - - boolean isDistributedDebuggerEnabled(); - } - public interface ProbeResolver { ProbeImplementation resolve(String encodedProbeId); } @@ -119,7 +103,6 @@ public interface CodeOriginRecorder { String captureCodeOrigin(Method method, boolean entry); } - private static volatile ProductConfigUpdater productConfigUpdater; private static volatile ProbeResolver probeResolver; private static volatile ClassFilter classFilter; private static volatile ClassNameFilter classNameFilter; @@ -129,10 +112,6 @@ public interface CodeOriginRecorder { private static volatile ExceptionDebugger exceptionDebugger; private static volatile CodeOriginRecorder codeOriginRecorder; - public static void initProductConfigUpdater(ProductConfigUpdater productConfigUpdater) { - DebuggerContext.productConfigUpdater = productConfigUpdater; - } - public static void initProbeResolver(ProbeResolver probeResolver) { DebuggerContext.probeResolver = probeResolver; } @@ -165,59 +144,6 @@ public static void initCodeOrigin(CodeOriginRecorder codeOriginRecorder) { DebuggerContext.codeOriginRecorder = codeOriginRecorder; } - public static void updateConfig( - Boolean dynamicInstrumentationEnabled, - Boolean exceptionReplayEnabled, - Boolean codeOriginEnabled, - Boolean liveDebuggingEnabled) { - LOGGER.debug( - "Updating config: dynamicInstrumentationEnabled: {}, exceptionReplayEnabled: {}, codeOriginEnabled: {}, liveDebuggingEnabled: {}", - dynamicInstrumentationEnabled, - exceptionReplayEnabled, - codeOriginEnabled, - liveDebuggingEnabled); - ProductConfigUpdater updater = productConfigUpdater; - if (updater != null) { - updater.updateConfig( - dynamicInstrumentationEnabled, - exceptionReplayEnabled, - codeOriginEnabled, - liveDebuggingEnabled); - } - } - - public static boolean isDynamicInstrumentationEnabled() { - ProductConfigUpdater updater = productConfigUpdater; - if (updater != null) { - return updater.isDynamicInstrumentationEnabled(); - } - return Config.get().isDynamicInstrumentationEnabled(); - } - - public static boolean isExceptionReplayEnabled() { - ProductConfigUpdater updater = productConfigUpdater; - if (updater != null) { - return updater.isExceptionReplayEnabled(); - } - return Config.get().isDebuggerExceptionEnabled(); - } - - public static boolean isCodeOriginEnabled() { - ProductConfigUpdater updater = productConfigUpdater; - if (updater != null) { - return updater.isCodeOriginEnabled(); - } - return Config.get().isDebuggerCodeOriginEnabled(); - } - - public static boolean isDistributedDebuggerEnabled() { - ProductConfigUpdater updater = productConfigUpdater; - if (updater != null) { - return updater.isDistributedDebuggerEnabled(); - } - return Config.get().isDistributedDebuggerEnabled(); - } - /** * Returns the probe details based on the probe id provided. If no probe is found, try to * re-transform the class using the callingClass parameter No-op if no implementation available diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java index eabf4752fca..43bc126f0be 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java @@ -25,6 +25,7 @@ import datadog.remoteconfig.ConfigurationPoller; import datadog.remoteconfig.Product; import datadog.trace.api.Config; +import datadog.trace.api.debugger.DebuggerConfigBridge; import datadog.trace.api.flare.TracerFlare; import datadog.trace.api.git.GitInfo; import datadog.trace.api.git.GitInfoProvider; @@ -73,9 +74,10 @@ public static synchronized void run(Instrumentation inst, SharedCommunicationObj instrumentation = inst; sharedCommunicationObjects = sco; Config config = Config.get(); - DebuggerContext.initProductConfigUpdater(new DefaultProductConfigUpdater()); classesToRetransformFinder = new ClassesToRetransformFinder(); setupSourceFileTracking(instrumentation, classesToRetransformFinder); + // set config updater after setup is done, as some deferred updates might be immediately called + DebuggerConfigBridge.setUpdater(new DefaultDebuggerConfigUpdater()); if (config.isDebuggerCodeOriginEnabled()) { startCodeOriginForSpans(); } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DefaultProductConfigUpdater.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DefaultDebuggerConfigUpdater.java similarity index 81% rename from dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DefaultProductConfigUpdater.java rename to dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DefaultDebuggerConfigUpdater.java index 3b71b4c985b..a7256bed4ee 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DefaultProductConfigUpdater.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DefaultDebuggerConfigUpdater.java @@ -3,38 +3,35 @@ import datadog.trace.api.Config; import datadog.trace.api.config.DebuggerConfig; import datadog.trace.api.config.TraceInstrumentationConfig; -import datadog.trace.bootstrap.debugger.DebuggerContext; +import datadog.trace.api.debugger.DebuggerConfigUpdate; +import datadog.trace.api.debugger.DebuggerConfigUpdater; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -class DefaultProductConfigUpdater implements DebuggerContext.ProductConfigUpdater { +class DefaultDebuggerConfigUpdater implements DebuggerConfigUpdater { - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultProductConfigUpdater.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDebuggerConfigUpdater.class); @Override - public void updateConfig( - Boolean dynamicInstrumentationEnabled, - Boolean exceptionReplayEnabled, - Boolean codeOriginEnabled, - Boolean liveDebuggingEnabled) { + public void updateConfig(DebuggerConfigUpdate update) { startOrStopFeature( DebuggerConfig.DYNAMIC_INSTRUMENTATION_ENABLED, - dynamicInstrumentationEnabled, + update.getDynamicInstrumentationEnabled(), DebuggerAgent::startDynamicInstrumentation, DebuggerAgent::stopDynamicInstrumentation); startOrStopFeature( DebuggerConfig.EXCEPTION_REPLAY_ENABLED, - exceptionReplayEnabled, + update.getExceptionReplayEnabled(), DebuggerAgent::startExceptionReplay, DebuggerAgent::stopExceptionReplay); startOrStopFeature( TraceInstrumentationConfig.CODE_ORIGIN_FOR_SPANS_ENABLED, - codeOriginEnabled, + update.getCodeOriginEnabled(), DebuggerAgent::startCodeOriginForSpans, DebuggerAgent::stopCodeOriginForSpans); startOrStopFeature( DebuggerConfig.DISTRIBUTED_DEBUGGER_ENABLED, - liveDebuggingEnabled, + update.getLiveDebuggingEnabled(), DebuggerAgent::startDistributedDebugger, DebuggerAgent::stopDistributedDebugger); } diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultProductConfigUpdaterTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultDebuggerConfigUpdaterTest.java similarity index 79% rename from dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultProductConfigUpdaterTest.java rename to dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultDebuggerConfigUpdaterTest.java index 16e1ea7c46e..544326aee46 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultProductConfigUpdaterTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultDebuggerConfigUpdaterTest.java @@ -10,26 +10,26 @@ import java.lang.instrument.Instrumentation; import org.junit.jupiter.api.Test; -class DefaultProductConfigUpdaterTest { +class DefaultDebuggerConfigUpdaterTest { @Test public void enableDisable() { SharedCommunicationObjects sco = mock(SharedCommunicationObjects.class); when(sco.featuresDiscovery(any())).thenReturn(mock(DDAgentFeaturesDiscovery.class)); DebuggerAgent.run(mock(Instrumentation.class), sco); - DefaultProductConfigUpdater productConfigUpdater = new DefaultProductConfigUpdater(); - productConfigUpdater.updateConfig(null, null, null, null); - productConfigUpdater.updateConfig(true, true, true, true); + DefaultDebuggerConfigUpdater productConfigUpdater = new DefaultDebuggerConfigUpdater(); + productConfigUpdater.updateConfig(null); + productConfigUpdater.updateConfig(true); assertTrue(productConfigUpdater.isDynamicInstrumentationEnabled()); assertTrue(productConfigUpdater.isExceptionReplayEnabled()); assertTrue(productConfigUpdater.isCodeOriginEnabled()); assertTrue(productConfigUpdater.isDistributedDebuggerEnabled()); - productConfigUpdater.updateConfig(null, null, null, null); + productConfigUpdater.updateConfig(null); assertTrue(productConfigUpdater.isDynamicInstrumentationEnabled()); assertTrue(productConfigUpdater.isExceptionReplayEnabled()); assertTrue(productConfigUpdater.isCodeOriginEnabled()); assertTrue(productConfigUpdater.isDistributedDebuggerEnabled()); - productConfigUpdater.updateConfig(false, false, false, false); + productConfigUpdater.updateConfig(false); assertFalse(productConfigUpdater.isDynamicInstrumentationEnabled()); assertFalse(productConfigUpdater.isExceptionReplayEnabled()); assertFalse(productConfigUpdater.isCodeOriginEnabled()); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java index 976287d0cb4..84e796e8556 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java @@ -31,6 +31,8 @@ import com.datadog.debugger.util.TestTraceInterceptor; import datadog.trace.agent.tooling.TracerInstaller; import datadog.trace.api.Config; +import datadog.trace.api.debugger.DebuggerConfigBridge; +import datadog.trace.api.debugger.DebuggerConfigUpdater; import datadog.trace.api.interceptor.MutableSpan; import datadog.trace.bootstrap.debugger.DebuggerContext; import datadog.trace.bootstrap.debugger.DebuggerContext.ClassNameFilter; @@ -98,10 +100,9 @@ public void before() { ProbeRateLimiter.setSamplerSupplier(rate -> rate < 101 ? probeSampler : globalSampler); ProbeRateLimiter.setGlobalSnapshotRate(1000); // to activate the call to DebuggerContext.handleException - DebuggerContext.ProductConfigUpdater mockProductConfigUpdater = - mock(DebuggerContext.ProductConfigUpdater.class); + DebuggerConfigUpdater mockProductConfigUpdater = mock(DebuggerConfigUpdater.class); when(mockProductConfigUpdater.isExceptionReplayEnabled()).thenReturn(true); - DebuggerContext.initProductConfigUpdater(mockProductConfigUpdater); + DebuggerConfigBridge.setUpdater(mockProductConfigUpdater); setFieldInConfig(Config.get(), "debuggerExceptionEnabled", true); setFieldInConfig(Config.get(), "dynamicInstrumentationClassFileDumpEnabled", true); } diff --git a/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy index d1c9322bd84..72ce9011604 100644 --- a/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy +++ b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy @@ -182,7 +182,7 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { outputGobbler.start() errorGobbler.start() - if (!p.waitFor(timeoutSecs , TimeUnit.SECONDS)) { + if (!p.waitFor(timeoutSecs, TimeUnit.SECONDS)) { p.destroyForcibly() throw new TimeoutException("Instrumented process failed to exit within $timeoutSecs seconds") } @@ -251,7 +251,6 @@ class JUnitConsoleSmokeTest extends CiVisibilitySmokeTest { "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_AGENTLESS_URL)}=${mockBackend.intakeUrl}," + "${Strings.propertyNameToSystemPropertyName(GeneralConfig.SERVICE_NAME)}=${TEST_SERVICE_NAME}," + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_BUILD_INSTRUMENTATION_ENABLED)}=false," + - "${Strings.propertyNameToSystemPropertyName(GeneralConfig.TRACE_DEBUG)}=true," + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_FLAKY_RETRY_ONLY_KNOWN_FLAKES)}=true," agentArgument += additionalAgentArgs.join(",") diff --git a/dd-trace-core/src/main/java/datadog/trace/core/DDSpan.java b/dd-trace-core/src/main/java/datadog/trace/core/DDSpan.java index 2c282f15a57..77435fb0775 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/DDSpan.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/DDSpan.java @@ -15,6 +15,7 @@ import datadog.trace.api.EndpointTracker; import datadog.trace.api.TagMap; import datadog.trace.api.TraceConfig; +import datadog.trace.api.debugger.DebuggerConfigBridge; import datadog.trace.api.gateway.Flow; import datadog.trace.api.gateway.RequestContext; import datadog.trace.api.metrics.SpanMetricRegistry; @@ -365,7 +366,7 @@ public DDSpan addThrowable(Throwable error, byte errorPriority) { } private boolean isExceptionReplayEnabled() { - if (!DebuggerContext.isExceptionReplayEnabled()) { + if (!DebuggerConfigBridge.isExceptionReplayEnabled()) { return false; } boolean captureOnlyRootSpan = diff --git a/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java b/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java index ad3a57c1505..d3b27062ac1 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java @@ -27,8 +27,9 @@ import datadog.remoteconfig.state.ProductListener; import datadog.trace.api.Config; import datadog.trace.api.DynamicConfig; +import datadog.trace.api.debugger.DebuggerConfigBridge; +import datadog.trace.api.debugger.DebuggerConfigUpdate; import datadog.trace.api.sampling.SamplingRule; -import datadog.trace.bootstrap.debugger.DebuggerContext; import datadog.trace.logging.GlobalLogLevelSwitcher; import datadog.trace.logging.LogLevel; import java.io.ByteArrayInputStream; @@ -220,11 +221,13 @@ void applyConfigOverrides(LibConfig libConfig) { maybeOverride(builder::setTraceSampleRate, libConfig.traceSampleRate); maybeOverride(builder::setTracingTags, parseTagListToMap(libConfig.tracingTags)); - DebuggerContext.updateConfig( - libConfig.dynamicInstrumentationEnabled, - libConfig.exceptionReplayEnabled, - libConfig.codeOriginEnabled, - libConfig.liveDebuggingEnabled); + DebuggerConfigBridge.updateConfig( + new DebuggerConfigUpdate.Builder() + .setDynamicInstrumentationEnabled(libConfig.dynamicInstrumentationEnabled) + .setExceptionReplayEnabled(libConfig.exceptionReplayEnabled) + .setCodeOriginEnabled(libConfig.codeOriginEnabled) + .setLiveDebuggingEnabled(libConfig.liveDebuggingEnabled) + .build()); builder.apply(); } diff --git a/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigBridge.java b/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigBridge.java new file mode 100644 index 00000000000..3df805bea61 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigBridge.java @@ -0,0 +1,83 @@ +package datadog.trace.api.debugger; + +import datadog.trace.api.Config; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.atomic.AtomicReference; +import javax.annotation.Nonnull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class DebuggerConfigBridge { + private static final Logger LOGGER = LoggerFactory.getLogger(DebuggerConfigBridge.class); + + private static final int MAX_DEFERRED_UPDATES = 10; + private static final BlockingQueue DEFERRED_UPDATES = + new ArrayBlockingQueue<>(MAX_DEFERRED_UPDATES); + + private static final AtomicReference UPDATER = new AtomicReference<>(); + + public static void updateConfig(DebuggerConfigUpdate update) { + if (!update.hasUpdates()) { + LOGGER.debug("No config update detected, skipping"); + return; + } + if (UPDATER.get() != null) { + LOGGER.debug("DebuggerConfigUpdater available, performing update"); + UPDATER.get().updateConfig(update); + } else { + LOGGER.debug("DebuggerConfigUpdater not available, deferring update"); + if (!DEFERRED_UPDATES.offer(update)) { + LOGGER.debug("Queue is full, update not deferred"); + } + } + } + + public static void setUpdater(@Nonnull DebuggerConfigUpdater updater) { + DebuggerConfigUpdater oldUpdater = UPDATER.getAndSet(updater); + if (oldUpdater == null) { + LOGGER.info("DebuggerConfigUpdater set for first time, processing deferred updates"); + processDeferredUpdates(updater); + } + } + + public static boolean isDynamicInstrumentationEnabled() { + DebuggerConfigUpdater updater = UPDATER.get(); + if (updater != null) { + return updater.isDynamicInstrumentationEnabled(); + } + return Config.get().isDynamicInstrumentationEnabled(); + } + + public static boolean isExceptionReplayEnabled() { + DebuggerConfigUpdater updater = UPDATER.get(); + if (updater != null) { + return updater.isExceptionReplayEnabled(); + } + return Config.get().isDebuggerExceptionEnabled(); + } + + public static boolean isCodeOriginEnabled() { + DebuggerConfigUpdater updater = UPDATER.get(); + if (updater != null) { + return updater.isCodeOriginEnabled(); + } + return Config.get().isDebuggerCodeOriginEnabled(); + } + + public static boolean isDistributedDebuggerEnabled() { + DebuggerConfigUpdater updater = UPDATER.get(); + if (updater != null) { + return updater.isDistributedDebuggerEnabled(); + } + return Config.get().isDistributedDebuggerEnabled(); + } + + private static void processDeferredUpdates(DebuggerConfigUpdater updater) { + DebuggerConfigUpdate deferredUpdate; + while ((deferredUpdate = DEFERRED_UPDATES.poll()) != null) { + updater.updateConfig(deferredUpdate); + LOGGER.debug("Processed deferred update {}", deferredUpdate); + } + } +} diff --git a/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdate.java b/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdate.java new file mode 100644 index 00000000000..4744812233c --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdate.java @@ -0,0 +1,104 @@ +package datadog.trace.api.debugger; + +import java.util.Objects; + +public class DebuggerConfigUpdate { + private final Boolean dynamicInstrumentationEnabled; + private final Boolean exceptionReplayEnabled; + private final Boolean codeOriginEnabled; + private final Boolean liveDebuggingEnabled; + + private DebuggerConfigUpdate(Builder builder) { + this.dynamicInstrumentationEnabled = builder.dynamicInstrumentationEnabled; + this.exceptionReplayEnabled = builder.exceptionReplayEnabled; + this.codeOriginEnabled = builder.codeOriginEnabled; + this.liveDebuggingEnabled = builder.liveDebuggingEnabled; + } + + public Boolean getDynamicInstrumentationEnabled() { + return dynamicInstrumentationEnabled; + } + + public Boolean getExceptionReplayEnabled() { + return exceptionReplayEnabled; + } + + public Boolean getCodeOriginEnabled() { + return codeOriginEnabled; + } + + public Boolean getLiveDebuggingEnabled() { + return liveDebuggingEnabled; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof DebuggerConfigUpdate)) return false; + DebuggerConfigUpdate that = (DebuggerConfigUpdate) o; + return Objects.equals(dynamicInstrumentationEnabled, that.dynamicInstrumentationEnabled) + && Objects.equals(exceptionReplayEnabled, that.exceptionReplayEnabled) + && Objects.equals(codeOriginEnabled, that.codeOriginEnabled) + && Objects.equals(liveDebuggingEnabled, that.liveDebuggingEnabled); + } + + @Override + public String toString() { + return "DebuggerConfigUpdate{" + + "dynamicInstrumentationEnabled=" + + dynamicInstrumentationEnabled + + ", exceptionReplayEnabled=" + + exceptionReplayEnabled + + ", codeOriginEnabled=" + + codeOriginEnabled + + ", liveDebuggingEnabled=" + + liveDebuggingEnabled + + '}'; + } + + @Override + public int hashCode() { + return Objects.hash( + dynamicInstrumentationEnabled, + exceptionReplayEnabled, + codeOriginEnabled, + liveDebuggingEnabled); + } + + public boolean hasUpdates() { + return dynamicInstrumentationEnabled != null + || exceptionReplayEnabled != null + || codeOriginEnabled != null + || liveDebuggingEnabled != null; + } + + public static final class Builder { + private Boolean dynamicInstrumentationEnabled; + private Boolean exceptionReplayEnabled; + private Boolean codeOriginEnabled; + private Boolean liveDebuggingEnabled; + + public Builder setDynamicInstrumentationEnabled(Boolean dynamicInstrumentationEnabled) { + this.dynamicInstrumentationEnabled = dynamicInstrumentationEnabled; + return this; + } + + public Builder setExceptionReplayEnabled(Boolean exceptionReplayEnabled) { + this.exceptionReplayEnabled = exceptionReplayEnabled; + return this; + } + + public Builder setCodeOriginEnabled(Boolean codeOriginEnabled) { + this.codeOriginEnabled = codeOriginEnabled; + return this; + } + + public Builder setLiveDebuggingEnabled(Boolean liveDebuggingEnabled) { + this.liveDebuggingEnabled = liveDebuggingEnabled; + return this; + } + + public DebuggerConfigUpdate build() { + return new DebuggerConfigUpdate(this); + } + } +} diff --git a/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdater.java b/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdater.java new file mode 100644 index 00000000000..beddc55fe4e --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdater.java @@ -0,0 +1,13 @@ +package datadog.trace.api.debugger; + +public interface DebuggerConfigUpdater { + void updateConfig(DebuggerConfigUpdate update); + + boolean isDynamicInstrumentationEnabled(); + + boolean isExceptionReplayEnabled(); + + boolean isCodeOriginEnabled(); + + boolean isDistributedDebuggerEnabled(); +} diff --git a/internal-api/src/test/groovy/datadog/trace/api/debugger/DebuggerConfigBridgeTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/debugger/DebuggerConfigBridgeTest.groovy new file mode 100644 index 00000000000..82ee0ab194b --- /dev/null +++ b/internal-api/src/test/groovy/datadog/trace/api/debugger/DebuggerConfigBridgeTest.groovy @@ -0,0 +1,57 @@ +package datadog.trace.api.debugger + +import spock.lang.Specification + +class DebuggerConfigBridgeTest extends Specification { + + def "test bridge update calls"() { + def updater = new MockDebuggerConfigUpdater() + + when: + DebuggerConfigBridge.updateConfig(new DebuggerConfigUpdate.Builder().build()) + + then: + updater.calls == 0 + + when: + DebuggerConfigBridge.setUpdater(updater) + + then: + updater.calls == 1 // deferred updates are done on set + + when: + DebuggerConfigBridge.updateConfig(new DebuggerConfigUpdate.Builder().build()) + + then: + updater.calls == 2 + } + + private static class MockDebuggerConfigUpdater implements DebuggerConfigUpdater { + private int calls = 0 + + @Override + void updateConfig(DebuggerConfigUpdate update) { + calls++ + } + + @Override + boolean isDynamicInstrumentationEnabled() { + return false + } + + @Override + boolean isExceptionReplayEnabled() { + return false + } + + @Override + boolean isCodeOriginEnabled() { + return false + } + + @Override + boolean isDistributedDebuggerEnabled() { + return false + } + } +} From ab8d8e9f8d93236edaa671a18b96243140b1ad7d Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 27 Aug 2025 12:53:42 +0200 Subject: [PATCH 22/24] fix: move check from policy to history for FTR --- .../trace/civisibility/events/TestEventsHandlerImpl.java | 3 +-- .../debugger/exception/DefaultExceptionDebugger.java | 6 +++--- .../api/civisibility/execution/TestExecutionHistory.java | 3 +++ .../api/civisibility/execution/TestExecutionPolicy.java | 3 --- .../trace/api/debugger/DebuggerConfigBridgeTest.groovy | 4 ++-- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java index e7ee215b33f..79c41e3111c 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java @@ -160,8 +160,6 @@ public void onTestStart( testSuite.testStart(testName, testParameters, testSourceData.getTestMethod(), startTime); TestIdentifier thisTest = test.getIdentifier(); - test.getContext() - .set(TestExecutionPolicy.class, executionPolicy(thisTest, testSourceData, categories)); if (testModule.isNew(thisTest)) { test.setTag(Tags.TEST_IS_NEW, true); @@ -184,6 +182,7 @@ public void onTestStart( } if (testExecutionHistory != null) { + test.getContext().set(TestExecutionHistory.class, testExecutionHistory); RetryReason retryReason = testExecutionHistory.currentExecutionRetryReason(); if (retryReason != null) { test.setTag(Tags.TEST_IS_RETRY, true); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java index 7b5966e848f..6da2b4497c2 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java @@ -12,7 +12,7 @@ import datadog.trace.api.Config; import datadog.trace.api.civisibility.InstrumentationTestBridge; import datadog.trace.api.civisibility.domain.TestContext; -import datadog.trace.api.civisibility.execution.TestExecutionPolicy; +import datadog.trace.api.civisibility.execution.TestExecutionHistory; import datadog.trace.bootstrap.debugger.DebuggerContext; import datadog.trace.bootstrap.debugger.DebuggerContext.ClassNameFilter; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -92,8 +92,8 @@ public void handleException(Throwable t, AgentSpan span) { if (testContext == null) { return; } - TestExecutionPolicy executionPolicy = testContext.get(TestExecutionPolicy.class); - if (executionPolicy == null || !executionPolicy.failedTestReplayApplicable()) { + TestExecutionHistory executionHistory = testContext.get(TestExecutionHistory.class); + if (executionHistory == null || !executionHistory.failedTestReplayApplicable()) { return; } } else { diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionHistory.java b/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionHistory.java index e97aa2b458c..d281a74d9e2 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionHistory.java +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionHistory.java @@ -35,4 +35,7 @@ public interface TestExecutionHistory { * multiple retries) */ boolean hasSucceededAllRetries(); + + /** @return {@code true} if the test should be instrumented by FTR */ + boolean failedTestReplayApplicable(); } diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionPolicy.java b/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionPolicy.java index c40bca00c3b..00de8d306e4 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionPolicy.java +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/execution/TestExecutionPolicy.java @@ -20,7 +20,4 @@ public interface TestExecutionPolicy extends TestExecutionHistory { * result */ boolean suppressFailures(); - - /** @return {@code true} if the test should be instrumented by FTR */ - boolean failedTestReplayApplicable(); } diff --git a/internal-api/src/test/groovy/datadog/trace/api/debugger/DebuggerConfigBridgeTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/debugger/DebuggerConfigBridgeTest.groovy index 82ee0ab194b..06f8367a7b9 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/debugger/DebuggerConfigBridgeTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/debugger/DebuggerConfigBridgeTest.groovy @@ -8,7 +8,7 @@ class DebuggerConfigBridgeTest extends Specification { def updater = new MockDebuggerConfigUpdater() when: - DebuggerConfigBridge.updateConfig(new DebuggerConfigUpdate.Builder().build()) + DebuggerConfigBridge.updateConfig(new DebuggerConfigUpdate.Builder().setExceptionReplayEnabled(true).build()) then: updater.calls == 0 @@ -20,7 +20,7 @@ class DebuggerConfigBridgeTest extends Specification { updater.calls == 1 // deferred updates are done on set when: - DebuggerConfigBridge.updateConfig(new DebuggerConfigUpdate.Builder().build()) + DebuggerConfigBridge.updateConfig(new DebuggerConfigUpdate.Builder().setExceptionReplayEnabled(false).build()) then: updater.calls == 2 From bd817693b81a67b54d0d6951657f4821177d50e1 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 27 Aug 2025 14:19:40 +0200 Subject: [PATCH 23/24] fix: tests --- .../events/TestEventsHandlerImpl.java | 1 - .../DefaultDebuggerConfigUpdaterTest.java | 9 ++++--- .../api/debugger/DebuggerConfigUpdate.java | 26 +++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java index 79c41e3111c..825f1dd6e6e 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java @@ -160,7 +160,6 @@ public void onTestStart( testSuite.testStart(testName, testParameters, testSourceData.getTestMethod(), startTime); TestIdentifier thisTest = test.getIdentifier(); - if (testModule.isNew(thisTest)) { test.setTag(Tags.TEST_IS_NEW, true); } diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultDebuggerConfigUpdaterTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultDebuggerConfigUpdaterTest.java index 544326aee46..5636e051520 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultDebuggerConfigUpdaterTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultDebuggerConfigUpdaterTest.java @@ -7,6 +7,7 @@ import datadog.communication.ddagent.DDAgentFeaturesDiscovery; import datadog.communication.ddagent.SharedCommunicationObjects; +import datadog.trace.api.debugger.DebuggerConfigUpdate; import java.lang.instrument.Instrumentation; import org.junit.jupiter.api.Test; @@ -18,18 +19,18 @@ public void enableDisable() { when(sco.featuresDiscovery(any())).thenReturn(mock(DDAgentFeaturesDiscovery.class)); DebuggerAgent.run(mock(Instrumentation.class), sco); DefaultDebuggerConfigUpdater productConfigUpdater = new DefaultDebuggerConfigUpdater(); - productConfigUpdater.updateConfig(null); - productConfigUpdater.updateConfig(true); + productConfigUpdater.updateConfig(DebuggerConfigUpdate.empty()); + productConfigUpdater.updateConfig(DebuggerConfigUpdate.allEnabled()); assertTrue(productConfigUpdater.isDynamicInstrumentationEnabled()); assertTrue(productConfigUpdater.isExceptionReplayEnabled()); assertTrue(productConfigUpdater.isCodeOriginEnabled()); assertTrue(productConfigUpdater.isDistributedDebuggerEnabled()); - productConfigUpdater.updateConfig(null); + productConfigUpdater.updateConfig(DebuggerConfigUpdate.empty()); assertTrue(productConfigUpdater.isDynamicInstrumentationEnabled()); assertTrue(productConfigUpdater.isExceptionReplayEnabled()); assertTrue(productConfigUpdater.isCodeOriginEnabled()); assertTrue(productConfigUpdater.isDistributedDebuggerEnabled()); - productConfigUpdater.updateConfig(false); + productConfigUpdater.updateConfig(DebuggerConfigUpdate.allDisabled()); assertFalse(productConfigUpdater.isDynamicInstrumentationEnabled()); assertFalse(productConfigUpdater.isExceptionReplayEnabled()); assertFalse(productConfigUpdater.isCodeOriginEnabled()); diff --git a/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdate.java b/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdate.java index 4744812233c..5148ec1b07d 100644 --- a/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdate.java +++ b/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdate.java @@ -97,8 +97,34 @@ public Builder setLiveDebuggingEnabled(Boolean liveDebuggingEnabled) { return this; } + public Builder enableAll() { + return setDynamicInstrumentationEnabled(true) + .setExceptionReplayEnabled(true) + .setCodeOriginEnabled(true) + .setLiveDebuggingEnabled(true); + } + + public Builder disableAll() { + return setDynamicInstrumentationEnabled(false) + .setExceptionReplayEnabled(false) + .setCodeOriginEnabled(false) + .setLiveDebuggingEnabled(false); + } + public DebuggerConfigUpdate build() { return new DebuggerConfigUpdate(this); } } + + public static DebuggerConfigUpdate empty() { + return new Builder().build(); + } + + public static DebuggerConfigUpdate allEnabled() { + return new Builder().enableAll().build(); + } + + public static DebuggerConfigUpdate allDisabled() { + return new Builder().disableAll().build(); + } } From c257390e975c2533eff16793cfbaf569fe0f7d10 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 27 Aug 2025 14:28:02 +0200 Subject: [PATCH 24/24] chore: update codeowners --- .github/CODEOWNERS | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 62520f362fe..0b8c1fce3ee 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -95,8 +95,9 @@ **/CiVisibility*.groovy @DataDog/ci-app-libraries-java # @DataDog/debugger-java (Live Debugger) -/dd-java-agent/agent-debugger/ @DataDog/debugger-java -/dd-smoke-tests/debugger-integration-tests/ @DataDog/debugger-java +/dd-java-agent/agent-debugger/ @DataDog/debugger-java +/dd-smoke-tests/debugger-integration-tests/ @DataDog/debugger-java +/internal-api/src/main/java/datadog/trace/api/debugger/ @DataDog/debugger-java # @DataDog/data-jobs-monitoring /dd-java-agent/instrumentation/spark/ @DataDog/data-jobs-monitoring