moonchen commented on code in PR #13540:
URL: https://github.com/apache/trafficserver/pull/13540#discussion_r3898261964


##########
tests/autest-parallel.py.in:
##########
@@ -227,6 +227,37 @@ def strip_ansi(text: str) -> str:
     return ansi_escape.sub('', text)
 
 
+def parse_test_completion(line: str) -> Optional[Tuple[str, str]]:
+    """Extract a completed test name and status from an autest progress 
line."""
+    clean = strip_ansi(line).strip()
+    marker = 'Running Test '
+    marker_pos = clean.rfind(marker)
+
+    if marker_pos < 0:
+        return None
+
+    completion = clean[marker_pos + len(marker):]
+    match = re.match(r'([^:\s]+):.*\b(Passed|Failed|Skipped)\s*$', completion, 
re.IGNORECASE)

Review Comment:
   AuTest 1.10.6 can finish a test as Unknown, Skipped, Passed, Warning, 
Failed, or Exception, but this regex and parse_detached_test_status accept only 
three. When Warning, Exception, or Unknown is skipped, last_completion is not 
advanced, so that test runtime is added to the next recognized test and the 
corrupted duration is saved for future partitioning. Please recognize all 
terminal statuses here and update short_status accordingly.



##########
tests/autest-parallel.py.in:
##########
@@ -517,151 +548,150 @@ def run_worker(
     existing = env.get('PYTHONPATH', '')
     env['PYTHONPATH'] = ':'.join(pythonpath_dirs + ([existing] if existing 
else []))
 
-    if collect_timings:
-        # Run tests one at a time to collect accurate timing
-        all_output = []
-        total_tests = len(tests)
-        try:
-            for idx, test in enumerate(tests, 1):
-                test_name, duration, status, output = run_single_test(
-                    test, script_dir, sandbox, ats_bin, build_root, 
extra_args, env)
-                result.test_timings[test_name] = duration
-                all_output.append(output)
-
-                if status == "PASS":
-                    result.passed += 1
-                elif status == "SKIP":
-                    result.skipped += 1
-                else:
-                    result.failed += 1
-                    result.failed_tests.append(test_name)
+    # Keep all tests in one autest process so its shared port queue is not
+    # reset between tests. This is both faster and avoids reusing ports that a
+    # recently completed test may still hold.
+    cmd = [
+        'uv',
+        'run',
+        'autest',
+        'run',
+        '--directory',
+        '${CMAKE_GOLD_DIR}',
+        '--ats-bin',
+        ats_bin,
+        '--proxy-verifier-bin',
+        '${PROXY_VERIFIER_PATH}',
+        '--build-root',
+        build_root,
+        '--sandbox',
+        str(sandbox),
+        '--filters',
+    ]
+    cmd.extend(f'/{test}' for test in tests)
+    cmd.extend(extra_args)
 
-                timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
-                # Fixed-width format: date time status duration worker 
progress test_name
-                print(
-                    f"{timestamp} {status:4s} {duration:6.1f}s 
Worker:{worker_id:2d} {idx:2d}/{total_tests:2d} {test}", flush=True)
-        except KeyboardInterrupt:
-            result.return_code = 130
+    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+    print(f"{timestamp} Worker:{worker_id:2d} Starting batch of {len(tests)} 
tests (port offset {port_offset})", flush=True)
+    if verbose:
+        print(
+            f"             Worker:{worker_id:2d} Tests: {', '.join(tests[:5])}"
+            f"{'...' if len(tests) > 5 else ''}",
+            flush=True)
 
-        result.output = "\n".join(all_output)
-        if result.return_code != 130:
-            result.return_code = 0 if result.failed == 0 else 1
-    else:
-        # Run all tests in batch (faster but no per-test timing)
-        cmd = [
-            'uv',
-            'run',
-            'autest',
-            'run',
-            '--directory',
-            '${CMAKE_GOLD_DIR}',
-            '--ats-bin',
-            ats_bin,
-            '--proxy-verifier-bin',
-            '${PROXY_VERIFIER_PATH}',
-            '--build-root',
-            build_root,
-            '--sandbox',
-            str(sandbox),
-        ]
-
-        # Add test filters
-        cmd.append('--filters')
-        cmd.extend(tests)
-
-        # Add any extra arguments
-        cmd.extend(extra_args)
-
-        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
-        print(f"{timestamp} Worker:{worker_id:2d} Starting batch of 
{len(tests)} tests (port offset {port_offset})", flush=True)
-        if verbose:
-            print(
-                f"             Worker:{worker_id:2d} Tests: {', 
'.join(tests[:5])}"
-                f"{'...' if len(tests) > 5 else ''}",
-                flush=True)
+    try:
+        if verbose or collect_timings:
+            # Autest flushes each completed test line. Observe those lines to
+            # collect timings without changing how autest executes the batch.
+            proc = subprocess.Popen(
+                cmd,
+                cwd=script_dir,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+                text=True,
+                env=env,
+            )
+            output_lines = []
+            last_completion = time.monotonic()
+            completed_count = 0
+            pending_test = None
+            try:
+                for line in proc.stdout:

Review Comment:
   The timeout below only starts after stdout reaches EOF. If a test wedges 
while keeping the pipe open, this blocking iteration never finishes; 
collect-timings has therefore lost the previous 600-second per-test bound, and 
as_completed waits forever as well. Please enforce a wall-clock timeout while 
consuming stdout and terminate the child when it expires.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to