-
Notifications
You must be signed in to change notification settings - Fork 0
feat(browser): retain MV3 failure profile cleanup evidence #141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: test/agent-task-failure-cleanup-evidence
Are you sure you want to change the base?
Changes from all commits
7df0058
2301459
42af52f
60183b8
c5a91bc
acfc1c8
493279e
e15a436
bc9b265
645a710
4fc623e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -723,26 +723,55 @@ def _run_restart_trial( | |
| fixture_url: str, | ||
| trial_number: int, | ||
| ) -> dict[str, Any]: | ||
| """Run one independent initial/restart pair and return credential-free evidence.""" | ||
| """Run one independent initial/restart pair and retain cleanup evidence on failure.""" | ||
|
|
||
| trial_started = time.monotonic() | ||
| profile_path: pathlib.Path | ||
| initial: dict[str, Any] | None = None | ||
| restarted: dict[str, Any] | None = None | ||
| failure_type: str | None = None | ||
| with tempfile.TemporaryDirectory( | ||
| prefix=f"originweave-mv3-trial-{trial_number}-" | ||
| ) as profile_dir: | ||
| initial = _run_browser_pass( | ||
| chrome_bin, | ||
| chromedriver_bin, | ||
| fixture_url, | ||
| profile_dir, | ||
| "initialized", | ||
| ) | ||
| restarted = _run_browser_pass( | ||
| chrome_bin, | ||
| chromedriver_bin, | ||
| fixture_url, | ||
| profile_dir, | ||
| "persisted", | ||
| ) | ||
| profile_path = pathlib.Path(profile_dir) | ||
| try: | ||
| initial = _run_browser_pass( | ||
| chrome_bin, | ||
| chromedriver_bin, | ||
| fixture_url, | ||
| profile_dir, | ||
| "initialized", | ||
| ) | ||
| restarted = _run_browser_pass( | ||
| chrome_bin, | ||
| chromedriver_bin, | ||
| fixture_url, | ||
| profile_dir, | ||
| "persisted", | ||
| ) | ||
| except ( | ||
| OSError, | ||
| ValueError, | ||
| RuntimeError, | ||
| json.JSONDecodeError, | ||
| subprocess.TimeoutExpired, | ||
| ) as exc: | ||
| failure_type = type(exc).__name__ | ||
|
Comment on lines
+752
to
+759
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Unexpected exceptions skip cleanup verification The inner except catches only OSError, ValueError, RuntimeError, json.JSONDecodeError, and subprocess.TimeoutExpired. Other classes propagate past the Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| profile_cleaned = not profile_path.exists() | ||
| if not profile_cleaned: | ||
| raise RuntimeError(f"Manifest V3 profile cleanup failed in trial {trial_number}") | ||
|
Comment on lines
+760
to
+762
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Cleanup check largely tautological
Was this helpful? React with 👍 or 👎 to provide feedback. |
||
|
|
||
| duration_ms = round((time.monotonic() - trial_started) * 1000) | ||
| if failure_type is not None: | ||
| return { | ||
| "trial_number": trial_number, | ||
| "passed": False, | ||
| "failure_type": failure_type, | ||
| "profile_cleaned": True, | ||
| "duration_ms": duration_ms, | ||
| } | ||
| if initial is None or restarted is None: | ||
| raise RuntimeError("Manifest V3 restart trial returned incomplete browser evidence") | ||
|
|
||
| initial_count = int(initial["worker_start_count"]) | ||
| restarted_count = int(restarted["worker_start_count"]) | ||
|
|
@@ -758,7 +787,14 @@ def _run_restart_trial( | |
| } | ||
| ) | ||
| if not all(surfaces.values()): | ||
| raise RuntimeError(f"compatibility surface failed in trial {trial_number}") | ||
| return { | ||
| "trial_number": trial_number, | ||
| "passed": False, | ||
| "failure_type": "CompatibilitySurfaceFailure", | ||
| "surfaces": surfaces, | ||
| "profile_cleaned": True, | ||
| "duration_ms": duration_ms, | ||
| } | ||
|
|
||
| return { | ||
| "trial_number": trial_number, | ||
|
|
@@ -777,7 +813,8 @@ def _run_restart_trial( | |
| "storage_persistence": restarted["storage_persistence"], | ||
| }, | ||
| ], | ||
| "duration_ms": round((time.monotonic() - trial_started) * 1000), | ||
| "profile_cleaned": True, | ||
| "duration_ms": duration_ms, | ||
| } | ||
|
|
||
|
|
||
|
|
@@ -1424,6 +1461,9 @@ def main() -> int: | |
| 1 for trial in trial_results if trial.get("passed") is True | ||
| ) | ||
| trial_pass_rate = successful_trials / REPEATABILITY_TRIALS | ||
| mv3_profiles_cleaned = all( | ||
| trial.get("profile_cleaned") is True for trial in trial_results | ||
| ) | ||
|
seonghobae marked this conversation as resolved.
|
||
| successful_results = [ | ||
| trial for trial in trial_results if trial.get("passed") is True | ||
| ] | ||
|
|
@@ -1552,6 +1592,7 @@ def main() -> int: | |
| "repeatability_trials": REPEATABILITY_TRIALS, | ||
| "successful_trials": successful_trials, | ||
| "trial_pass_rate": trial_pass_rate, | ||
| "profiles_cleaned": mv3_profiles_cleaned, | ||
| "surfaces": common_surfaces, | ||
| "trial_results": trial_results, | ||
| "browser_passes": ( | ||
|
|
@@ -1576,6 +1617,8 @@ def main() -> int: | |
| "duration_ms": round((time.monotonic() - started) * 1000), | ||
| } | ||
| print(json.dumps(evidence, sort_keys=True)) | ||
| if not mv3_profiles_cleaned: | ||
| raise RuntimeError("Manifest V3 profile cleanup gate failed") | ||
| if successful_trials != REPEATABILITY_TRIALS: | ||
| raise RuntimeError( | ||
| "Manifest V3 repeatability gate failed: " | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| """Contract for MV3 restart-trial profile cleanup evidence on browser failure.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import pathlib | ||
| import runpy | ||
| import unittest | ||
|
|
||
| ROOT = pathlib.Path(__file__).resolve().parents[1] | ||
| RUNNER = ROOT / "scripts" / "ci" / "run_mv3_compatibility.py" | ||
|
|
||
|
|
||
| class Mv3FailureProfileCleanupContractTests(unittest.TestCase): | ||
| """Require failed extension-compatibility trials to retain teardown evidence.""" | ||
|
|
||
| def _namespace(self, name: str) -> dict[str, object]: | ||
| return runpy.run_path(str(RUNNER), run_name=name) | ||
|
|
||
| def test_failed_restart_pass_returns_profile_cleanup_evidence(self) -> None: | ||
| """A failed initial or restarted browser pass must retain profile cleanup proof.""" | ||
|
|
||
| namespace = self._namespace("mv3_failure_cleanup_behavior") | ||
| run_trial = namespace["_run_restart_trial"] | ||
|
|
||
| def fail_browser_pass(*_args: object, **_kwargs: object) -> dict[str, object]: | ||
| raise RuntimeError("synthetic MV3 browser failure") | ||
|
|
||
| run_trial.__globals__["_run_browser_pass"] = fail_browser_pass | ||
| result = run_trial( | ||
| pathlib.Path("controlled-chrome"), | ||
| pathlib.Path("controlled-chromedriver"), | ||
| "http://127.0.0.1/controlled-fixture", | ||
| 11, | ||
| ) | ||
|
|
||
| self.assertEqual(result["trial_number"], 11) | ||
| self.assertIs(result["passed"], False) | ||
| self.assertEqual(result["failure_type"], "RuntimeError") | ||
| self.assertIs(result["profile_cleaned"], True) | ||
| self.assertNotIn("synthetic MV3 browser failure", repr(result)) | ||
|
|
||
| def test_surface_failure_preserves_cleanup_and_surface_evidence(self) -> None: | ||
| """A compatibility regression must not be mislabeled as profile cleanup failure.""" | ||
|
|
||
| namespace = self._namespace("mv3_surface_failure_cleanup_behavior") | ||
| run_trial = namespace["_run_restart_trial"] | ||
| browser_pass_number = 0 | ||
|
|
||
| def browser_pass(*_args: object, **_kwargs: object) -> dict[str, object]: | ||
| nonlocal browser_pass_number | ||
| browser_pass_number += 1 | ||
| persisted = browser_pass_number == 2 | ||
| return { | ||
| "browser_version": "controlled-browser", | ||
| "worker_start_count": browser_pass_number, | ||
| "storage_persistence": "persisted" if persisted else "initialized", | ||
| "surfaces": { | ||
| "service-worker": True, | ||
| "real-browser-click": not persisted, | ||
| }, | ||
| } | ||
|
|
||
| run_trial.__globals__["_run_browser_pass"] = browser_pass | ||
| result = run_trial( | ||
| pathlib.Path("controlled-chrome"), | ||
| pathlib.Path("controlled-chromedriver"), | ||
| "http://127.0.0.1/controlled-fixture", | ||
| 12, | ||
| ) | ||
|
|
||
| self.assertEqual(result["trial_number"], 12) | ||
| self.assertIs(result["passed"], False) | ||
| self.assertEqual(result["failure_type"], "CompatibilitySurfaceFailure") | ||
| self.assertIs(result["profile_cleaned"], True) | ||
| self.assertIs(result["surfaces"]["real-browser-click"], False) | ||
|
|
||
| def test_acceptance_gate_requires_cleanup_evidence_for_every_mv3_trial(self) -> None: | ||
| """Failed MV3 trials must not be filtered out of the profile-cleanup gate.""" | ||
|
|
||
| runner = RUNNER.read_text(encoding="utf-8") | ||
| self.assertIn("mv3_profiles_cleaned = all(", runner) | ||
| self.assertIn('trial.get("profile_cleaned") is True', runner) | ||
| self.assertIn('"profiles_cleaned": mv3_profiles_cleaned', runner) | ||
| self.assertIn("Manifest V3 profile cleanup gate failed", runner) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| """Regression contract for MV3 restart teardown timeout cleanup evidence.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import pathlib | ||
| import runpy | ||
| import subprocess | ||
| import unittest | ||
|
|
||
| ROOT = pathlib.Path(__file__).resolve().parents[1] | ||
| RUNNER = ROOT / "scripts" / "ci" / "run_mv3_compatibility.py" | ||
|
|
||
|
|
||
| class Mv3RestartTimeoutCleanupContractTests(unittest.TestCase): | ||
| """Require reviewed ChromeDriver teardown timeouts to retain MV3 cleanup proof.""" | ||
|
|
||
| def test_teardown_timeout_returns_profile_cleanup_evidence(self) -> None: | ||
| """A bounded process teardown timeout must become failed-trial evidence.""" | ||
|
|
||
| namespace = runpy.run_path(str(RUNNER), run_name="mv3_restart_timeout_cleanup") | ||
| run_trial = namespace["_run_restart_trial"] | ||
|
|
||
| def timeout_browser_pass(*_args: object, **_kwargs: object) -> dict[str, object]: | ||
| raise subprocess.TimeoutExpired( | ||
| cmd="private-controlled-chromedriver-path", | ||
| timeout=5, | ||
| ) | ||
|
|
||
| run_trial.__globals__["_run_browser_pass"] = timeout_browser_pass | ||
| result = run_trial( | ||
| pathlib.Path("controlled-chrome"), | ||
| pathlib.Path("controlled-chromedriver"), | ||
| "http://127.0.0.1/controlled-fixture", | ||
| 12, | ||
| ) | ||
|
|
||
| self.assertEqual(result["trial_number"], 12) | ||
| self.assertIs(result["passed"], False) | ||
| self.assertEqual(result["failure_type"], "TimeoutExpired") | ||
| self.assertIs(result["profile_cleaned"], True) | ||
| self.assertNotIn("private-controlled-chromedriver-path", repr(result)) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
Uh oh!
There was an error while loading. Please reload this page.