This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 8a0a2edbf7 feat(push): record test counts and wall-clock times in
test-run-history.tsv
8a0a2edbf7 is described below
commit 8a0a2edbf7cad4211622e5e837605023dd3d819e
Author: James Bognar <[email protected]>
AuthorDate: Thu May 28 09:16:22 2026 -0400
feat(push): record test counts and wall-clock times in test-run-history.tsv
Instrument scripts/push.py to parse Surefire XML reports after each
successful test run and append a TSV row (timestamp, git SHA, branch,
tests_run, failures, errors, skipped, wall_sec) to
juneau-utest/test-run-history.tsv. The file is committed with every
push, giving a git-native history of test suite health over time.
Supersedes the Jenkins/GHA CI perf-guard wiring from TODO-139; the
local perf-baseline.txt / ci-perf-guard.py tools remain for developer
use. Closes TODO-139.
---
juneau-utest/test-run-history.tsv | 2 ++
pom.xml | 5 +--
scripts/push.py | 66 +++++++++++++++++++++++++++++++++++++--
3 files changed, 69 insertions(+), 4 deletions(-)
diff --git a/juneau-utest/test-run-history.tsv
b/juneau-utest/test-run-history.tsv
new file mode 100644
index 0000000000..9c00a097d8
--- /dev/null
+++ b/juneau-utest/test-run-history.tsv
@@ -0,0 +1,2 @@
+timestamp git_sha branch tests_run failures errors skipped
wall_sec
+2026-05-28T13:15:26Z 36d714f9b237 master 125869 0 0 21
136
diff --git a/pom.xml b/pom.xml
index 2ce127e27a..1e3ad91fea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -167,8 +167,9 @@
<exclude>**/META-INF/services/**</exclude>
<!-- Performance baseline data
file - plain text metrics, no license header syntax -->
-
<exclude>**/perf-baseline.txt</exclude>
- </excludes>
+ <exclude>**/perf-baseline.txt</exclude>
+
<exclude>**/test-run-history.tsv</exclude>
+ </excludes>
<consoleOutput>true</consoleOutput>
<!-- Note: useEclipseDefaultExcludes
does not work for subprojects. See RAT-107 -->
</configuration>
diff --git a/scripts/push.py b/scripts/push.py
index 8d77ca8f0a..e992af1ce3 100755
--- a/scripts/push.py
+++ b/scripts/push.py
@@ -39,6 +39,9 @@ import os
import platform
import subprocess
import sys
+import time
+import xml.etree.ElementTree as ET
+from datetime import datetime, timezone
from pathlib import Path
@@ -381,6 +384,58 @@ def play_sound(success=True): # NOSONAR python:S3776 --
Cognitive complexity is
pass
+def _collect_surefire_stats(juneau_root: Path):
+ """Aggregate tests/failures/errors/skipped from all Surefire XML files
under juneau-utest."""
+ reports = juneau_root / "juneau-utest" / "target" / "surefire-reports"
+ totals = {"tests": 0, "failures": 0, "errors": 0, "skipped": 0}
+ found = False
+ for xml_path in sorted(reports.rglob("TEST-*.xml")):
+ try:
+ root = ET.parse(xml_path).getroot()
+ for key in totals:
+ totals[key] += int(root.attrib.get(key, 0))
+ found = True
+ except Exception:
+ pass
+ return (totals["tests"], totals["failures"], totals["errors"],
totals["skipped"]) if found else None
+
+
+def _append_test_run_history(juneau_root: Path, wall_sec: int) -> None:
+ """Append one row to juneau-utest/test-run-history.tsv; creates with
header if absent. Never raises."""
+ try:
+ tsv_path = juneau_root / "juneau-utest" / "test-run-history.tsv"
+ header =
"timestamp\tgit_sha\tbranch\ttests_run\tfailures\terrors\tskipped\twall_sec"
+ need_header = not tsv_path.exists() or tsv_path.stat().st_size == 0
+
+ ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
+
+ try:
+ sha = subprocess.check_output(
+ ["git", "rev-parse", "HEAD"], cwd=juneau_root, text=True,
stderr=subprocess.DEVNULL
+ ).strip()[:12]
+ except Exception:
+ sha = "?"
+
+ branch = current_branch(juneau_root)
+
+ stats = _collect_surefire_stats(juneau_root)
+ if stats is not None:
+ tests_run, failures, errors, skipped = stats
+ else:
+ tests_run = failures = errors = skipped = "?"
+
+ row = "\t".join(str(v) for v in [ts, sha, branch, tests_run, failures,
errors, skipped, wall_sec])
+
+ with tsv_path.open("a", encoding="utf-8") as f:
+ if need_header:
+ f.write(header + "\n")
+ f.write(row + "\n")
+
+ print(f"๐ Test metrics appended โ juneau-utest/test-run-history.tsv
({tests_run} tests, {wall_sec}s)")
+ except Exception as exc:
+ print(f"โ Warning: Could not append test metrics: {exc}")
+
+
def main(): # NOSONAR python:S3776 -- Cognitive complexity is acceptable for
this main function
parser = argparse.ArgumentParser(
description="Build, test, and push Juneau project to Git repository",
@@ -479,30 +534,37 @@ Examples:
if test_script.exists():
print(f"\n๐งช Step {step_num}: Running tests via test.py...")
try:
+ _test_start = time.time()
result = subprocess.run(
[sys.executable, str(test_script), "--full",
"--timing-log", str(timing_file)],
cwd=juneau_root,
check=False
)
+ _test_wall_sec = int(time.time() - _test_start)
if result.returncode != 0:
print("\nโ Build process aborted due to test failures.")
play_sound(success=False)
return 1
print(f"โ
Step {step_num}: Tests passed")
+ _append_test_run_history(juneau_root, _test_wall_sec)
except Exception as e:
print(f"\nโ Error running tests: {e}")
play_sound(success=False)
return 1
else:
# Fallback to direct mvn test if test.py doesn't exist
- if not run_command(
+ _test_start = time.time()
+ _mvn_ok = run_command(
["mvn", "test"],
f"๐งช Step {step_num}: Running tests...",
juneau_root
- ):
+ )
+ _test_wall_sec = int(time.time() - _test_start)
+ if not _mvn_ok:
print("\nโ Build process aborted due to test failures.")
play_sound(success=False)
return 1
+ _append_test_run_history(juneau_root, _test_wall_sec)
timing_report = script_dir / "push-timings.py"
if timing_report.exists():
run_command(