diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ef437eebae2..0b8c1fce3ee 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 @@ -94,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 diff --git a/communication/src/main/java/datadog/communication/BackendApiFactory.java b/communication/src/main/java/datadog/communication/BackendApiFactory.java index 4207d328044..5cea26600de 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.Intake; import datadog.trace.util.throwable.FatalAgentMisconfigurationError; -import java.util.function.Function; import javax.annotation.Nullable; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; @@ -28,8 +28,8 @@ public BackendApiFactory(Config config, SharedCommunicationObjects sharedCommuni public @Nullable BackendApi createBackendApi(Intake 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,46 +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), - CI_INTAKE( - "ci-intake", - "v2", - Config::isCiVisibilityAgentlessEnabled, - Config::getCiVisibilityIntakeAgentlessUrl); - - 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-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java index d8f09c75089..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,6 +632,7 @@ public void execute() { } maybeStartAppSec(scoClass, sco); + // 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 @@ -1308,10 +1309,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/CiVisibilityServices.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilityServices.java index 095614e46ad..c2f2023000d 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; @@ -84,10 +85,8 @@ public class CiVisibilityServices { this.processHierarchy = new ProcessHierarchy(); this.config = config; this.metricCollector = metricCollector; - this.backendApi = - new BackendApiFactory(config, sco).createBackendApi(BackendApiFactory.Intake.API); - this.ciIntake = - new BackendApiFactory(config, sco).createBackendApi(BackendApiFactory.Intake.CI_INTAKE); + this.backendApi = new BackendApiFactory(config, sco).createBackendApi(Intake.API); + this.ciIntake = new BackendApiFactory(config, sco).createBackendApi(Intake.CI_INTAKE); this.jvmInfoFactory = new CachingJvmInfoFactory(config, new JvmInfoFactoryImpl()); this.gitClientFactory = buildGitClientFactory(config, metricCollector); 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 2d68404b83f..95895dc5de9 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; @@ -102,6 +104,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/config/CiVisibilitySettings.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CiVisibilitySettings.java index ca5d6324b1a..05aeca8a8d5 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 @@ -18,6 +18,7 @@ public class CiVisibilitySettings { false, false, false, + false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null); @@ -30,6 +31,7 @@ public class CiVisibilitySettings { private final boolean impactedTestsDetectionEnabled; private final boolean knownTestsEnabled; private final boolean coverageReportUploadEnabled; + private final boolean failedTestReplayEnabled; private final EarlyFlakeDetectionSettings earlyFlakeDetectionSettings; private final TestManagementSettings testManagementSettings; @Nullable private final String defaultBranch; @@ -43,6 +45,7 @@ public class CiVisibilitySettings { boolean impactedTestsDetectionEnabled, boolean knownTestsEnabled, boolean coverageReportUploadEnabled, + boolean failedTestReplayEnabled, EarlyFlakeDetectionSettings earlyFlakeDetectionSettings, TestManagementSettings testManagementSettings, @Nullable String defaultBranch) { @@ -54,6 +57,7 @@ public class CiVisibilitySettings { this.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled; this.knownTestsEnabled = knownTestsEnabled; this.coverageReportUploadEnabled = coverageReportUploadEnabled; + this.failedTestReplayEnabled = failedTestReplayEnabled; this.earlyFlakeDetectionSettings = earlyFlakeDetectionSettings; this.testManagementSettings = testManagementSettings; this.defaultBranch = defaultBranch; @@ -91,6 +95,10 @@ public boolean isCoverageReportUploadEnabled() { return coverageReportUploadEnabled; } + public boolean isFailedTestReplayEnabled() { + return failedTestReplayEnabled; + } + public EarlyFlakeDetectionSettings getEarlyFlakeDetectionSettings() { return earlyFlakeDetectionSettings; } @@ -121,6 +129,7 @@ public boolean equals(Object o) { && impactedTestsDetectionEnabled == that.impactedTestsDetectionEnabled && knownTestsEnabled == that.knownTestsEnabled && coverageReportUploadEnabled == that.coverageReportUploadEnabled + && failedTestReplayEnabled == that.failedTestReplayEnabled && Objects.equals(earlyFlakeDetectionSettings, that.earlyFlakeDetectionSettings) && Objects.equals(testManagementSettings, that.testManagementSettings) && Objects.equals(defaultBranch, that.defaultBranch); @@ -137,6 +146,7 @@ public int hashCode() { impactedTestsDetectionEnabled, knownTestsEnabled, coverageReportUploadEnabled, + failedTestReplayEnabled, earlyFlakeDetectionSettings, testManagementSettings, defaultBranch); @@ -165,6 +175,7 @@ public CiVisibilitySettings fromJson(Map json) { getBoolean(json, "impacted_tests_enabled", false), getBoolean(json, "known_tests_enabled", false), getBoolean(json, "coverage_report_upload_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/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 9d94ed10cc0..d3bc8345e0b 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/config/ExecutionSettings.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/ExecutionSettings.java index 7c1809f534d..ba312cf8fa4 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 @@ -27,6 +27,7 @@ public class ExecutionSettings { false, false, false, + false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null, @@ -45,6 +46,7 @@ public class ExecutionSettings { private final boolean flakyTestRetriesEnabled; private final boolean impactedTestsDetectionEnabled; private final boolean codeCoverageReportUploadEnabled; + private final boolean failedTestReplayEnabled; @Nonnull private final EarlyFlakeDetectionSettings earlyFlakeDetectionSettings; @Nonnull private final TestManagementSettings testManagementSettings; @Nullable private final String itrCorrelationId; @@ -61,6 +63,7 @@ public ExecutionSettings( boolean flakyTestRetriesEnabled, boolean impactedTestsDetectionEnabled, boolean codeCoverageReportUploadEnabled, + boolean failedTestReplayEnabled, @Nonnull EarlyFlakeDetectionSettings earlyFlakeDetectionSettings, @Nonnull TestManagementSettings testManagementSettings, @Nullable String itrCorrelationId, @@ -78,6 +81,7 @@ public ExecutionSettings( this.flakyTestRetriesEnabled = flakyTestRetriesEnabled; this.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled; this.codeCoverageReportUploadEnabled = codeCoverageReportUploadEnabled; + this.failedTestReplayEnabled = failedTestReplayEnabled; this.earlyFlakeDetectionSettings = earlyFlakeDetectionSettings; this.testManagementSettings = testManagementSettings; this.itrCorrelationId = itrCorrelationId; @@ -115,6 +119,7 @@ private ExecutionSettings( boolean flakyTestRetriesEnabled, boolean impactedTestsDetectionEnabled, boolean codeCoverageReportUploadEnabled, + boolean failedTestReplayEnabled, @Nonnull EarlyFlakeDetectionSettings earlyFlakeDetectionSettings, @Nonnull TestManagementSettings testManagementSettings, @Nullable String itrCorrelationId, @@ -129,6 +134,7 @@ private ExecutionSettings( this.flakyTestRetriesEnabled = flakyTestRetriesEnabled; this.impactedTestsDetectionEnabled = impactedTestsDetectionEnabled; this.codeCoverageReportUploadEnabled = codeCoverageReportUploadEnabled; + this.failedTestReplayEnabled = failedTestReplayEnabled; this.earlyFlakeDetectionSettings = earlyFlakeDetectionSettings; this.testManagementSettings = testManagementSettings; this.itrCorrelationId = itrCorrelationId; @@ -167,6 +173,10 @@ public boolean isCodeCoverageReportUploadEnabled() { return codeCoverageReportUploadEnabled; } + public boolean isFailedTestReplayEnabled() { + return failedTestReplayEnabled; + } + @Nonnull public EarlyFlakeDetectionSettings getEarlyFlakeDetectionSettings() { return earlyFlakeDetectionSettings; @@ -254,6 +264,7 @@ public boolean equals(Object o) { && flakyTestRetriesEnabled == that.flakyTestRetriesEnabled && impactedTestsDetectionEnabled == that.impactedTestsDetectionEnabled && codeCoverageReportUploadEnabled == that.codeCoverageReportUploadEnabled + && failedTestReplayEnabled == that.failedTestReplayEnabled && Objects.equals(earlyFlakeDetectionSettings, that.earlyFlakeDetectionSettings) && Objects.equals(testManagementSettings, that.testManagementSettings) && Objects.equals(itrCorrelationId, that.itrCorrelationId) @@ -273,6 +284,7 @@ public int hashCode() { flakyTestRetriesEnabled, impactedTestsDetectionEnabled, codeCoverageReportUploadEnabled, + failedTestReplayEnabled, earlyFlakeDetectionSettings, testManagementSettings, itrCorrelationId, @@ -291,6 +303,7 @@ public static class Serializer { private static final int FLAKY_TEST_RETRIES_ENABLED_FLAG = 8; private static final int IMPACTED_TESTS_DETECTION_ENABLED_FLAG = 16; private static final int CODE_COVERAGE_REPORT_UPLOAD_ENABLED_FLAG = 32; + private static final int FAILED_TEST_REPLAY_ENABLED_FLAG = 64; public static ByteBuffer serialize(ExecutionSettings settings) { datadog.trace.civisibility.ipc.serialization.Serializer s = @@ -307,7 +320,8 @@ public static ByteBuffer serialize(ExecutionSettings settings) { : 0) | (settings.codeCoverageReportUploadEnabled ? CODE_COVERAGE_REPORT_UPLOAD_ENABLED_FLAG - : 0)); + : 0) + | (settings.failedTestReplayEnabled ? FAILED_TEST_REPLAY_ENABLED_FLAG : 0)); s.write(flags); EarlyFlakeDetectionSettings.Serializer.serialize(s, settings.earlyFlakeDetectionSettings); @@ -348,6 +362,7 @@ public static ExecutionSettings deserialize(ByteBuffer buffer) { boolean impactedTestsDetectionEnabled = (flags & IMPACTED_TESTS_DETECTION_ENABLED_FLAG) != 0; boolean codeCoverageReportUploadEnabled = (flags & CODE_COVERAGE_REPORT_UPLOAD_ENABLED_FLAG) != 0; + boolean failedTestReplayEnabled = (flags & FAILED_TEST_REPLAY_ENABLED_FLAG) != 0; EarlyFlakeDetectionSettings earlyFlakeDetectionSettings = EarlyFlakeDetectionSettings.Serializer.deserialize(buffer); @@ -391,6 +406,7 @@ public static ExecutionSettings deserialize(ByteBuffer buffer) { flakyTestRetriesEnabled, impactedTestsDetectionEnabled, codeCoverageReportUploadEnabled, + 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 ca153fc9ce8..5fcc776edce 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 @@ -182,6 +182,11 @@ private Map doCreate( settings, CiVisibilitySettings::isCoverageReportUploadEnabled, Config::isCiVisibilityCodeCoverageReportUploadEnabled); + boolean failedTestReplayEnabled = + isFeatureEnabled( + settings, + CiVisibilitySettings::isFailedTestReplayEnabled, + Config::isCiVisibilityFailedTestReplayEnabled); TestManagementSettings testManagementSettings = getTestManagementSettings(settings); @@ -195,7 +200,8 @@ private Map doCreate( + "Known tests marking - {},\n" + "Auto test retries - {},\n" + "Test Management - {},\n" - + "Code coverage report upload - {}", + + "Code coverage report upload - {},\n" + + "Failed Test Replay - {}", repositoryRoot, tracerEnvironment.getConfigurations().getRuntimeName(), tracerEnvironment.getConfigurations().getRuntimeVersion(), @@ -208,7 +214,8 @@ private Map doCreate( knownTestsRequest, flakyTestRetriesEnabled, testManagementSettings.isEnabled(), - codeCoverageReportUpload); + codeCoverageReportUpload, + failedTestReplayEnabled); Future skippableTestsFuture = executor.submit(() -> getSkippableTests(tracerEnvironment, itrEnabled)); @@ -261,6 +268,7 @@ private Map doCreate( flakyTestRetriesEnabled, impactedTestsEnabled, codeCoverageReportUpload, + failedTestReplayEnabled, earlyFlakeDetectionEnabled ? settings.getEarlyFlakeDetectionSettings() : EarlyFlakeDetectionSettings.DEFAULT, 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 a2d8d155ea6..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 @@ -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; @@ -243,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(); @@ -305,6 +310,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, + 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-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 812bb045233..2dc9fc47825 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 @@ -180,6 +180,11 @@ 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())); + // 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-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..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 @@ -181,6 +181,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-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-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 2b9b9af2f44..de0b9f775b2 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.Intake import okhttp3.HttpUrl import okhttp3.OkHttpClient import org.apache.commons.io.IOUtils @@ -55,10 +55,10 @@ class ConfigurationApiImplTest extends Specification { where: agentless | compression | expectedSettings - 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, 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, 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, false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null) + false | true | new CiVisibilitySettings(true, true, true, true, true, true, true, true, true, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, "main") + true | false | new CiVisibilitySettings(false, true, false, true, false, true, false, 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, true, false, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(5000, 3), new ExecutionsByDuration(120000, 2)], 10), new TestManagementSettings(true, 20), "prod") } def "test skippable tests request"() { @@ -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(), Intake.API.getVersion())) String apiKey = "api-key" String traceId = "a-trace-id" 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 ce99c31354d..423ff5b0ecf 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 @@ -27,6 +27,7 @@ class ExecutionSettingsTest extends DDSpecification { false, false, false, + false, EarlyFlakeDetectionSettings.DEFAULT, TestManagementSettings.DEFAULT, null, @@ -46,6 +47,7 @@ class ExecutionSettingsTest extends DDSpecification { true, true, true, + false, new EarlyFlakeDetectionSettings(true, [], 10), new TestManagementSettings(true, 20), "", @@ -66,6 +68,7 @@ class ExecutionSettingsTest extends DDSpecification { false, true, false, + true, new EarlyFlakeDetectionSettings(true, [new ExecutionsByDuration(10, 20)], 10), new TestManagementSettings(true, 20), "itrCorrelationId", @@ -90,6 +93,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", diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/coverage/report/CoverageReportUploaderTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/coverage/report/CoverageReportUploaderTest.groovy index e67fd34f8e2..d1322d91c3b 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/coverage/report/CoverageReportUploaderTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/coverage/report/CoverageReportUploaderTest.groovy @@ -2,11 +2,11 @@ package datadog.trace.civisibility.coverage.report import com.fasterxml.jackson.databind.ObjectMapper import datadog.communication.BackendApi -import datadog.communication.BackendApiFactory import datadog.communication.IntakeApi import datadog.communication.http.HttpRetryPolicy import datadog.communication.http.OkHttpUtils import datadog.trace.api.civisibility.telemetry.CiVisibilityMetricCollector +import datadog.trace.api.intake.Intake import datadog.trace.test.util.MultipartRequestParser import okhttp3.HttpUrl import okhttp3.OkHttpClient @@ -79,7 +79,7 @@ class CoverageReportUploaderTest extends Specification { } private BackendApi givenIntakeApi() { - HttpUrl intakeUrl = HttpUrl.get(String.format("%s/api/%s/", server.address.toString(), BackendApiFactory.Intake.CI_INTAKE.version)) + HttpUrl intakeUrl = HttpUrl.get(String.format("%s/api/%s/", server.address.toString(), Intake.CI_INTAKE.version)) String apiKey = "api-key" String traceId = "a-trace-id" 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 1add55398e5..0900c45e0f8 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 @@ -11,6 +11,7 @@ "impacted_tests_enabled": ${settings.impactedTestsDetectionEnabled?c}, "known_tests_enabled": ${settings.knownTestsEnabled?c}, "coverage_report_upload_enabled": ${settings.coverageReportUploadEnabled?c}, + "di_enabled": ${settings.failedTestReplayEnabled?c}, <#if settings.defaultBranch??> "default_branch": "${settings.defaultBranch}", 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 39298a6860c..f19d88656cf 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 @@ -113,6 +113,7 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { private volatile boolean earlyFlakinessDetectionEnabled private volatile boolean impactedTestsDetectionEnabled private volatile boolean testManagementEnabled + private volatile boolean failedTestReplayEnabled = false } private final Settings settings = new Settings() @@ -235,6 +236,7 @@ abstract class CiVisibilityInstrumentationTest extends AgentTestRunner { settings.flakyRetryEnabled, settings.impactedTestsDetectionEnabled, false, + settings.failedTestReplayEnabled, earlyFlakinessDetectionSettings, testManagementSettings, settings.itrEnabled ? "itrCorrelationId" : 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 7fa37f7c919..9ca9719e688 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 @@ -56,4 +56,40 @@ abstract class CiVisibilitySmokeTest extends Specification { protected verifyCoverageReports(String projectName, List reports, Map replacements) { CiVisibilityTestUtils.assertData(projectName, reports, replacements) } + + protected static verifySnapshotLogs(List> receivedLogs, int expectedProbes, int expectedSnapshots) { + def logsPerProbe = 3 // 3 probe statuses per probe -> received, installed, emitting + + assert receivedLogs.size() == logsPerProbe * expectedProbes + expectedSnapshots + + def probeStatusLogs = receivedLogs.findAll { it.containsKey("message") } + def snapshotLogs = receivedLogs.findAll { !it.containsKey("message") } + + verifyProbeStatuses(probeStatusLogs, expectedProbes) + verifySnapshots(snapshotLogs, expectedSnapshots) + } + + 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, expectedCount) { + assert logs.size() == expectedCount + + 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/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 4de8622576a..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; @@ -40,7 +41,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; @@ -74,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(); } @@ -210,13 +211,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/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..2d8ca8dd18f 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.getDistributedDebuggerEnabled(), DebuggerAgent::startDistributedDebugger, DebuggerAgent::stopDistributedDebugger); } 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..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 @@ -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.isCiVisibilityEnabled() ? 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/exception/DefaultExceptionDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java index 981f52208b8..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 @@ -9,9 +9,14 @@ 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.api.civisibility.InstrumentationTestBridge; +import datadog.trace.api.civisibility.domain.TestContext; +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; +import datadog.trace.bootstrap.instrumentation.api.Tags; import datadog.trace.util.AgentTaskScheduler; import java.time.Duration; import java.util.ArrayDeque; @@ -31,27 +36,36 @@ 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 + 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; private final CircuitBreaker circuitBreaker; private final int maxCapturedFrames; + private final boolean applyConfigAsync; + private final boolean failedTestReplayMode; public DefaultExceptionDebugger( ConfigurationUpdater configurationUpdater, ClassNameFilter classNameFiltering, - Duration captureInterval, - int maxExceptionPerSecond, - int maxCapturedFrames) { + Config config) { this( - new ExceptionProbeManager(classNameFiltering, captureInterval), + new ExceptionProbeManager( + classNameFiltering, + config.isCiVisibilityEnabled() + ? Duration.ofSeconds(0) + : Duration.ofSeconds(config.getDebuggerExceptionCaptureInterval())), configurationUpdater, classNameFiltering, - maxExceptionPerSecond, - maxCapturedFrames); + config.getDebuggerMaxExceptionPerSecond(), + config.getDebuggerExceptionMaxCapturedFrames(), + config.isDebuggerExceptionAsyncConfig(), + config.isCiVisibilityEnabled()); } DefaultExceptionDebugger( @@ -59,24 +73,39 @@ public DefaultExceptionDebugger( ConfigurationUpdater configurationUpdater, ClassNameFilter classNameFiltering, int maxExceptionPerSecond, - int maxCapturedFrames) { + int maxCapturedFrames, + boolean applyConfigAsync, + 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.failedTestReplayMode = failedTestReplayMode; } @Override public void handleException(Throwable t, AgentSpan span) { - if (t instanceof Error) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Skip handling error: {}", t.toString()); + if (failedTestReplayMode) { + TestContext testContext = InstrumentationTestBridge.getCurrentTestContext(); + if (testContext == null) { + return; + } + TestExecutionHistory executionHistory = testContext.get(TestExecutionHistory.class); + if (executionHistory == null || !executionHistory.failedTestReplayApplicable()) { + 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) { @@ -97,7 +126,13 @@ public void handleException(Throwable t, AgentSpan span) { return; } processSnapshotsAndSetTags( - t, span, state, chainedExceptionsList, fingerprint, maxCapturedFrames); + t, + span, + state, + chainedExceptionsList, + fingerprint, + maxCapturedFrames, + failedTestReplayMode); exceptionProbeManager.updateLastCapture(fingerprint); } else { // climb up the exception chain to find the first exception that has instrumented frames @@ -108,7 +143,11 @@ public void handleException(Throwable t, AgentSpan span) { exceptionProbeManager.createProbesForException( throwable.getStackTrace(), chainedExceptionIdx); if (creationResult.probesCreated > 0) { - AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); + if (failedTestReplayMode || !applyConfigAsync) { + applyExceptionConfiguration(fingerprint); + } else { + AgentTaskScheduler.INSTANCE.execute(() -> applyExceptionConfiguration(fingerprint)); + } break; } else { if (LOGGER.isDebugEnabled()) { @@ -135,7 +174,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 @@ -166,6 +206,23 @@ 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()); + + 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); + 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); } @@ -179,7 +236,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/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..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 @@ -86,6 +86,7 @@ public void start() { } public void stop() { + lowRateFlush(this); cancelSchedule(this.flushIntervalScheduled); cancelSchedule(this.lowRateScheduled); probeStatusSink.stop(); 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/agent/DefaultProductConfigUpdaterTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultDebuggerConfigUpdaterTest.java similarity index 75% 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..5636e051520 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 @@ -7,29 +7,30 @@ 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; -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(DebuggerConfigUpdate.empty()); + productConfigUpdater.updateConfig(DebuggerConfigUpdate.allEnabled()); assertTrue(productConfigUpdater.isDynamicInstrumentationEnabled()); assertTrue(productConfigUpdater.isExceptionReplayEnabled()); assertTrue(productConfigUpdater.isCodeOriginEnabled()); assertTrue(productConfigUpdater.isDistributedDebuggerEnabled()); - productConfigUpdater.updateConfig(null, null, null, null); + productConfigUpdater.updateConfig(DebuggerConfigUpdate.empty()); assertTrue(productConfigUpdater.isDynamicInstrumentationEnabled()); assertTrue(productConfigUpdater.isExceptionReplayEnabled()); assertTrue(productConfigUpdater.isCodeOriginEnabled()); assertTrue(productConfigUpdater.isDistributedDebuggerEnabled()); - productConfigUpdater.updateConfig(false, false, false, false); + productConfigUpdater.updateConfig(DebuggerConfigUpdate.allDisabled()); assertFalse(productConfigUpdater.isDynamicInstrumentationEnabled()); assertFalse(productConfigUpdater.isExceptionReplayEnabled()); assertFalse(productConfigUpdater.isCodeOriginEnabled()); 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..6c48b171c62 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,35 @@ 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 failedTestReplayModeWithoutActiveTest() { + // other execution path is tested with smoke tests + ExceptionProbeManager manager = mock(ExceptionProbeManager.class); + exceptionDebugger = + new DefaultExceptionDebugger( + manager, configurationUpdater, classNameFiltering, 100, 3, false, true); + exceptionDebugger.handleException(new RuntimeException("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 +438,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 f5266fc8d5f..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 @@ -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; @@ -32,12 +31,15 @@ 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; 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; @@ -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); } @@ -157,7 +158,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 +199,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))); } @@ -383,7 +384,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 9d9cd95ca05..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 @@ -124,6 +124,22 @@ public void addSnapshot(boolean processTagsEnabled) throws IOException { } } + @Test + public void addFailedTestReplaySnapshot() throws IOException { + when(config.isCiVisibilityEnabled()).thenReturn(true); + 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); + assertEquals(JsonSnapshotSerializer.TEST_OPT_PRODUCT, intakeRequest.getProduct()); + } + @Test public void addMultipleSnapshots() throws IOException { when(config.getDynamicInstrumentationUploadBatchSize()).thenReturn(2); 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..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 @@ -4,8 +4,8 @@ import datadog.communication.BackendApi; import datadog.communication.BackendApiFactory; -import datadog.communication.BackendApiFactory.Intake; import datadog.trace.api.Config; +import datadog.trace.api.intake.Intake; import datadog.trace.api.logging.intake.LogsWriter; import datadog.trace.util.AgentThreadFactory; import java.util.ArrayList; 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..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 @@ -288,6 +288,7 @@ public static List capabilities(String version) { boolean isExceptionSuppressionSupported = isExceptionSuppressionSupported(version); if (isExceptionSuppressionSupported) { baseCapabilities.add(LibraryCapability.ATR); + baseCapabilities.add(LibraryCapability.FTR); baseCapabilities.add(LibraryCapability.QUARANTINE); } if (isEFDSupported) { 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 de2890c9e26..3f178ddffa9 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 @@ -46,6 +46,7 @@ class MockBackend implements AutoCloseable { private boolean testManagementEnabled = false private boolean codeCoverageReportUploadEnabled = false private int attemptToFixRetries = 0 + private boolean failedTestReplayEnabled = false void reset() { receivedTraces.clear() @@ -69,6 +70,7 @@ class MockBackend implements AutoCloseable { testManagementEnabled = false codeCoverageReportUploadEnabled = false attemptToFixRetries = 0 + failedTestReplayEnabled = false } @Override @@ -143,6 +145,10 @@ class MockBackend implements AutoCloseable { ]) } + void givenFailedTestReplay(boolean failedTestReplayEnabled) { + this.failedTestReplayEnabled = failedTestReplayEnabled + } + String getIntakeUrl() { return intakeServer.address.toString() } @@ -190,6 +196,7 @@ class MockBackend implements AutoCloseable { "impacted_tests_enabled": $impactedTestsDetectionEnabled, "known_tests_enabled": $knownTestsEnabled, "coverage_report_upload_enabled": $codeCoverageReportUploadEnabled, + "di_enabled": $failedTestReplayEnabled, "test_management": { "enabled": $testManagementEnabled, "attempt_to_fix_retries": $attemptToFixRetries 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 ea5291c29a2..8b0ace8ab69 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 @@ -242,6 +242,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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", @@ -305,6 +306,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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", @@ -370,6 +372,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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", @@ -435,6 +438,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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", @@ -500,6 +504,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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 4bd4970350e..0713df1b0ea 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 @@ -190,6 +190,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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 fdaa0055a7b..704808ca030 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 @@ -242,6 +242,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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 f327423fecd..bea2cbbf2f9 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 @@ -237,6 +237,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -465,4 +466,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 ec8f9da06fd..b0d7a632a15 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 @@ -239,6 +239,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -301,6 +302,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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 a277f6f1bf6..221936b548f 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 @@ -188,6 +188,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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", @@ -249,6 +250,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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 9fdf3a28b66..14c6a95353d 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 @@ -188,6 +188,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -298,6 +299,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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 c35fa2f919c..b4ce7ee570d 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 @@ -237,6 +237,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -347,6 +348,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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 b2d6b1d000b..93d9bd86b60 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 @@ -194,6 +194,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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", @@ -255,6 +256,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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 91ae16cba3e..c43af98e1bf 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 @@ -243,6 +243,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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", @@ -304,6 +305,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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 c1fdd78ad33..0e19405c653 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 @@ -238,6 +238,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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", @@ -299,6 +300,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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 3b4664f9804..d4de5a2c4ce 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 @@ -188,6 +188,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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", @@ -249,6 +250,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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/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..72ce9011604 --- /dev/null +++ b/dd-smoke-tests/junit-console/src/test/groovy/datadog/smoketest/JUnitConsoleSmokeTest.groovy @@ -0,0 +1,291 @@ +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 { + // 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" + 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.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)}=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(7), mockBackend.waitForCoverages(0), additionalDynamicTags) + verifySnapshotLogs(mockBackend.waitForLogs(5), 1, 2) + + 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]) + 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 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(timeoutSecs, TimeUnit.SECONDS)) { + p.destroyForcibly() + throw new TimeoutException("Instrumented process failed to exit within $timeoutSecs 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," + + "${Strings.propertyNameToSystemPropertyName(CiVisibilityConfig.CIVISIBILITY_FLAKY_RETRY_ONLY_KNOWN_FLAKES)}=true," + + agentArgument += additionalAgentArgs.join(",") + + arguments += agentArgument.toString() + return arguments.join("\\ ") + } + + private static String buildJavaHome() { + if (Jvm.current.isJava8()) { + return System.getenv("JAVA_8_HOME") + } + 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..e1e9336592a --- /dev/null +++ b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/coverages.ftl @@ -0,0 +1,37 @@ +[ { + "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} +}, { + "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 new file mode 100644 index 00000000000..b63697c6059 --- /dev/null +++ b/dd-smoke-tests/junit-console/src/test/resources/test_junit_console_failed_test_replay/events.ftl @@ -0,0 +1,409 @@ +[ { + "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_another_failed", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.source.method" : "test_another_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" : 16, + "test.source.start" : 15 + }, + "name" : "junit5.test", + "parent_id" : ${content_parent_id}, + "resource" : "com.example.TestFailed.test_another_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.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}, + "_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_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", + "error.debug_info_captured" : "true", + "error.message" : ${content_meta_error_message}, + "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}, + "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_5}, + "_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_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_4} + }, + "type" : "test", + "version" : 2 +}, { + "content" : { + "duration" : ${content_duration_6}, + "error" : 0, + "meta" : { + "_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}, + "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_6}, + "_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_6}, + "test_session_id" : ${content_test_session_id} + }, + "type" : "test_session_end", + "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", + "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_7}, + "test.itr.tests_skipping.count" : 0 + }, + "name" : "junit5.test_module", + "resource" : "test-headless-service", + "service" : "test-headless-service", + "start" : ${content_start_7}, + "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..e768c32ea2d --- /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,18 @@ +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); + } + + @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 b8dfc7ba1fc..e6b5f350182 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 @@ -171,55 +171,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"() { @@ -234,10 +234,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)}=3" 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(7), mockBackend.waitForCoverages(0), additionalDynamicTags) + verifySnapshotLogs(mockBackend.waitForLogs(5), 1, 2) + + 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" 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..0c4d6ef93a3 --- /dev/null +++ b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/coverages.ftl @@ -0,0 +1,37 @@ +[ { + "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} +}, { + "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 new file mode 100644 index 00000000000..c129cb1fcc4 --- /dev/null +++ b/dd-smoke-tests/maven/src/test/resources/test_failed_maven_failed_test_replay/events.ftl @@ -0,0 +1,552 @@ +[ { + "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" : 19, + "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.coverage_report_upload" : "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_another_failed", + "test.source.file" : "src/test/java/com/example/TestFailed.java", + "test.source.method" : "test_another_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" : 17, + "test.source.start" : 14 + }, + "name" : "junit4.test", + "parent_id" : ${content_parent_id}, + "resource" : "com.example.TestFailed.test_another_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.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.coverage_report_upload" : "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}, + "_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.coverage_report_upload" : "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_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_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.coverage_report_upload" : "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}, + "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_7}, + "_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_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_4} + }, + "type" : "test", + "version" : 2 +} ] 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..503fae292a1 --- /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,19 @@ +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); + } + + @Test + public void test_another_failed() { + assertTrue(Calculator.add(1,1) == 11); + } + +} 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 1c032d9aecf..a237432c335 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 @@ -309,6 +309,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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.coverage_report_upload" : "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", @@ -439,6 +441,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -505,6 +508,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -571,6 +575,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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 0bf2338a889..cd0b63da220 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 @@ -313,6 +313,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -375,6 +376,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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 4e61ba52a39..51c799b9074 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 @@ -277,6 +277,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -339,6 +340,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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..8123f51db35 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 @@ -272,8 +272,10 @@ "error" : 0, "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.coverage_report_upload" : "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", @@ -327,4 +329,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_junit_platform_runner/events.ftl b/dd-smoke-tests/maven/src/test/resources/test_successful_maven_run_junit_platform_runner/events.ftl index b2a9fdb241b..2089a78afa2 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 @@ -329,6 +329,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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", @@ -379,4 +380,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 252ba824ece..d4046cd0e26 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 @@ -313,6 +313,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -375,6 +376,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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 0bf2338a889..cd0b63da220 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 @@ -313,6 +313,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -375,6 +376,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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 0bf2338a889..cd0b63da220 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 @@ -313,6 +313,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -375,6 +376,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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..ae1b833a653 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 @@ -274,8 +274,10 @@ "error" : 1, "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.coverage_report_upload" : "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", @@ -339,8 +341,10 @@ "error" : 1, "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.coverage_report_upload" : "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", @@ -406,8 +410,10 @@ "error" : 1, "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.coverage_report_upload" : "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", @@ -473,8 +479,10 @@ "error" : 1, "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.coverage_report_upload" : "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", @@ -540,8 +548,10 @@ "error" : 1, "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.coverage_report_upload" : "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", @@ -609,8 +619,10 @@ "error" : 1, "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.coverage_report_upload" : "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", @@ -721,8 +733,10 @@ "error" : 0, "meta" : { "_dd.library_capabilities.auto_test_retries" : "1", + "_dd.library_capabilities.coverage_report_upload" : "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", @@ -776,4 +790,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_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 4127ce74a85..caccb604953 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 @@ -269,6 +269,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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 87a11c6f035..4f32f3e92ad 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 @@ -366,6 +366,7 @@ "_dd.library_capabilities.auto_test_retries" : "1", "_dd.library_capabilities.coverage_report_upload" : "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 529e4948018..492864b5418 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 @@ -313,6 +313,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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", @@ -375,6 +376,7 @@ "_dd.library_capabilities.coverage_report_upload" : "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-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index ad94c6bed52..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 @@ -208,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/CiVisibilityConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java index 3aa72841201..31ee79215a4 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 @@ -86,6 +86,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/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/dd-trace-core/src/main/java/datadog/trace/core/DDSpan.java b/dd-trace-core/src/main/java/datadog/trace/core/DDSpan.java index 2a463042d34..ac457e3dcbe 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..d9c2cdd3ef9 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) + .setDistributedDebuggerEnabled(libConfig.liveDebuggingEnabled) + .build()); builder.apply(); } diff --git a/internal-api/build.gradle.kts b/internal-api/build.gradle.kts index 5cf96edcb40..f85f1f07271 100644 --- a/internal-api/build.gradle.kts +++ b/internal-api/build.gradle.kts @@ -73,6 +73,8 @@ val excludedClassesCoverage by extra( "datadog.trace.api.datastreams.NoopDataStreamsMonitoring", "datadog.trace.api.datastreams.NoopPathwayContext", "datadog.trace.api.datastreams.StatsPoint", + // Debugger + "datadog.trace.api.debugger.DebuggerConfigUpdate", // Bootstrap API "datadog.trace.bootstrap.ActiveSubsystems", "datadog.trace.bootstrap.ContextStore.Factory", 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 074edf0c62b..89b605227f7 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; @@ -272,6 +273,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; @@ -280,6 +282,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 +307,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; @@ -644,6 +648,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.Intake; import datadog.trace.api.naming.SpanNaming; import datadog.trace.api.profiling.ProfilingEnablement; import datadog.trace.api.rum.RumInjectorConfig; @@ -1033,6 +1038,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; @@ -1048,6 +1054,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; @@ -1076,6 +1083,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; @@ -2307,6 +2315,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( @@ -2335,6 +2345,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); @@ -2444,6 +2456,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); @@ -3957,6 +3972,10 @@ public Integer getCiVisibilityTestManagementAttemptToFixRetries() { return ciVisibilityTestManagementAttemptToFixRetries; } + public boolean isCiVisibilityFailedTestReplayEnabled() { + return ciVisibilityFailedTestReplayEnabled; + } + public String getGitPullRequestBaseBranch() { return gitPullRequestBaseBranch; } @@ -4101,6 +4120,10 @@ public int getDebuggerExceptionCaptureInterval() { return debuggerExceptionCaptureInterval; } + public boolean isDebuggerExceptionAsyncConfig() { + return debuggerExceptionAsyncConfig; + } + public boolean isDebuggerCodeOriginEnabled() { return debuggerCodeOriginEnabled; } @@ -4139,11 +4162,21 @@ private String getFinalDebuggerBaseUrl() { } public String getFinalDebuggerSnapshotUrl() { - return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; + if (Strings.isNotBlank(dynamicInstrumentationSnapshotUrl)) { + return dynamicInstrumentationSnapshotUrl; + } else if (isCiVisibilityAgentlessEnabled()) { + return Intake.LOGS.getAgentlessUrl(this) + "logs"; + } else { + return getFinalDebuggerBaseUrl() + "/debugger/v1/input"; + } } public String getFinalDebuggerSymDBUrl() { - return getFinalDebuggerBaseUrl() + "/symdb/v1/input"; + if (isCiVisibilityAgentlessEnabled()) { + return Intake.LOGS.getAgentlessUrl(this) + "logs"; + } else { + return getFinalDebuggerBaseUrl() + "/symdb/v1/input"; + } } public String getDynamicInstrumentationProbeFile() { 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 be3aca538ff..486e18844bd 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"), 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/telemetry/CiVisibilityCountMetric.java b/internal-api/src/main/java/datadog/trace/api/civisibility/telemetry/CiVisibilityCountMetric.java index 351c7e852e6..58076bb5eb8 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; @@ -84,6 +85,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 +140,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"; + } + } +} 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..a531f44c939 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigBridge.java @@ -0,0 +1,89 @@ +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); + } + } + + // for testing purposes + static void reset() { + UPDATER.set(null); + DEFERRED_UPDATES.clear(); + } + + 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..16787fb82cf --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/debugger/DebuggerConfigUpdate.java @@ -0,0 +1,130 @@ +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 distributedDebuggerEnabled; + + private DebuggerConfigUpdate(Builder builder) { + this.dynamicInstrumentationEnabled = builder.dynamicInstrumentationEnabled; + this.exceptionReplayEnabled = builder.exceptionReplayEnabled; + this.codeOriginEnabled = builder.codeOriginEnabled; + this.distributedDebuggerEnabled = builder.distributedDebuggerEnabled; + } + + public Boolean getDynamicInstrumentationEnabled() { + return dynamicInstrumentationEnabled; + } + + public Boolean getExceptionReplayEnabled() { + return exceptionReplayEnabled; + } + + public Boolean getCodeOriginEnabled() { + return codeOriginEnabled; + } + + public Boolean getDistributedDebuggerEnabled() { + return distributedDebuggerEnabled; + } + + @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(distributedDebuggerEnabled, that.distributedDebuggerEnabled); + } + + @Override + public String toString() { + return "DebuggerConfigUpdate{" + + "dynamicInstrumentationEnabled=" + + dynamicInstrumentationEnabled + + ", exceptionReplayEnabled=" + + exceptionReplayEnabled + + ", codeOriginEnabled=" + + codeOriginEnabled + + ", distributedDebuggerEnabled=" + + distributedDebuggerEnabled + + '}'; + } + + @Override + public int hashCode() { + return Objects.hash( + dynamicInstrumentationEnabled, + exceptionReplayEnabled, + codeOriginEnabled, + distributedDebuggerEnabled); + } + + public boolean hasUpdates() { + return dynamicInstrumentationEnabled != null + || exceptionReplayEnabled != null + || codeOriginEnabled != null + || distributedDebuggerEnabled != null; + } + + public static final class Builder { + private Boolean dynamicInstrumentationEnabled; + private Boolean exceptionReplayEnabled; + private Boolean codeOriginEnabled; + private Boolean distributedDebuggerEnabled; + + 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 setDistributedDebuggerEnabled(Boolean distributedDebuggerEnabled) { + this.distributedDebuggerEnabled = distributedDebuggerEnabled; + return this; + } + + public Builder enableAll() { + return setDynamicInstrumentationEnabled(true) + .setExceptionReplayEnabled(true) + .setCodeOriginEnabled(true) + .setDistributedDebuggerEnabled(true); + } + + public Builder disableAll() { + return setDynamicInstrumentationEnabled(false) + .setExceptionReplayEnabled(false) + .setCodeOriginEnabled(false) + .setDistributedDebuggerEnabled(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(); + } +} 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/main/java/datadog/trace/api/intake/Intake.java b/internal-api/src/main/java/datadog/trace/api/intake/Intake.java new file mode 100644 index 00000000000..51d4473974e --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/intake/Intake.java @@ -0,0 +1,57 @@ +package datadog.trace.api.intake; + +import datadog.trace.api.Config; +import java.util.function.Function; + +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), + CI_INTAKE( + "ci-intake", + "v2", + Config::isCiVisibilityAgentlessEnabled, + Config::getCiVisibilityIntakeAgentlessUrl); + + 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; + } + + 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); + } + } +} 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"; 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..27600b35dc3 --- /dev/null +++ b/internal-api/src/test/groovy/datadog/trace/api/debugger/DebuggerConfigBridgeTest.groovy @@ -0,0 +1,116 @@ +package datadog.trace.api.debugger + +import spock.lang.Specification + +class DebuggerConfigBridgeTest extends Specification { + + def cleanup() { + DebuggerConfigBridge.reset() + } + + def "test bridge handles calls"() { + when: + def updater = new MockDebuggerConfigUpdater() + + then: + !DebuggerConfigBridge.isDynamicInstrumentationEnabled() + !DebuggerConfigBridge.isExceptionReplayEnabled() + !DebuggerConfigBridge.isCodeOriginEnabled() + !DebuggerConfigBridge.isDistributedDebuggerEnabled() + + when: + DebuggerConfigBridge.setUpdater(updater) + DebuggerConfigBridge.updateConfig(new DebuggerConfigUpdate.Builder().setExceptionReplayEnabled(true).build()) + + then: + updater.calls == 1 + DebuggerConfigBridge.isExceptionReplayEnabled() + + when: + DebuggerConfigBridge.updateConfig(new DebuggerConfigUpdate.Builder().setDynamicInstrumentationEnabled(true).build()) + + then: + updater.calls == 2 + DebuggerConfigBridge.isExceptionReplayEnabled() + DebuggerConfigBridge.isDynamicInstrumentationEnabled() + + when: + DebuggerConfigBridge.updateConfig(DebuggerConfigUpdate.allDisabled()) + + then: + updater.calls == 3 + !DebuggerConfigBridge.isDynamicInstrumentationEnabled() + !DebuggerConfigBridge.isExceptionReplayEnabled() + !DebuggerConfigBridge.isCodeOriginEnabled() + !DebuggerConfigBridge.isDistributedDebuggerEnabled() + } + + def "test bridge ignores empty updates"() { + def updater = new MockDebuggerConfigUpdater() + DebuggerConfigBridge.setUpdater(updater) + + when: + DebuggerConfigBridge.updateConfig(new DebuggerConfigUpdate.Builder().build()) + + then: + updater.calls == 0 + } + + def "test bridge handles deferred updates"() { + def updater = new MockDebuggerConfigUpdater() + + when: + DebuggerConfigBridge.updateConfig(DebuggerConfigUpdate.allEnabled()) + DebuggerConfigBridge.updateConfig(DebuggerConfigUpdate.allDisabled()) + + then: + updater.calls == 0 + + when: + DebuggerConfigBridge.setUpdater(updater) + + then: + updater.calls == 2 + !DebuggerConfigBridge.isDynamicInstrumentationEnabled() + !DebuggerConfigBridge.isExceptionReplayEnabled() + !DebuggerConfigBridge.isCodeOriginEnabled() + !DebuggerConfigBridge.isDistributedDebuggerEnabled() + } + + private static class MockDebuggerConfigUpdater implements DebuggerConfigUpdater { + private int calls = 0 + private boolean di + private boolean er + private boolean co + private boolean dd + + @Override + void updateConfig(DebuggerConfigUpdate update) { + calls++ + di = update.getDynamicInstrumentationEnabled() != null ? update.getDynamicInstrumentationEnabled() : di + er = update.getExceptionReplayEnabled() != null ? update.getExceptionReplayEnabled() : er + co = update.getCodeOriginEnabled() != null ? update.getCodeOriginEnabled() : co + dd = update.getDistributedDebuggerEnabled() != null ? update.getDistributedDebuggerEnabled() : dd + } + + @Override + boolean isDynamicInstrumentationEnabled() { + return di + } + + @Override + boolean isExceptionReplayEnabled() { + return er + } + + @Override + boolean isCodeOriginEnabled() { + return co + } + + @Override + boolean isDistributedDebuggerEnabled() { + return dd + } + } +} 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/" + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 2de3e282faa..b0176002a4c 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",