diff --git a/.gitlab/TagInitializationErrors.java b/.gitlab/TagInitializationErrors.java new file mode 100644 index 00000000000..d2bee684419 --- /dev/null +++ b/.gitlab/TagInitializationErrors.java @@ -0,0 +1,107 @@ +import org.w3c.dom.Element; +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: +/// +/// ``` +/// +/// ``` +/// +/// After: +/// +/// ``` +/// +/// +/// +/// +/// +/// ``` +/// +/// 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 "); + System.exit(1); + } + var xmlFile = new File(args[0]); + if (!xmlFile.exists()) { + System.err.println("File not found: " + xmlFile); + System.exit(1); + } + var doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFile); + var testcases = doc.getElementsByTagName("testcase"); + Map> 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; + } + } +} diff --git a/.gitlab/collect_results.sh b/.gitlab/collect_results.sh index 5991a125902..dec34ed5d59 100755 --- a/.gitlab/collect_results.sh +++ b/.gitlab/collect_results.sh @@ -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)"