Skip to content

Commit

Permalink
Fix TestNG tracing for parameterized tests that modify parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
nikita-tkachenko-datadog committed Jun 20, 2024
1 parent 9bf5bbd commit 94d24cb
Show file tree
Hide file tree
Showing 10 changed files with 354 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package datadog.trace.instrumentation.testng;

import datadog.trace.api.civisibility.InstrumentationBridge;
import datadog.trace.api.civisibility.events.TestDescriptor;
import datadog.trace.api.civisibility.events.TestEventsHandler;
import datadog.trace.api.civisibility.events.TestSuiteDescriptor;
import datadog.trace.util.AgentThreadFactory;
import org.testng.ITestResult;

public abstract class TestEventsHandlerHolder {

public static volatile TestEventsHandler<TestSuiteDescriptor, TestDescriptor> TEST_EVENTS_HANDLER;
public static volatile TestEventsHandler<TestSuiteDescriptor, ITestResult> TEST_EVENTS_HANDLER;

static {
start();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package datadog.trace.instrumentation.testng;

import datadog.trace.api.civisibility.config.TestIdentifier;
import datadog.trace.api.civisibility.events.TestDescriptor;
import datadog.trace.api.civisibility.events.TestSuiteDescriptor;
import datadog.trace.util.Strings;
import java.io.InputStream;
Expand Down Expand Up @@ -247,16 +246,6 @@ public static TestIdentifier toTestIdentifier(ITestResult result) {
return new TestIdentifier(testSuiteName, testName, testParameters, null);
}

public static TestDescriptor toTestDescriptor(ITestResult result) {
String testSuiteName = result.getInstanceName();
IClass iTestClass = result.getTestClass();
Class<?> testClass = iTestClass != null ? iTestClass.getRealClass() : null;
String testName =
(result.getName() != null) ? result.getName() : result.getMethod().getMethodName();
String parameters = TestNGUtils.getParameters(result);
return new TestDescriptor(testSuiteName, testClass, testName, parameters, result);
}

public static TestSuiteDescriptor toSuiteDescriptor(ITestClass testClass) {
String testSuiteName = testClass.getName();
Class<?> testSuiteClass = testClass.getRealClass();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package datadog.trace.instrumentation.testng;

import datadog.trace.api.civisibility.events.TestDescriptor;
import datadog.trace.api.civisibility.events.TestSuiteDescriptor;
import datadog.trace.api.civisibility.telemetry.tag.TestFrameworkInstrumentation;
import datadog.trace.instrumentation.testng.retry.RetryAnalyzer;
Expand Down Expand Up @@ -75,7 +74,6 @@ public void onConfigurationSkip(ITestResult result) {
public void onTestStart(final ITestResult result) {
TestSuiteDescriptor suiteDescriptor =
TestNGUtils.toSuiteDescriptor(result.getMethod().getTestClass());
TestDescriptor testDescriptor = TestNGUtils.toTestDescriptor(result);
String testSuiteName = result.getInstanceName();
String testName =
(result.getName() != null) ? result.getName() : result.getMethod().getMethodName();
Expand All @@ -86,7 +84,7 @@ public void onTestStart(final ITestResult result) {
String testMethodName = testMethod != null ? testMethod.getName() : null;
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestStart(
suiteDescriptor,
testDescriptor,
result,
testSuiteName,
testName,
FRAMEWORK_NAME,
Expand All @@ -110,16 +108,14 @@ private boolean isRetry(final ITestResult result) {

@Override
public void onTestSuccess(final ITestResult result) {
TestDescriptor testDescriptor = TestNGUtils.toTestDescriptor(result);
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestFinish(testDescriptor);
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestFinish(result);
}

@Override
public void onTestFailure(final ITestResult result) {
TestDescriptor testDescriptor = TestNGUtils.toTestDescriptor(result);
Throwable throwable = result.getThrowable();
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestFailure(testDescriptor, throwable);
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestFinish(testDescriptor);
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestFailure(result, throwable);
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestFinish(result);
}

@Override
Expand All @@ -129,19 +125,18 @@ public void onTestFailedButWithinSuccessPercentage(final ITestResult result) {

@Override
public void onTestSkipped(final ITestResult result) {
TestDescriptor testDescriptor = TestNGUtils.toTestDescriptor(result);
Throwable throwable = result.getThrowable();
if (TestNGUtils.wasRetried(result)) {
// TestNG reports tests retried with IRetryAnalyzer as skipped,
// this is done to avoid failing the build when retrying tests.
// We want to report such tests as failed to Datadog,
// to provide more accurate data (and to enable flakiness detection)
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestFailure(testDescriptor, throwable);
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestFailure(result, throwable);
} else {
// Typically the way of skipping a TestNG test is throwing a SkipException
String reason = throwable != null ? throwable.getMessage() : null;
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestSkip(testDescriptor, reason);
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestSkip(result, reason);
}
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestFinish(testDescriptor);
TestEventsHandlerHolder.TEST_EVENTS_HANDLER.onTestFinish(result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.example.TestFailedThenSucceed
import org.example.TestFailedWithSuccessPercentage
import org.example.TestInheritance
import org.example.TestParameterized
import org.example.TestParameterizedModifiesParams
import org.example.TestSkipped
import org.example.TestSkippedClass
import org.example.TestSkippedNested
Expand Down Expand Up @@ -47,6 +48,7 @@ abstract class TestNGTest extends CiVisibilityInstrumentationTest {
"test-error" | [TestError] | 2
"test-skipped" | [TestSkipped] | 2
"test-parameterized" | [TestParameterized] | 3
"test-parameterized-modifies-params" | [TestParameterizedModifiesParams] | 2
"test-success-with-groups" | [TestSucceedGroups] | 2
"test-class-skipped" | [TestSkippedClass] | 3
"test-success-and-skipped" | [TestSucceedAndSkipped] | 3
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.example;

import static org.testng.AssertJUnit.assertEquals;

import java.util.HashSet;
import java.util.Set;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

/** Inspired by a real-world example */
public class TestParameterizedModifiesParams {

@DataProvider(name = "dataProvider")
public static Object[][] data() {
return new Object[][] {{"I will modify this set", new HashSet<>()}};
}

@Test(dataProvider = "dataProvider")
public void parameterized_test_succeed(final String str, final Set<String> set) {
set.add("why not");
assertEquals(1, set.size());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[ ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
[ {
"type" : "test_suite_end",
"version" : 1,
"content" : {
"test_session_id" : ${content_test_session_id},
"test_module_id" : ${content_test_module_id},
"test_suite_id" : ${content_test_suite_id},
"service" : "worker.org.gradle.process.internal.worker.gradleworkermain",
"name" : "testng.test_suite",
"resource" : "org.example.TestParameterizedModifiesParams",
"start" : ${content_start},
"duration" : ${content_duration},
"error" : 0,
"metrics" : { },
"meta" : {
"test.type" : "test",
"os.architecture" : ${content_meta_os_architecture},
"test.source.file" : "dummy_source_path",
"test.module" : "testng-6",
"test.status" : "pass",
"runtime.name" : ${content_meta_runtime_name},
"runtime.vendor" : ${content_meta_runtime_vendor},
"env" : "none",
"os.platform" : ${content_meta_os_platform},
"dummy_ci_tag" : "dummy_ci_tag_value",
"os.version" : ${content_meta_os_version},
"library_version" : ${content_meta_library_version},
"component" : "testng",
"span.kind" : "test_suite_end",
"test.suite" : "org.example.TestParameterizedModifiesParams",
"runtime.version" : ${content_meta_runtime_version},
"test.framework_version" : ${content_meta_test_framework_version},
"test.framework" : "testng"
}
}
}, {
"type" : "test",
"version" : 2,
"content" : {
"trace_id" : ${content_trace_id},
"span_id" : ${content_span_id},
"parent_id" : ${content_parent_id},
"test_session_id" : ${content_test_session_id},
"test_module_id" : ${content_test_module_id},
"test_suite_id" : ${content_test_suite_id},
"service" : "worker.org.gradle.process.internal.worker.gradleworkermain",
"name" : "testng.test",
"resource" : "org.example.TestParameterizedModifiesParams.parameterized_test_succeed",
"start" : ${content_start_2},
"duration" : ${content_duration_2},
"error" : 0,
"metrics" : {
"process_id" : ${content_metrics_process_id},
"_dd.profiling.enabled" : 0,
"_dd.trace_span_attribute_schema" : 0,
"test.source.end" : 18,
"test.source.start" : 12
},
"meta" : {
"os.architecture" : ${content_meta_os_architecture},
"test.source.file" : "dummy_source_path",
"test.source.method" : "parameterized_test_succeed(Ljava/lang/String;Ljava/util/Set;)V",
"test.module" : "testng-6",
"test.status" : "pass",
"language" : "jvm",
"runtime.name" : ${content_meta_runtime_name},
"os.platform" : ${content_meta_os_platform},
"test.codeowners" : "[\"owner1\",\"owner2\"]",
"os.version" : ${content_meta_os_version},
"library_version" : ${content_meta_library_version},
"test.name" : "parameterized_test_succeed",
"span.kind" : "test",
"test.suite" : "org.example.TestParameterizedModifiesParams",
"runtime.version" : ${content_meta_runtime_version},
"runtime-id" : ${content_meta_runtime_id},
"test.type" : "test",
"runtime.vendor" : ${content_meta_runtime_vendor},
"env" : "none",
"dummy_ci_tag" : "dummy_ci_tag_value",
"test.parameters" : "{\"arguments\":{\"0\":\"I will modify this set\",\"1\":\"[]\"}}",
"component" : "testng",
"_dd.profiling.ctx" : "test",
"test.framework_version" : ${content_meta_test_framework_version},
"test.framework" : "testng"
}
}
}, {
"type" : "test_session_end",
"version" : 1,
"content" : {
"test_session_id" : ${content_test_session_id},
"service" : "worker.org.gradle.process.internal.worker.gradleworkermain",
"name" : "testng.test_session",
"resource" : "testng-6",
"start" : ${content_start_3},
"duration" : ${content_duration_3},
"error" : 0,
"metrics" : {
"process_id" : ${content_metrics_process_id},
"_dd.profiling.enabled" : 0,
"_dd.trace_span_attribute_schema" : 0
},
"meta" : {
"test.type" : "test",
"os.architecture" : ${content_meta_os_architecture},
"test.status" : "pass",
"language" : "jvm",
"runtime.name" : ${content_meta_runtime_name},
"runtime.vendor" : ${content_meta_runtime_vendor},
"env" : "none",
"os.platform" : ${content_meta_os_platform},
"dummy_ci_tag" : "dummy_ci_tag_value",
"os.version" : ${content_meta_os_version},
"library_version" : ${content_meta_library_version},
"component" : "testng",
"_dd.profiling.ctx" : "test",
"span.kind" : "test_session_end",
"runtime.version" : ${content_meta_runtime_version},
"runtime-id" : ${content_meta_runtime_id},
"test.command" : "testng-6",
"test.framework_version" : ${content_meta_test_framework_version},
"test.framework" : "testng"
}
}
}, {
"type" : "test_module_end",
"version" : 1,
"content" : {
"test_session_id" : ${content_test_session_id},
"test_module_id" : ${content_test_module_id},
"service" : "worker.org.gradle.process.internal.worker.gradleworkermain",
"name" : "testng.test_module",
"resource" : "testng-6",
"start" : ${content_start_4},
"duration" : ${content_duration_4},
"error" : 0,
"metrics" : { },
"meta" : {
"test.type" : "test",
"os.architecture" : ${content_meta_os_architecture},
"test.module" : "testng-6",
"test.status" : "pass",
"runtime.name" : ${content_meta_runtime_name},
"runtime.vendor" : ${content_meta_runtime_vendor},
"env" : "none",
"os.platform" : ${content_meta_os_platform},
"dummy_ci_tag" : "dummy_ci_tag_value",
"os.version" : ${content_meta_os_version},
"library_version" : ${content_meta_library_version},
"component" : "testng",
"span.kind" : "test_module_end",
"runtime.version" : ${content_meta_runtime_version},
"test.framework_version" : ${content_meta_test_framework_version},
"test.framework" : "testng"
}
}
} ]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[ ]
Loading

0 comments on commit 94d24cb

Please sign in to comment.