diff --git a/.gitlab/TagSyntheticFailures.java b/.gitlab/TagSyntheticFailures.java
deleted file mode 100644
index a52b8d1e5a9..00000000000
--- a/.gitlab/TagSyntheticFailures.java
+++ /dev/null
@@ -1,140 +0,0 @@
-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;
-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 org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-/// Tags synthetic testcases (`initializationError`, `executionError`, `test exception`) with
-/// `dd_tags[test.final_status]=skip` so Test Optimization does not treat them as real failures.
-/// The script is idempotent — testcases that already carry a `dd_tags[test.final_status]` property
-/// are left unchanged.
-///
-/// **`initializationError`** — Gradle generates these for setup methods. When retried and
-// eventually
-/// successful, multiple testcases appear; only the last one passes. All intermediate attempts are
-/// tagged skip. Groups with only one (or zero) `initializationError` entries per classname are left
-// unmodified.
-///
-/// **`executionError`** and **`test exception`** — Framework-level synthetic failures that do not
-/// represent real test results. Tagged skip unconditionally so Test Optimization treats them as
-// non-failures.
-///
-/// Before (two retries of the same class — first is intermediate, second is the final outcome):
-///
-/// ```
-///
-///
-/// ```
-///
-/// After (only the intermediate attempt is tagged; the last entry is left untouched):
-///
-/// ```
-///
-///
-///
-///
-///
-///
-/// ```
-///
-/// Usage (Java 25): `java TagSyntheticFailures.java junit-report.xml`
-
-class TagSyntheticFailures {
- public static void main(String[] args) throws Exception {
- if (args.length == 0) {
- System.err.println("Usage: java TagSyntheticFailures.java ");
- 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> byClassname = new LinkedHashMap<>();
- boolean modified = false;
- for (int i = 0; i < testcases.getLength(); i++) {
- var e = (Element) testcases.item(i);
- var name = e.getAttribute("name");
- if ("initializationError".equals(name)) {
- byClassname.computeIfAbsent(e.getAttribute("classname"), k -> new ArrayList<>()).add(e);
- } else if ("executionError".equals(name) || "test exception".equals(name)) {
- if (tagSkip(doc, e)) modified = true;
- }
- }
- for (var group : byClassname.values()) {
- for (int i = 0; i < group.size() - 1; i++) {
- if (tagSkip(doc, group.get(i))) {
- modified = true;
- }
- }
- }
- if (!modified) {
- return;
- }
- var tmpFile = File.createTempFile("TagSyntheticFailures", ".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;
- }
- }
-
- static Element firstChildElement(Element parent, String tagName) {
- var children = parent.getChildNodes();
- for (int i = 0; i < children.getLength(); i++) {
- var child = children.item(i);
- if (child instanceof Element e && tagName.equals(e.getTagName())) {
- return e;
- }
- }
- return null;
- }
-
- static boolean tagSkip(Document doc, Element testcase) {
- var props = firstChildElement(testcase, "properties");
- if (props != null) {
- var children = props.getChildNodes();
- for (int j = 0; j < children.getLength(); j++) {
- if (children.item(j) instanceof Element e
- && "property".equals(e.getTagName())
- && "dd_tags[test.final_status]".equals(e.getAttribute("name"))) {
- return false;
- }
- }
- 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);
- }
- return true;
- }
-}
diff --git a/.gitlab/add_final_status.xsl b/.gitlab/add_final_status.xsl
deleted file mode 100644
index 4b4c0da17fd..00000000000
--- a/.gitlab/add_final_status.xsl
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- fail
- skip
- pass
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.gitlab/collect-result/CollectResults.java b/.gitlab/collect-result/CollectResults.java
new file mode 100644
index 00000000000..ad7c6429cda
--- /dev/null
+++ b/.gitlab/collect-result/CollectResults.java
@@ -0,0 +1,14 @@
+import java.nio.file.Path;
+import java.util.List;
+
+class CollectResults {
+ public static void main(String[] args) throws Exception {
+ var collector =
+ new ResultCollector(
+ Path.of("results"),
+ Path.of("workspace"),
+ List.of(Path.of("workspace"), Path.of("buildSrc")));
+
+ collector.collect();
+ }
+}
diff --git a/.gitlab/collect-result/JUnitReport.java b/.gitlab/collect-result/JUnitReport.java
new file mode 100644
index 00000000000..0c74f4c934b
--- /dev/null
+++ b/.gitlab/collect-result/JUnitReport.java
@@ -0,0 +1,213 @@
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+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 org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+final class JUnitReport {
+ private static final String FINAL_STATUS_PROPERTY = "dd_tags[test.final_status]";
+ private static final Pattern HASH_CODE = Pattern.compile("@[0-9a-f]{5,}");
+ private static final Pattern LOCALHOST_PORT = Pattern.compile("localhost:[0-9]{2,5}");
+ private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY =
+ newDocumentBuilderFactory();
+ private static final TransformerFactory TRANSFORMER_FACTORY = newTransformerFactory();
+
+ private final Document document;
+
+ private JUnitReport(Document document) {
+ this.document = document;
+ }
+
+ static JUnitReport parse(Path xmlFile) throws Exception {
+ var document = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse(xmlFile.toFile());
+ return new JUnitReport(document);
+ }
+
+ boolean addFileAttribute(String sourceFile) {
+ var changed = false;
+ for (var testcase : testcases()) {
+ if (testcase.hasAttribute("time")) {
+ changed |= !sourceFile.equals(testcase.getAttribute("file"));
+ testcase.setAttribute("file", sourceFile);
+ }
+ }
+ return changed;
+ }
+
+ boolean normalizeStableTestNames() {
+ var changed = false;
+ for (var testcase : testcases()) {
+ var attributes = testcase.getAttributes();
+ for (var i = 0; i < attributes.getLength(); i++) {
+ var attribute = attributes.item(i);
+ var value = attribute.getNodeValue();
+ var normalized =
+ LOCALHOST_PORT
+ .matcher(HASH_CODE.matcher(value).replaceAll("@HASHCODE"))
+ .replaceAll("localhost:PORT");
+ if (!value.equals(normalized)) {
+ attribute.setNodeValue(normalized);
+ changed = true;
+ }
+ }
+ }
+ return changed;
+ }
+
+ void tagSyntheticFailures() {
+ Map> initializationErrorsByClassname = new LinkedHashMap<>();
+ for (var testcase : testcases()) {
+ var name = testcase.getAttribute("name");
+ if ("initializationError".equals(name)) {
+ initializationErrorsByClassname
+ .computeIfAbsent(testcase.getAttribute("classname"), ignored -> new ArrayList<>())
+ .add(testcase);
+ } else if ("executionError".equals(name) || "test exception".equals(name)) {
+ addFinalStatusProperty(testcase, "skip", MissingPropertiesPlacement.APPEND_TO_TESTCASE);
+ }
+ }
+
+ for (var group : initializationErrorsByClassname.values()) {
+ for (var i = 0; i < group.size() - 1; i++) {
+ addFinalStatusProperty(
+ group.get(i), "skip", MissingPropertiesPlacement.APPEND_TO_TESTCASE);
+ }
+ }
+ }
+
+ void tagFinalStatuses() {
+ for (var testcase : testcases()) {
+ if (hasFinalStatusProperty(testcase)) {
+ continue;
+ }
+ addFinalStatusProperty(
+ testcase, finalStatus(testcase), MissingPropertiesPlacement.FIRST_CHILD);
+ }
+ }
+
+ void write(Path xmlFile) throws Exception {
+ Files.createDirectories(xmlFile.getParent());
+ var tmpFile = Files.createTempFile(xmlFile.getParent(), "collect-results-", ".xml");
+ try (OutputStream output = Files.newOutputStream(tmpFile)) {
+ var transformer = TRANSFORMER_FACTORY.newTransformer();
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.transform(new DOMSource(document), new StreamResult(output));
+ } catch (Exception e) {
+ Files.deleteIfExists(tmpFile);
+ throw e;
+ }
+ Files.move(tmpFile, xmlFile, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+ private static DocumentBuilderFactory newDocumentBuilderFactory() {
+ try {
+ var factory = DocumentBuilderFactory.newInstance();
+ factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ factory.setXIncludeAware(false);
+ factory.setExpandEntityReferences(false);
+ return factory;
+ } catch (Exception e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ private static TransformerFactory newTransformerFactory() {
+ var factory = TransformerFactory.newInstance();
+ factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+ factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
+ return factory;
+ }
+
+ private List testcases() {
+ var testcases = document.getElementsByTagName("testcase");
+ var elements = new ArrayList(testcases.getLength());
+ for (var i = 0; i < testcases.getLength(); i++) {
+ elements.add((Element) testcases.item(i));
+ }
+ return elements;
+ }
+
+ private boolean addFinalStatusProperty(
+ Element testcase, String status, MissingPropertiesPlacement missingPropertiesPlacement) {
+ var properties = firstChildElement(testcase, "properties");
+ if (properties != null) {
+ if (propertiesHasFinalStatusProperty(properties)) {
+ return false;
+ }
+ } else {
+ properties = document.createElement("properties");
+ if (missingPropertiesPlacement == MissingPropertiesPlacement.FIRST_CHILD) {
+ testcase.insertBefore(properties, testcase.getFirstChild());
+ } else {
+ testcase.appendChild(properties);
+ }
+ }
+
+ var property = document.createElement("property");
+ property.setAttribute("name", FINAL_STATUS_PROPERTY);
+ property.setAttribute("value", status);
+ properties.appendChild(property);
+ return true;
+ }
+
+ private static boolean hasFinalStatusProperty(Element testcase) {
+ var properties = firstChildElement(testcase, "properties");
+ return properties != null && propertiesHasFinalStatusProperty(properties);
+ }
+
+ private static boolean propertiesHasFinalStatusProperty(Element properties) {
+ var children = properties.getChildNodes();
+ for (var i = 0; i < children.getLength(); i++) {
+ if (children.item(i) instanceof Element element
+ && "property".equals(element.getTagName())
+ && FINAL_STATUS_PROPERTY.equals(element.getAttribute("name"))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static String finalStatus(Element testcase) {
+ if (hasChildElement(testcase, "failure") || hasChildElement(testcase, "error")) {
+ return "fail";
+ }
+ if (hasChildElement(testcase, "skipped")) {
+ return "skip";
+ }
+ return "pass";
+ }
+
+ private static Element firstChildElement(Element parent, String tagName) {
+ var children = parent.getChildNodes();
+ for (var i = 0; i < children.getLength(); i++) {
+ if (children.item(i) instanceof Element element && tagName.equals(element.getTagName())) {
+ return element;
+ }
+ }
+ return null;
+ }
+
+ private static boolean hasChildElement(Element parent, String tagName) {
+ return firstChildElement(parent, tagName) != null;
+ }
+
+ private enum MissingPropertiesPlacement {
+ APPEND_TO_TESTCASE,
+ FIRST_CHILD
+ }
+}
diff --git a/.gitlab/collect-result/ResultCollector.java b/.gitlab/collect-result/ResultCollector.java
new file mode 100644
index 00000000000..d993cd41d32
--- /dev/null
+++ b/.gitlab/collect-result/ResultCollector.java
@@ -0,0 +1,114 @@
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Stream;
+
+final class ResultCollector {
+ private static final int[] AGGREGATED_NAME_FIELDS_FROM_END = {5, 2, 1};
+
+ private final Path resultsDir;
+ private final Path workspaceDir;
+ private final List searchDirs;
+ private final SourceFileResolver sourceFileResolver;
+
+ ResultCollector(Path resultsDir, Path workspaceDir, List searchDirs) {
+ this.resultsDir = resultsDir;
+ this.workspaceDir = workspaceDir;
+ this.searchDirs = searchDirs;
+ this.sourceFileResolver = new SourceFileResolver(workspaceDir);
+ }
+
+ void collect() throws Exception {
+ Files.createDirectories(resultsDir);
+ Files.createDirectories(workspaceDir);
+
+ var testResultDirs = findTestResultDirs();
+ if (testResultDirs.isEmpty()) {
+ System.out.println("No test results found");
+ return;
+ }
+
+ System.out.println("Saving test results:");
+ for (var sourceXml : findXmlFiles(testResultDirs)) {
+ collect(sourceXml);
+ }
+ }
+
+ private void collect(Path sourceXml) throws Exception {
+ var aggregatedName = aggregatedFileName(sourceXml);
+ var targetXml = resultsDir.resolve(aggregatedName);
+ System.out.print("- " + toUnixString(sourceXml) + " as " + aggregatedName);
+
+ var sourceFile = sourceFileResolver.resolve(sourceXml);
+ var report = JUnitReport.parse(sourceXml);
+ var reportChangedBeforeFinalStatus = report.addFileAttribute(sourceFile);
+ reportChangedBeforeFinalStatus |= report.normalizeStableTestNames();
+ report.tagSyntheticFailures();
+ report.tagFinalStatuses();
+ report.write(targetXml);
+
+ if (reportChangedBeforeFinalStatus) {
+ System.out.print(" (non-stable test names detected)");
+ }
+ System.out.println();
+ }
+
+ private List findTestResultDirs() throws IOException {
+ var found = new ArrayList();
+ for (var searchDir : searchDirs) {
+ if (!Files.isDirectory(searchDir)) {
+ continue;
+ }
+ try (var paths = Files.walk(searchDir)) {
+ paths
+ .filter(Files::isDirectory)
+ .filter(path -> "test-results".equals(fileName(path)))
+ .forEach(found::add);
+ }
+ }
+ found.sort(Comparator.comparing(ResultCollector::toUnixString));
+ return found;
+ }
+
+ private static List findXmlFiles(List testResultDirs) throws IOException {
+ var found = new ArrayList();
+ for (var testResultDir : testResultDirs) {
+ try (Stream paths = Files.walk(testResultDir)) {
+ paths
+ .filter(Files::isRegularFile)
+ .filter(path -> fileName(path).endsWith(".xml"))
+ .forEach(found::add);
+ } catch (UncheckedIOException e) {
+ throw e.getCause();
+ }
+ }
+ found.sort(Comparator.comparing(ResultCollector::toUnixString));
+ return found;
+ }
+
+ private static String aggregatedFileName(Path sourceXml) {
+ var normalized = sourceXml.normalize();
+ var parts = new ArrayList(AGGREGATED_NAME_FIELDS_FROM_END.length);
+ var nameCount = normalized.getNameCount();
+ for (var fieldFromEnd : AGGREGATED_NAME_FIELDS_FROM_END) {
+ var index = nameCount - fieldFromEnd;
+ if (index >= 0) {
+ parts.add(normalized.getName(index).toString());
+ }
+ }
+ return String.join("_", parts);
+ }
+
+ private static String fileName(Path path) {
+ var fileName = path.getFileName();
+ return fileName == null ? "" : fileName.toString();
+ }
+
+ static String toUnixString(Path path) {
+ return path.toString().replace(path.getFileSystem().getSeparator(), "/");
+ }
+}
diff --git a/.gitlab/collect-result/SourceFileResolver.java b/.gitlab/collect-result/SourceFileResolver.java
new file mode 100644
index 00000000000..bf35fc842be
--- /dev/null
+++ b/.gitlab/collect-result/SourceFileResolver.java
@@ -0,0 +1,177 @@
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+final class SourceFileResolver {
+ private static final String UNKNOWN = "UNKNOWN";
+ private static final Pattern CLASS_DECLARATION =
+ Pattern.compile("\\b(?:static\\s+)?class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\b");
+
+ private final String workspacePrefix;
+ private final Map indexes = new HashMap<>();
+
+ SourceFileResolver(Path workspaceDir) {
+ this.workspacePrefix = ResultCollector.toUnixString(workspaceDir) + "/";
+ }
+
+ String resolve(Path resultXml) throws IOException {
+ var resultXmlPath = ResultCollector.toUnixString(resultXml);
+ var sourceRoot = sourceRoot(resultXmlPath);
+ if (resultXmlPath.contains("#")) {
+ return sourceRoot;
+ }
+
+ var className = className(resultXml);
+ if (className.isEmpty()) {
+ return UNKNOWN;
+ }
+
+ return indexes.computeIfAbsent(sourceRoot, SourceIndex::new).resolve(className);
+ }
+
+ private String sourceRoot(String resultXmlPath) {
+ var buildIndex = resultXmlPath.indexOf("/build");
+ var projectPath = buildIndex >= 0 ? resultXmlPath.substring(0, buildIndex) : resultXmlPath;
+ if (projectPath.startsWith(workspacePrefix)) {
+ projectPath = projectPath.substring(workspacePrefix.length());
+ }
+ return projectPath + "/src";
+ }
+
+ private static String className(Path resultXml) {
+ var fileName = resultXml.getFileName();
+ if (fileName == null) {
+ return "";
+ }
+
+ var name = fileName.toString();
+ if (name.endsWith(".xml")) {
+ name = name.substring(0, name.length() - ".xml".length());
+ }
+
+ var testPrefix = name.lastIndexOf("TEST-");
+ if (testPrefix >= 0) {
+ name = name.substring(testPrefix + "TEST-".length());
+ }
+
+ var packageSeparator = name.lastIndexOf('.');
+ if (packageSeparator >= 0) {
+ name = name.substring(packageSeparator + 1);
+ }
+
+ var innerClassSeparator = name.lastIndexOf('$');
+ if (innerClassSeparator >= 0) {
+ name = name.substring(innerClassSeparator + 1);
+ }
+ return name;
+ }
+
+ private static final class SourceIndex {
+ private final String sourceRoot;
+ private final Map> classLocations = new HashMap<>();
+ private boolean indexed;
+
+ private SourceIndex(String sourceRoot) {
+ this.sourceRoot = sourceRoot;
+ }
+
+ private String resolve(String className) {
+ try {
+ indexIfNecessary();
+ } catch (IOException e) {
+ return UNKNOWN;
+ }
+
+ var locations = locations(className);
+ if (locations.isEmpty()) {
+ return UNKNOWN;
+ }
+
+ var commonRoot = commonRoot(locations);
+ if (commonRoot == null) {
+ return UNKNOWN;
+ }
+ return "/" + ResultCollector.toUnixString(commonRoot);
+ }
+
+ private List locations(String className) {
+ var locations = new ArrayList();
+ for (var entry : classLocations.entrySet()) {
+ if (entry.getKey().startsWith(className)) {
+ locations.addAll(entry.getValue());
+ }
+ }
+ return locations;
+ }
+
+ private void indexIfNecessary() throws IOException {
+ if (indexed) {
+ return;
+ }
+ indexed = true;
+
+ var root = Path.of(sourceRoot);
+ if (!Files.isDirectory(root)) {
+ return;
+ }
+
+ try (var paths = Files.walk(root)) {
+ var iterator =
+ paths.filter(Files::isRegularFile).filter(SourceIndex::isSourceFile).iterator();
+ while (iterator.hasNext()) {
+ try {
+ index(iterator.next());
+ } catch (IOException ignored) {
+ // Match grep's best-effort behavior from the old shell implementation.
+ }
+ }
+ }
+ }
+
+ private static boolean isSourceFile(Path path) {
+ var fileName = path.getFileName();
+ if (fileName == null) {
+ return false;
+ }
+ var name = fileName.toString();
+ return name.endsWith(".java")
+ || name.endsWith(".groovy")
+ || name.endsWith(".kt")
+ || name.endsWith(".scala");
+ }
+
+ private void index(Path sourceFile) throws IOException {
+ try (BufferedReader reader = Files.newBufferedReader(sourceFile, StandardCharsets.UTF_8)) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ var matcher = CLASS_DECLARATION.matcher(line);
+ while (matcher.find()) {
+ classLocations
+ .computeIfAbsent(matcher.group(1), ignored -> new ArrayList<>())
+ .add(sourceFile);
+ }
+ }
+ }
+ }
+
+ private static Path commonRoot(List locations) {
+ var commonRoot = locations.get(0);
+ for (var location : locations) {
+ while (commonRoot != null && !location.startsWith(commonRoot)) {
+ commonRoot = commonRoot.getParent();
+ }
+ if (commonRoot == null) {
+ return null;
+ }
+ }
+ return commonRoot.getNameCount() == 0 ? null : commonRoot;
+ }
+ }
+}
diff --git a/.gitlab/collect_results.sh b/.gitlab/collect_results.sh
index 36614632481..de404f48513 100755
--- a/.gitlab/collect_results.sh
+++ b/.gitlab/collect_results.sh
@@ -3,100 +3,13 @@
# Save all important reports and artifacts into (project-root)/results
# This folder will be saved by gitlab and available after test runs.
-set -e
-# Enable '**' support
-shopt -s globstar
+set -euo pipefail
-TEST_RESULTS_DIR=results
-WORKSPACE_DIR=workspace
-mkdir -p $TEST_RESULTS_DIR
-mkdir -p $WORKSPACE_DIR
-
-# Main project modules redirect their build directory to workspace//build/ in CI
-# (see build.gradle.kts layout.buildDirectory override). buildSrc is a separate Gradle build
-# that runs before the main build is configured, so this redirect never applies to it;
-# its test results always land in buildSrc/**/build/test-results/, not under workspace/.
-SEARCH_DIRS=($WORKSPACE_DIR buildSrc)
-
-mapfile -t TEST_RESULT_DIRS < <(find "${SEARCH_DIRS[@]}" -name test-results -type d)
-
-if [[ ${#TEST_RESULT_DIRS[@]} -eq 0 ]]; then
- echo "No test results found"
- exit 0
+java_bin="${JAVA_25_HOME:-}"
+if [[ -n "$java_bin" ]]; then
+ java_bin="$java_bin/bin/java"
+else
+ java_bin="java"
fi
-function get_source_file () {
- file_path="${RESULT_XML_FILE%%"/build"*}"
- file_path="${file_path/#"$WORKSPACE_DIR"\//}/src"
- if ! [[ $RESULT_XML_FILE == *"#"* ]]; then
- class="${RESULT_XML_FILE%.xml}"
- class="${class##*"TEST-"}"
- class="${class##*"."}"
- class="${class##*"$"}" # remove inner class name if it exists
- set +e # allow grep to fail
- common_root=$(grep -rl "class $class\|static class $class" "$file_path" 2>/dev/null | head -n 1)
- set -e
-
- if [[ -n "$common_root" ]]; then
- while IFS= read -r line; do
- while [[ $line != "$common_root"* ]]; do
- common_root=$(dirname "$common_root")
- if [[ "$common_root" == "$common_root/.." ]] || [[ "$common_root" == "/" ]]; then
- common_root=""
- break
- fi
- done
- done < <(grep -rl "class $class\|static class $class" "$file_path" 2>/dev/null)
-
- if [[ -n "$common_root" && "$common_root" != "/" ]]; then
- file_path="/$common_root"
- else
- file_path="UNKNOWN"
- fi
- else
- file_path="UNKNOWN"
- fi
- fi
-}
-
-echo "Saving test results:"
-while IFS= read -r -d '' RESULT_XML_FILE
-do
- echo -n "- $RESULT_XML_FILE"
- # Assuming the path looks like that: dd-java-agent/instrumentation/tomcat/tomcat-5.5/build/test-results/forkedTest/TEST-TomcatServletV1ForkedTest.xml
- # it will extracts 3 components from the path (counting from the end), to form the new name AGGREGATED_FILE_NAME:
- #
- # 1. Field 1 (from end): The XML filename itself
- # 2. Field 2 (from end): The test suite type (test, forkedTest, etc.)
- # 3. Field 5 (from end): The module/subproject name
- #
- # E.g. for the example path: tomcat-5.5_forkedTest_TEST-TomcatServletV1ForkedTest.xml
- AGGREGATED_FILE_NAME=$(echo "$RESULT_XML_FILE" | rev | cut -d "/" -f 1,2,5 | rev | tr "/" "_")
- echo -n " as $AGGREGATED_FILE_NAME"
- TARGET_DIR="$TEST_RESULTS_DIR"
- mkdir -p "$TARGET_DIR"
- cp "$RESULT_XML_FILE" "$TARGET_DIR/$AGGREGATED_FILE_NAME"
- # Insert file attribute to testcase XML nodes
- get_source_file
- sed -i "/