| """Rerun policy shared by the API and Shell test formats.""" |
| |
| import re |
| |
| import lit.Test |
| |
| # Failures that come from flaky infrastructure rather than from the test |
| # itself. Rerunning masks real bugs, so this is deliberately not a blanket |
| # retry of every failure. Adding an entry is a last resort, reserved for a |
| # defect outside LLDB's control that cannot be worked around any other way. |
| KNOWN_FLAKES = [ |
| # macOS 26.3 denies debugserver permission to attach when too many debug |
| # sessions start at once, which surfaces as an immediate process exit. |
| re.compile(r"process exited with status -1"), |
| ] |
| |
| MAX_ATTEMPTS = 3 |
| |
| |
| def _hit_known_flake(output): |
| return any(flake.search(output) for flake in KNOWN_FLAKES) |
| |
| |
| def execute_with_reruns(execute_once): |
| """Run execute_once, which returns a lit.Test.Result, until it stops |
| failing with a known flake.""" |
| for attempt in range(MAX_ATTEMPTS): |
| result = execute_once() |
| if result.code != lit.Test.FAIL or not _hit_known_flake(result.output): |
| break |
| |
| # A pass that needed a rerun is reported apart from a clean pass so that |
| # the flakiness stays visible. |
| if attempt > 0: |
| if result.code == lit.Test.PASS: |
| result.code = lit.Test.FLAKYPASS |
| result.attempts = attempt + 1 |
| result.max_allowed_attempts = MAX_ATTEMPTS |
| |
| return result |