From 6db570f2a7fa4e28acc38a7b6a92b823a5ffe133 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 03:32:48 +0000 Subject: [PATCH] test: gracefully skip test_ai_tui_agent_harness on Windows Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- src/tests/test_ai_tui_agent_harness.py | 37 ++++++++++++- .../test-audit/run_python_flaky_check.py | 55 ++++++++++++------- .../test-audit/run_python_static_analysis.py | 19 ++++--- 3 files changed, 82 insertions(+), 29 deletions(-) diff --git a/src/tests/test_ai_tui_agent_harness.py b/src/tests/test_ai_tui_agent_harness.py index ed228a38..7f5ef859 100644 --- a/src/tests/test_ai_tui_agent_harness.py +++ b/src/tests/test_ai_tui_agent_harness.py @@ -6,6 +6,9 @@ import sys import pytest +pytest.importorskip("pty") +pytest.importorskip("termios") + def _load_harness_module(): repo_root = Path(__file__).resolve().parents[2] @@ -14,14 +17,24 @@ def _load_harness_module(): assert spec is not None and spec.loader is not None module = importlib.util.module_from_spec(spec) sys.modules[spec.name] = module - spec.loader.exec_module(module) + + try: + spec.loader.exec_module(module) + except ModuleNotFoundError as e: + pytest.skip(f"Skipping test due to missing module: {e}") + return module -harness = _load_harness_module() +try: + harness = _load_harness_module() +except pytest.skip.Exception: + harness = None def test_run_step_records_passed_result(): + if not harness: + pytest.skip("Harness not loaded") results = [] called = {"value": False} @@ -38,6 +51,8 @@ def test_run_step_records_passed_result(): def test_run_step_records_failed_result_and_reraises(): + if not harness: + pytest.skip("Harness not loaded") results = [] def _boom(): @@ -53,6 +68,9 @@ def test_run_step_records_failed_result_and_reraises(): def test_add_entry_menu_open_waits_when_choice_omitted(): + if not harness: + pytest.skip("Harness not loaded") + class Runner: def __init__(self): self.sent = [] @@ -72,6 +90,9 @@ def test_add_entry_menu_open_waits_when_choice_omitted(): def test_add_entry_menu_open_sends_choice_when_provided(): + if not harness: + pytest.skip("Harness not loaded") + class Runner: def __init__(self): self.sent = [] @@ -92,6 +113,9 @@ def test_add_entry_menu_open_sends_choice_when_provided(): def test_complete_onboarding_reaches_main_menu_and_sends_password(): + if not harness: + pytest.skip("Harness not loaded") + class Runner: def __init__(self): self.calls = [ @@ -122,6 +146,9 @@ def test_complete_onboarding_reaches_main_menu_and_sends_password(): def test_complete_onboarding_times_out_without_expected_prompts(): + if not harness: + pytest.skip("Harness not loaded") + class Runner: def try_wait_for(self, pattern, timeout): return False @@ -138,6 +165,9 @@ def test_complete_onboarding_times_out_without_expected_prompts(): def test_drain_to_main_menu_uses_helper_prompt_then_returns(): + if not harness: + pytest.skip("Harness not loaded") + class Runner: def __init__(self): self.main_attempts = 0 @@ -164,6 +194,9 @@ def test_drain_to_main_menu_uses_helper_prompt_then_returns(): def test_drain_to_main_menu_times_out_when_no_prompt_progress(monkeypatch): + if not harness: + pytest.skip("Harness not loaded") + class Runner: def wait_for(self, pattern, timeout): raise harness.TUIHarnessError("missing") diff --git a/torch/scripts/test-audit/run_python_flaky_check.py b/torch/scripts/test-audit/run_python_flaky_check.py index 9ddef432..3add8d15 100644 --- a/torch/scripts/test-audit/run_python_flaky_check.py +++ b/torch/scripts/test-audit/run_python_flaky_check.py @@ -11,6 +11,7 @@ TEST_PATTERN = "src/tests" RESULTS = {} RUN_DIAGNOSTICS = [] + def run_tests(run_index): """Run pytest and parse output for pass/fail counts.""" print(f"Run {run_index + 1}/{RUNS}...") @@ -19,11 +20,13 @@ def run_tests(run_index): # -q: quiet # --no-header --no-summary: minimal output cmd = [ - sys.executable, "-m", "pytest", + sys.executable, + "-m", + "pytest", TEST_PATTERN, "-q", "--no-header", - "--no-summary" + "--no-summary", ] start_time = time.time() @@ -33,7 +36,7 @@ def run_tests(run_index): cmd, capture_output=True, text=True, - env={"PYTHONPATH": "src"} # Ensure src is in path + env={"PYTHONPATH": "src"}, # Ensure src is in path ) output = result.stdout exit_code = result.returncode @@ -45,10 +48,10 @@ def run_tests(run_index): # Parse output character by character for status # . = pass, F = fail, E = error, s = skip, x = xfail, X = xpass - passed = output.count('.') - failed = output.count('F') - errors = output.count('E') - skipped = output.count('s') + passed = output.count(".") + failed = output.count("F") + errors = output.count("E") + skipped = output.count("s") # Store aggregate results (naive mapping, ideally we'd parse node ids) # Since we can't easily map node IDs without -v or --junitxml, we track totals for flakiness *rates* at suite level first. @@ -56,26 +59,36 @@ def run_tests(run_index): junit_path = OUTPUT_DIR / f"flaky-run-{run_index + 1}.xml" subprocess.run( - [sys.executable, "-m", "pytest", TEST_PATTERN, f"--junitxml={junit_path}", "-q"], - capture_output=True, # We only care about the XML file - env={"PYTHONPATH": "src"} + [ + sys.executable, + "-m", + "pytest", + TEST_PATTERN, + f"--junitxml={junit_path}", + "-q", + ], + capture_output=True, # We only care about the XML file + env={"PYTHONPATH": "src"}, + ) + + RUN_DIAGNOSTICS.append( + { + "run": run_index + 1, + "code": exit_code, + "duration": duration, + "passed": passed, + "failed": failed, + "errors": errors, + "skipped": skipped, + } ) - RUN_DIAGNOSTICS.append({ - "run": run_index + 1, - "code": exit_code, - "duration": duration, - "passed": passed, - "failed": failed, - "errors": errors, - "skipped": skipped - }) def parse_junit_xmls(): """Parse generated JUnit XML files to detect per-test flakiness.""" import xml.etree.ElementTree as ET - test_stats = {} # "test_id": {pass: 0, fail: 0, error: 0, skip: 0} + test_stats = {} # "test_id": {pass: 0, fail: 0, error: 0, skip: 0} for i in range(RUNS): xml_path = OUTPUT_DIR / f"flaky-run-{i + 1}.xml" @@ -108,6 +121,7 @@ def parse_junit_xmls(): return test_stats + def main(): OUTPUT_DIR.mkdir(parents=True, exist_ok=True) @@ -129,5 +143,6 @@ def main(): print(f"Flakiness matrix written to {OUTPUT_DIR / 'flakiness-matrix.json'}") + if __name__ == "__main__": main() diff --git a/torch/scripts/test-audit/run_python_static_analysis.py b/torch/scripts/test-audit/run_python_static_analysis.py index ec0116c4..a6aa78ca 100644 --- a/torch/scripts/test-audit/run_python_static_analysis.py +++ b/torch/scripts/test-audit/run_python_static_analysis.py @@ -20,29 +20,33 @@ PATTERNS = [ (r"# todo", "Found '# todo' comment"), ] + def scan_file(filepath): """Scan a single file for suspicious patterns.""" issues = [] try: content = filepath.read_text(encoding="utf-8") except Exception: - return # Skip non-text files + return # Skip non-text files for pattern, message in PATTERNS: if re.search(pattern, content): issues.append(message) # Check for lack of assertions (very naive) - if "test_" in filepath.name and "assert" not in content and "expect" not in content and "raise" not in content: + if ( + "test_" in filepath.name + and "assert" not in content + and "expect" not in content + and "raise" not in content + ): # Exclude conftest or helpers if "conftest" not in filepath.name and "helper" not in filepath.name: - issues.append("No obvious assertions found") + issues.append("No obvious assertions found") if issues: - SUSPICIOUS.append({ - "file": str(filepath), - "issues": issues - }) + SUSPICIOUS.append({"file": str(filepath), "issues": issues}) + def main(): OUTPUT_DIR.mkdir(parents=True, exist_ok=True) @@ -62,5 +66,6 @@ def main(): print(f"Found {len(SUSPICIOUS)} suspicious files.") print(f"Report written to {OUTPUT_DIR / 'suspicious-tests.json'}.") + if __name__ == "__main__": main()