Skip to content

Commit 7fb9855

Browse files
mhdatiedevflow.devflow-routing-intake
andauthored
Skip reporting synthetic failures (#11259)
Skip executionError and test exception synthetic testcases Use direct child scan instead of getElementsByTagName for properties lookup Restrict property duplicate check to direct children of properties element Improve doc example to illustrate initializationError grouping logic Fix inaccurate doc claims about file scope and CI behavior Document handled patterns and idempotency in class doc Unify testcase loops into a single pass change flaky test name formatting and removing redundant check Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
1 parent 0e2b83f commit 7fb9855

3 files changed

Lines changed: 82 additions & 56 deletions

File tree

Lines changed: 80 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,51 @@
1-
import org.w3c.dom.Element;
2-
import javax.xml.XMLConstants;
3-
import javax.xml.parsers.DocumentBuilderFactory;
4-
import javax.xml.transform.OutputKeys;
5-
import javax.xml.transform.TransformerFactory;
6-
import javax.xml.transform.dom.DOMSource;
7-
import javax.xml.transform.stream.StreamResult;
81
import java.io.File;
92
import java.nio.file.Files;
103
import java.util.ArrayList;
114
import java.util.LinkedHashMap;
125
import java.util.List;
136
import java.util.Map;
7+
import javax.xml.XMLConstants;
8+
import javax.xml.parsers.DocumentBuilderFactory;
9+
import javax.xml.transform.OutputKeys;
10+
import javax.xml.transform.TransformerFactory;
11+
import javax.xml.transform.dom.DOMSource;
12+
import javax.xml.transform.stream.StreamResult;
13+
import org.w3c.dom.Document;
14+
import org.w3c.dom.Element;
1415

15-
/// Tags intermediate `initializationError` retries with `dd_tags[test.final_status]=skip`.
16+
/// Tags synthetic testcases (`initializationError`, `executionError`, `test exception`) with
17+
/// `dd_tags[test.final_status]=skip` so Test Optimization does not treat them as real failures.
18+
/// The script is idempotent — testcases that already carry a `dd_tags[test.final_status]` property
19+
/// are left unchanged.
20+
///
21+
/// **`initializationError`** — Gradle generates these for setup methods. When retried and
22+
// eventually
23+
/// successful, multiple testcases appear; only the last one passes. All intermediate attempts are
24+
/// tagged skip. Groups with only one (or zero) `initializationError` entries per classname are left
25+
// unmodified.
1626
///
17-
/// Gradle generates synthetic "initializationError" testcases in JUnit reports for setup methods.
18-
/// When a setup is retried and eventually succeeds, multiple testcases are created, with only the
19-
/// last one passing. All intermediate attempts are marked skip so Test Optimization is not misled.
27+
/// **`executionError`** and **`test exception`** — Framework-level synthetic failures that do not
28+
/// represent real test results. Tagged skip unconditionally so Test Optimization treats them as
29+
// non-failures.
2030
///
21-
/// For any suite with multiple `initializationError` test cases (when retries occurred), all entries
22-
/// but the last one are tagged by this script with `dd_tags[test.final_status]=skip`. The last
23-
/// entry is left unmodified, allowing **Test Optimization** to apply its default status inference based
24-
/// on the actual outcome. Files with only one (or zero) `initializationError` test cases are left unmodified.
31+
/// Before (two retries of the same class — first is intermediate, second is the final outcome):
2532
///
26-
/// Before:
27-
///
2833
/// ```
29-
/// <testcase name="initializationError" />
34+
/// <testcase name="initializationError" classname="com.example.MyTest" />
35+
/// <testcase name="initializationError" classname="com.example.MyTest" />
3036
/// ```
31-
///
32-
/// After:
33-
///
37+
///
38+
/// After (only the intermediate attempt is tagged; the last entry is left untouched):
39+
///
3440
/// ```
35-
/// <testcase name="initializationError">
41+
/// <testcase name="initializationError" classname="com.example.MyTest">
3642
/// <properties>
37-
/// <property name="dd_tags[test.final_status]" value="skip" />
43+
/// <property name="dd_tags[test.final_status]" value="skip" />
3844
/// </properties>
3945
/// </testcase>
46+
/// <testcase name="initializationError" classname="com.example.MyTest" />
4047
/// ```
41-
///
48+
///
4249
/// Usage (Java 25): `java TagInitializationErrors.java junit-report.xml`
4350

4451
class TagInitializationErrors {
@@ -61,54 +68,73 @@ public static void main(String[] args) throws Exception {
6168
var doc = dbf.newDocumentBuilder().parse(xmlFile);
6269
var testcases = doc.getElementsByTagName("testcase");
6370
Map<String, List<Element>> byClassname = new LinkedHashMap<>();
71+
boolean modified = false;
6472
for (int i = 0; i < testcases.getLength(); i++) {
6573
var e = (Element) testcases.item(i);
66-
if ("initializationError".equals(e.getAttribute("name"))) {
74+
var name = e.getAttribute("name");
75+
if ("initializationError".equals(name)) {
6776
byClassname.computeIfAbsent(e.getAttribute("classname"), k -> new ArrayList<>()).add(e);
77+
} else if ("executionError".equals(name) || "test exception".equals(name)) {
78+
if (tagSkip(doc, e)) modified = true;
6879
}
6980
}
70-
boolean modified = false;
7181
for (var group : byClassname.values()) {
72-
if (group.size() <= 1) continue;
7382
for (int i = 0; i < group.size() - 1; i++) {
74-
var testcase = group.get(i);
75-
var existingProperties = testcase.getElementsByTagName("properties");
76-
if (existingProperties.getLength() > 0) {
77-
var props = (Element) existingProperties.item(0);
78-
var existingProps = props.getElementsByTagName("property");
79-
boolean alreadyTagged = false;
80-
for (int j = 0; j < existingProps.getLength(); j++) {
81-
if ("dd_tags[test.final_status]".equals(((Element) existingProps.item(j)).getAttribute("name"))) {
82-
alreadyTagged = true;
83-
break;
84-
}
85-
}
86-
if (alreadyTagged) continue;
87-
var property = doc.createElement("property");
88-
property.setAttribute("name", "dd_tags[test.final_status]");
89-
property.setAttribute("value", "skip");
90-
props.appendChild(property);
91-
} else {
92-
var properties = doc.createElement("properties");
93-
var property = doc.createElement("property");
94-
property.setAttribute("name", "dd_tags[test.final_status]");
95-
property.setAttribute("value", "skip");
96-
properties.appendChild(property);
97-
testcase.appendChild(properties);
83+
if (tagSkip(doc, group.get(i))) {
84+
modified = true;
9885
}
99-
modified = true;
10086
}
10187
}
102-
if (!modified) return;
88+
if (!modified) {
89+
return;
90+
}
10391
var tmpFile = File.createTempFile("TagInitializationErrors", ".xml", xmlFile.getParentFile());
10492
try {
10593
var transformer = TransformerFactory.newInstance().newTransformer();
10694
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
10795
transformer.transform(new DOMSource(doc), new StreamResult(tmpFile));
108-
Files.move(tmpFile.toPath(), xmlFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
96+
Files.move(
97+
tmpFile.toPath(), xmlFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
10998
} catch (Exception e) {
11099
tmpFile.delete();
111100
throw e;
112101
}
113102
}
103+
104+
static Element firstChildElement(Element parent, String tagName) {
105+
var children = parent.getChildNodes();
106+
for (int i = 0; i < children.getLength(); i++) {
107+
var child = children.item(i);
108+
if (child instanceof Element e && tagName.equals(e.getTagName())) {
109+
return e;
110+
}
111+
}
112+
return null;
113+
}
114+
115+
static boolean tagSkip(Document doc, Element testcase) {
116+
var props = firstChildElement(testcase, "properties");
117+
if (props != null) {
118+
var children = props.getChildNodes();
119+
for (int j = 0; j < children.getLength(); j++) {
120+
if (children.item(j) instanceof Element e
121+
&& "property".equals(e.getTagName())
122+
&& "dd_tags[test.final_status]".equals(e.getAttribute("name"))) {
123+
return false;
124+
}
125+
}
126+
var property = doc.createElement("property");
127+
property.setAttribute("name", "dd_tags[test.final_status]");
128+
property.setAttribute("value", "skip");
129+
props.appendChild(property);
130+
} else {
131+
var properties = doc.createElement("properties");
132+
var property = doc.createElement("property");
133+
property.setAttribute("name", "dd_tags[test.final_status]");
134+
property.setAttribute("value", "skip");
135+
properties.appendChild(property);
136+
testcase.appendChild(properties);
137+
}
138+
return true;
139+
}
114140
}

.gitlab/collect_results.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ do
9090
echo " (non-stable test names detected)"
9191
fi
9292

93-
echo "Add dd_tags[test.final_status] property on retried synthetics testcase initializationErrors"
93+
echo "Add dd_tags[test.final_status] property on retried synthetics testcase initializationErrors, and all executionError and test exception synthetic testcases"
9494
$JAVA_25_HOME/bin/java "$(dirname "$0")/TagInitializationErrors.java" "$TARGET_DIR/$AGGREGATED_FILE_NAME"
9595

9696
echo "Add dd_tags[test.final_status] property to each testcase on $TARGET_DIR/$AGGREGATED_FILE_NAME"

dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/base/HttpServerTest.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,7 @@ abstract class HttpServerTest<SERVER> extends WithHttpServer<SERVER> {
12481248
}
12491249

12501250
@Flaky(value = "https://github.com/DataDog/dd-trace-java/issues/9396", suites = ["PekkoHttpServerInstrumentationAsyncHttp2Test"])
1251-
def "test exception"() {
1251+
def "Instrumentation test exception"() {
12521252
setup:
12531253
def method = "GET"
12541254
def body = null

0 commit comments

Comments
 (0)