diff --git a/scripts/slots.py b/scripts/slots.py index eac475c6..1d506258 100644 --- a/scripts/slots.py +++ b/scripts/slots.py @@ -889,8 +889,8 @@ def main() -> int: parser.add_argument( "--strict", action="store_true", - help="exit non-zero on any contested destination, not only on one the " - "pack should have settled by itself", + help="exit 1 on any contested destination or destination declared twice; " + "without it the run reports them and exits 0", ) args = parser.parse_args() @@ -968,15 +968,19 @@ def main() -> int: fixable += decision.serves_both print(f" {format_decision(decision)}") total = sum(len(c) for c in found.values()) - print( - f"\n{total} contested destinations. {fixable} the pack settles on " - f"its own, {total - fixable} rest on an upstream declaration." - ) for platform, collisions in collided.items(): print(f"{platform}: {len(collisions)} destinations declared twice") for collision in collisions: print(f" {format_collision(collision)}") - if not args.strict and total: + # The totals close the run, collisions counted: printed before that + # section, they were not the last word and left it out. + doubled = sum(len(c) for c in collided.values()) + print( + f"\n{total} contested destinations. {fixable} the pack settles on " + f"its own, {total - fixable} rest on an upstream declaration. " + f"{doubled} destinations declared twice." + ) + if not args.strict and (total or doubled): print( "Reported, not failed: the remainder needs the upstream list " "corrected, which no build can do. Use --strict to gate on them." diff --git a/tests/test_slots.py b/tests/test_slots.py index 0313be89..d320be21 100644 --- a/tests/test_slots.py +++ b/tests/test_slots.py @@ -855,5 +855,32 @@ class ARecordIsFoundByItsPathOnce(unittest.TestCase): self.assertEqual(Counted.walks, 1) +class TheSlotsSummaryClosesTheRun(unittest.TestCase): + """The totals came before the collision section and ignored it, and the + help said the default run fails on conflicts the pack should settle, + which it never does.""" + + def test_order_and_help(self): + import subprocess # noqa: PLC0415 + + root = Path(__file__).resolve().parents[1] + if not (root / "database.json").exists(): + self.skipTest("database.json is not built") + proc = subprocess.run( + [sys.executable, "scripts/slots.py"], cwd=root, capture_output=True, + text=True, timeout=600, check=False, + ) + lines = [line for line in proc.stdout.splitlines() if line.strip()] + self.assertEqual(proc.returncode, 0) + totals = next(i for i, line in enumerate(lines) if "contested destinations." in line) + self.assertIn("declared twice.", lines[totals]) + self.assertFalse(any(line.endswith("destinations declared twice") for line in lines[totals:])) + helped = subprocess.run( + [sys.executable, "scripts/slots.py", "--help"], cwd=root, capture_output=True, + text=True, timeout=60, check=False, + ).stdout + self.assertIn("exits 0", " ".join(helped.split())) + + if __name__ == "__main__": unittest.main()