Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions .gitlab/TagInitializationErrors.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import org.w3c.dom.Element;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/// Tags intermediate `initializationError` retries with `dd_tags[test.final_status]=skip`.
///
/// Gradle generates synthetic "initializationError" testcases in JUnit reports for setup methods.
/// When a setup is retried and eventually succeeds, multiple testcases are created, with only the
/// last one passing. All intermediate attempts are marked skip so Test Optimization is not misled.
///
/// For any suite with multiple `initializationError` test cases (when retries occurred), all entries
/// but the last one are tagged by this script with `dd_tags[test.final_status]=skip`. The last
/// entry is left unmodified, allowing **Test Optimization** to apply its default status inference based
/// on the actual outcome. Files with only one (or zero) `initializationError` test cases are left unmodified.
///
/// Before:
///
/// ```
/// <testcase name="initializationError" />
/// ```
///
/// After:
///
/// ```
/// <testcase name="initializationError">
/// <properties>
/// <property name="dd_tags[test.final_status]" value="skip" />
/// </properties>
/// </testcase>
/// ```
///
/// Usage (Java 25): `java TagInitializationErrors.java junit-report.xml`

class TagInitializationErrors {
public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.err.println("Usage: java TagInitializationErrors.java <xml-file>");
System.exit(1);
}
var xmlFile = new File(args[0]);
if (!xmlFile.exists()) {
System.err.println("File not found: " + xmlFile);
System.exit(1);
}
var dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setExpandEntityReferences(false);
var doc = dbf.newDocumentBuilder().parse(xmlFile);
var testcases = doc.getElementsByTagName("testcase");
Map<String, List<Element>> byClassname = new LinkedHashMap<>();
for (int i = 0; i < testcases.getLength(); i++) {
var e = (Element) testcases.item(i);
if ("initializationError".equals(e.getAttribute("name"))) {
byClassname.computeIfAbsent(e.getAttribute("classname"), k -> new ArrayList<>()).add(e);
}
}
boolean modified = false;
for (var group : byClassname.values()) {
if (group.size() <= 1) continue;
for (int i = 0; i < group.size() - 1; i++) {
var testcase = group.get(i);
var existingProperties = testcase.getElementsByTagName("properties");
if (existingProperties.getLength() > 0) {
var props = (Element) existingProperties.item(0);
var existingProps = props.getElementsByTagName("property");
boolean alreadyTagged = false;
for (int j = 0; j < existingProps.getLength(); j++) {
if ("dd_tags[test.final_status]".equals(((Element) existingProps.item(j)).getAttribute("name"))) {
alreadyTagged = true;
break;
}
}
if (alreadyTagged) continue;
var property = doc.createElement("property");
property.setAttribute("name", "dd_tags[test.final_status]");
property.setAttribute("value", "skip");
props.appendChild(property);
} else {
var properties = doc.createElement("properties");
var property = doc.createElement("property");
property.setAttribute("name", "dd_tags[test.final_status]");
property.setAttribute("value", "skip");
properties.appendChild(property);
testcase.appendChild(properties);
}
modified = true;
}
}
if (!modified) return;
var tmpFile = File.createTempFile("TagInitializationErrors", ".xml", xmlFile.getParentFile());
try {
var transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.transform(new DOMSource(doc), new StreamResult(tmpFile));
Files.move(tmpFile.toPath(), xmlFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
tmpFile.delete();
throw e;
}
}
}
3 changes: 3 additions & 0 deletions .gitlab/collect_results.sh
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ do
echo " (non-stable test names detected)"
fi

echo "Add dd_tags[test.final_status] property on retried synthetics testcase initializationErrors"
$JAVA_25_HOME/bin/java "$(dirname "$0")/TagInitializationErrors.java" "$TARGET_DIR/$AGGREGATED_FILE_NAME"

echo "Add dd_tags[test.final_status] property to each testcase on $TARGET_DIR/$AGGREGATED_FILE_NAME"
xsl_file="$(dirname "$0")/add_final_status.xsl"
tmp_file="$(mktemp)"
Expand Down
Loading