Skip to content

Commit 4b63521

Browse files
authored
Merge branch 'master' into alejandro.gonzalez/APPSEC-61874_2
2 parents 675e33c + 42f154d commit 4b63521

File tree

28 files changed

+919
-361
lines changed

28 files changed

+919
-361
lines changed

.claude/skills/migrate-groovy-to-java/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ When converting Groovy code to Java code, make sure that:
2020
- `@TableTest` and `@MethodSource` may be combined on the same `@ParameterizedTest` when most cases are tabular but a few cases require programmatic setup.
2121
- In combined mode, keep table-friendly cases in `@TableTest`, and put only non-tabular/complex cases in `@MethodSource`.
2222
- If `@TableTest` is not viable for the test at all, use `@MethodSource` only.
23+
- If `@TableTest` was successfully used and if the `@ParameterizedTest` is not used to specify the test name, `@ParameterizedTest` can then be removed as `@TableTest` replace it fully.
2324
- For `@MethodSource`, name the arguments method `<testMethodName>Arguments` (camelCase, e.g. `testMethodArguments`) and return `Stream<Arguments>` using `Stream.of(...)` and `arguments(...)` with static import.
2425
- Ensure parameterized test names are human-readable (i.e. no hashcodes); instead add a description string as the first `Arguments.arguments(...)` value or index the test case
2526
- When converting tuples, create a light dedicated structure instead to keep the typing system

dd-java-agent/agent-aiguard/src/main/java/com/datadog/aiguard/AIGuardInternal.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public BadConfigurationException(final String message) {
6969
static final String ACTION_TAG = "ai_guard.action";
7070
static final String REASON_TAG = "ai_guard.reason";
7171
static final String BLOCKED_TAG = "ai_guard.blocked";
72+
static final String EVENT_TAG = "ai_guard.event";
7273
static final String META_STRUCT_TAG = "ai_guard";
7374
static final String META_STRUCT_MESSAGES = "messages";
7475
static final String META_STRUCT_CATEGORIES = "attack_categories";
@@ -227,6 +228,7 @@ public Evaluation evaluate(final List<Message> messages, final Options options)
227228
final AgentSpan localRootSpan = span.getLocalRootSpan();
228229
if (localRootSpan != null) {
229230
localRootSpan.setTag(Tags.AI_GUARD_KEEP, true);
231+
localRootSpan.setTag(EVENT_TAG, true);
230232
}
231233
try (final AgentScope scope = tracer.activateSpan(span)) {
232234
final Message last = messages.get(messages.size() - 1);

dd-java-agent/agent-aiguard/src/test/groovy/com/datadog/aiguard/AIGuardInternalTests.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ class AIGuardInternalTests extends DDSpecification {
190190
then:
191191
1 * span.setTag(AIGuardInternal.TARGET_TAG, suite.target)
192192
1 * localRootSpan.setTag(Tags.AI_GUARD_KEEP, true)
193+
1 * localRootSpan.setTag(AIGuardInternal.EVENT_TAG, true)
193194
if (suite.target == 'tool') {
194195
1 * span.setTag(AIGuardInternal.TOOL_TAG, 'calc')
195196
}

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/EarlyFlakeDetection.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ public boolean suppressFailures() {
7171
return suppressFailures;
7272
}
7373

74+
@Override
75+
public boolean propagateFailure() {
76+
// used to bypass TestNG's RetryAnalyzer, which made the framework session status depend on
77+
// result order:
78+
// pass + fail -> fail (correct)
79+
// fail + pass -> pass (incorrect)
80+
return !suppressFailures && results == ExecutionAggregation.MIXED;
81+
}
82+
7483
private int getExecutions(long durationMillis) {
7584
for (ExecutionsByDuration e : executionsByDuration) {
7685
if (durationMillis <= e.getDurationMillis()) {

dd-java-agent/instrumentation/testng/testng-7.0/src/main/java/datadog/trace/instrumentation/testng7/TestNGExecutionInstrumentation.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public void methodAdvice(MethodTransformer transformer) {
4747

4848
transformer.applyAdvice(
4949
named("runTestResultListener").and(takesArgument(0, named("org.testng.ITestResult"))),
50-
TestNGExecutionInstrumentation.class.getName() + "$SuppressFailuresAdvice");
50+
TestNGExecutionInstrumentation.class.getName() + "$ModifyStatusAdvice");
5151
}
5252

5353
@Override
@@ -106,24 +106,27 @@ public static void shouldRetryTestMethod(
106106
}
107107
}
108108

109-
public static class SuppressFailuresAdvice {
109+
public static class ModifyStatusAdvice {
110110
@SuppressWarnings("bytebuddy-exception-suppression")
111111
@Advice.OnMethodEnter
112-
public static void suppressFailures(@Advice.Argument(0) final ITestResult result) {
113-
if (result.getStatus() != ITestResult.FAILURE) {
114-
// nothing to suppress
115-
return;
116-
}
117-
112+
public static void modifyStatus(@Advice.Argument(0) final ITestResult result) {
118113
IRetryAnalyzer retryAnalyzer = TestNGUtils.getRetryAnalyzer(result);
119114
if (!(retryAnalyzer instanceof RetryAnalyzer)) {
120115
// test execution policies not injected
121116
return;
122117
}
123118
RetryAnalyzer ddRetryAnalyzer = (RetryAnalyzer) retryAnalyzer;
124-
if (ddRetryAnalyzer.getAndResetSuppressFailures()) {
119+
120+
if (result.getStatus() == ITestResult.FAILURE
121+
&& ddRetryAnalyzer.getAndResetSuppressFailures()) {
125122
// "failed but within success percentage"
126123
result.setStatus(ITestResult.SUCCESS_PERCENTAGE_FAILURE);
124+
} else if (result.isSuccess() && ddRetryAnalyzer.shouldPropagateFailure()) {
125+
// mark status as failed to propagate an earlier failure suppressed by TestNG
126+
result.setStatus(ITestResult.FAILURE);
127+
result.setThrowable(
128+
new AssertionError(
129+
"Datadog: propagating test failure based on aggregated execution results"));
127130
}
128131
}
129132
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[ ]
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
[ {
2+
"content" : {
3+
"duration" : ${content_duration},
4+
"error" : 0,
5+
"meta" : {
6+
"_dd.p.tid" : ${content_meta__dd_p_tid},
7+
"component" : "testng",
8+
"dummy_ci_tag" : "dummy_ci_tag_value",
9+
"env" : "none",
10+
"library_version" : ${content_meta_library_version},
11+
"span.kind" : "test_suite_end",
12+
"test.codeowners" : "[\"owner1\",\"owner2\"]",
13+
"test.framework" : "testng",
14+
"test.framework_version" : ${content_meta_test_framework_version},
15+
"test.module" : "testng-7.0",
16+
"test.source.file" : "dummy_source_path",
17+
"test.status" : "fail",
18+
"test.suite" : "org.example.TestFailAndThenSucceed",
19+
"test.type" : "test",
20+
"test_session.name" : "session-name"
21+
},
22+
"metrics" : {
23+
"_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count},
24+
"test.source.end" : 19,
25+
"test.source.start" : 11
26+
},
27+
"name" : "testng.test_suite",
28+
"resource" : "org.example.TestFailAndThenSucceed",
29+
"service" : "worker.org.gradle.process.internal.worker.gradleworkermain",
30+
"start" : ${content_start},
31+
"test_module_id" : ${content_test_module_id},
32+
"test_session_id" : ${content_test_session_id},
33+
"test_suite_id" : ${content_test_suite_id}
34+
},
35+
"type" : "test_suite_end",
36+
"version" : 1
37+
}, {
38+
"content" : {
39+
"duration" : ${content_duration_2},
40+
"error" : 1,
41+
"meta" : {
42+
"_dd.profiling.ctx" : "test",
43+
"_dd.tracer_host" : ${content_meta__dd_tracer_host},
44+
"component" : "testng",
45+
"dummy_ci_tag" : "dummy_ci_tag_value",
46+
"env" : "none",
47+
"error.message" : ${content_meta_error_message},
48+
"error.stack" : ${content_meta_error_stack},
49+
"error.type" : "java.lang.AssertionError",
50+
"language" : "jvm",
51+
"library_version" : ${content_meta_library_version},
52+
"runtime-id" : ${content_meta_runtime_id},
53+
"span.kind" : "test",
54+
"test.codeowners" : "[\"owner1\",\"owner2\"]",
55+
"test.framework" : "testng",
56+
"test.framework_version" : ${content_meta_test_framework_version},
57+
"test.is_new" : "true",
58+
"test.module" : "testng-7.0",
59+
"test.name" : "test_fail_and_then_succeed",
60+
"test.source.file" : "dummy_source_path",
61+
"test.source.method" : "test_fail_and_then_succeed()V",
62+
"test.status" : "fail",
63+
"test.suite" : "org.example.TestFailAndThenSucceed",
64+
"test.type" : "test",
65+
"test_session.name" : "session-name"
66+
},
67+
"metrics" : {
68+
"_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_2},
69+
"_dd.profiling.enabled" : 0,
70+
"_dd.trace_span_attribute_schema" : 0,
71+
"process_id" : ${content_metrics_process_id},
72+
"test.source.end" : 18,
73+
"test.source.start" : 12
74+
},
75+
"name" : "testng.test",
76+
"parent_id" : ${content_parent_id},
77+
"resource" : "org.example.TestFailAndThenSucceed.test_fail_and_then_succeed",
78+
"service" : "worker.org.gradle.process.internal.worker.gradleworkermain",
79+
"span_id" : ${content_span_id},
80+
"start" : ${content_start_2},
81+
"test_module_id" : ${content_test_module_id},
82+
"test_session_id" : ${content_test_session_id},
83+
"test_suite_id" : ${content_test_suite_id},
84+
"trace_id" : ${content_trace_id}
85+
},
86+
"type" : "test",
87+
"version" : 2
88+
}, {
89+
"content" : {
90+
"duration" : ${content_duration_3},
91+
"error" : 0,
92+
"meta" : {
93+
"_dd.profiling.ctx" : "test",
94+
"_dd.tracer_host" : ${content_meta__dd_tracer_host},
95+
"component" : "testng",
96+
"dummy_ci_tag" : "dummy_ci_tag_value",
97+
"env" : "none",
98+
"language" : "jvm",
99+
"library_version" : ${content_meta_library_version},
100+
"runtime-id" : ${content_meta_runtime_id},
101+
"span.kind" : "test",
102+
"test.codeowners" : "[\"owner1\",\"owner2\"]",
103+
"test.final_status" : "fail",
104+
"test.framework" : "testng",
105+
"test.framework_version" : ${content_meta_test_framework_version},
106+
"test.is_new" : "true",
107+
"test.is_retry" : "true",
108+
"test.module" : "testng-7.0",
109+
"test.name" : "test_fail_and_then_succeed",
110+
"test.retry_reason" : "early_flake_detection",
111+
"test.source.file" : "dummy_source_path",
112+
"test.source.method" : "test_fail_and_then_succeed()V",
113+
"test.status" : "pass",
114+
"test.suite" : "org.example.TestFailAndThenSucceed",
115+
"test.type" : "test",
116+
"test_session.name" : "session-name"
117+
},
118+
"metrics" : {
119+
"_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_3},
120+
"_dd.profiling.enabled" : 0,
121+
"_dd.trace_span_attribute_schema" : 0,
122+
"process_id" : ${content_metrics_process_id},
123+
"test.source.end" : 18,
124+
"test.source.start" : 12
125+
},
126+
"name" : "testng.test",
127+
"parent_id" : ${content_parent_id},
128+
"resource" : "org.example.TestFailAndThenSucceed.test_fail_and_then_succeed",
129+
"service" : "worker.org.gradle.process.internal.worker.gradleworkermain",
130+
"span_id" : ${content_span_id_2},
131+
"start" : ${content_start_3},
132+
"test_module_id" : ${content_test_module_id},
133+
"test_session_id" : ${content_test_session_id},
134+
"test_suite_id" : ${content_test_suite_id},
135+
"trace_id" : ${content_trace_id_2}
136+
},
137+
"type" : "test",
138+
"version" : 2
139+
}, {
140+
"content" : {
141+
"duration" : ${content_duration_4},
142+
"error" : 0,
143+
"meta" : {
144+
"_dd.p.tid" : ${content_meta__dd_p_tid_2},
145+
"_dd.profiling.ctx" : "test",
146+
"_dd.tracer_host" : ${content_meta__dd_tracer_host},
147+
"component" : "testng",
148+
"dummy_ci_tag" : "dummy_ci_tag_value",
149+
"env" : "none",
150+
"language" : "jvm",
151+
"library_version" : ${content_meta_library_version},
152+
"runtime-id" : ${content_meta_runtime_id},
153+
"span.kind" : "test_session_end",
154+
"test.command" : "testng-7.0",
155+
"test.early_flake.enabled" : "true",
156+
"test.framework" : "testng",
157+
"test.framework_version" : ${content_meta_test_framework_version},
158+
"test.status" : "fail",
159+
"test.type" : "test",
160+
"test_session.name" : "session-name"
161+
},
162+
"metrics" : {
163+
"_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_4},
164+
"_dd.profiling.enabled" : 0,
165+
"_dd.trace_span_attribute_schema" : 0,
166+
"process_id" : ${content_metrics_process_id}
167+
},
168+
"name" : "testng.test_session",
169+
"resource" : "testng-7.0",
170+
"service" : "worker.org.gradle.process.internal.worker.gradleworkermain",
171+
"start" : ${content_start_4},
172+
"test_session_id" : ${content_test_session_id}
173+
},
174+
"type" : "test_session_end",
175+
"version" : 1
176+
}, {
177+
"content" : {
178+
"duration" : ${content_duration_5},
179+
"error" : 0,
180+
"meta" : {
181+
"_dd.p.tid" : ${content_meta__dd_p_tid_3},
182+
"component" : "testng",
183+
"dummy_ci_tag" : "dummy_ci_tag_value",
184+
"env" : "none",
185+
"library_version" : ${content_meta_library_version},
186+
"span.kind" : "test_module_end",
187+
"test.early_flake.enabled" : "true",
188+
"test.framework" : "testng",
189+
"test.framework_version" : ${content_meta_test_framework_version},
190+
"test.module" : "testng-7.0",
191+
"test.status" : "fail",
192+
"test.type" : "test",
193+
"test_session.name" : "session-name"
194+
},
195+
"metrics" : {
196+
"_dd.host.vcpu_count" : ${content_metrics__dd_host_vcpu_count_5}
197+
},
198+
"name" : "testng.test_module",
199+
"resource" : "testng-7.0",
200+
"service" : "worker.org.gradle.process.internal.worker.gradleworkermain",
201+
"start" : ${content_start_5},
202+
"test_module_id" : ${content_test_module_id},
203+
"test_session_id" : ${content_test_session_id}
204+
},
205+
"type" : "test_module_end",
206+
"version" : 1
207+
} ]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[ ]

0 commit comments

Comments
 (0)