diff --git a/clients/deadline.py b/clients/deadline.py index 7212104..dac5a08 100644 --- a/clients/deadline.py +++ b/clients/deadline.py @@ -25,6 +25,10 @@ def __init__(self, clock: MonotonicClock, timeout: float): self._timeout = float(timeout) self._started_at = clock.monotonic() + def elapsed(self) -> float: + """Return seconds since start; uncapped, may exceed timeout.""" + return self._clock.monotonic() - self._started_at + def remaining(self) -> float: """Return seconds left before the deadline (never negative).""" elapsed = self._clock.monotonic() - self._started_at diff --git a/scenarios/cli.py b/scenarios/cli.py index 1925c56..97b2093 100644 --- a/scenarios/cli.py +++ b/scenarios/cli.py @@ -60,7 +60,15 @@ def build_parser() -> argparse.ArgumentParser: subparsers.add_parser("version", help="Print package version") - subparsers.add_parser("ids", help="Print known scenario ids") + ids_parser = subparsers.add_parser("ids", help="Print known scenario ids") + ids_parser.add_argument( + "--format", + choices=("text", "json"), + default="text", + help="Output format (default: text)", + ) + + subparsers.add_parser("count", help="Print number of known scenarios") return parser @@ -153,8 +161,16 @@ def main(argv: list[str] | None = None) -> int: return 0 if args.command == "ids": - for scenario_id in known_scenario_ids(): - print(scenario_id) + ids = list(known_scenario_ids()) + if args.format == "json": + print(json.dumps(ids, indent=2)) + else: + for scenario_id in ids: + print(scenario_id) + return 0 + + if args.command == "count": + print(len(known_scenario_ids())) return 0 return 1 diff --git a/tests/test_cli.py b/tests/test_cli.py index d3381ec..b16c784 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -199,7 +199,28 @@ def test_ids_prints_one_id_per_line(self): lines = [line for line in buffer.getvalue().splitlines() if line.strip()] self.assertEqual(lines, list(known_scenario_ids())) + def test_count_prints_known_scenario_count(self): + buffer = io.StringIO() + with redirect_stdout(buffer): + code = main(["count"]) + self.assertEqual(code, 0) + self.assertEqual(buffer.getvalue().strip(), str(len(known_scenario_ids()))) + def test_ids_json_format(self): + buffer = io.StringIO() + with redirect_stdout(buffer): + code = main(["ids", "--format", "json"]) + self.assertEqual(code, 0) + parsed = json.loads(buffer.getvalue()) + self.assertEqual(parsed, list(known_scenario_ids())) + + def test_ids_text_format_default_unchanged(self): + buffer = io.StringIO() + with redirect_stdout(buffer): + code = main(["ids", "--format", "text"]) + self.assertEqual(code, 0) + lines = [line for line in buffer.getvalue().splitlines() if line.strip()] + self.assertEqual(lines, list(known_scenario_ids())) if __name__ == "__main__": diff --git a/tests/test_deadline.py b/tests/test_deadline.py index ec9dde2..572aebf 100644 --- a/tests/test_deadline.py +++ b/tests/test_deadline.py @@ -41,6 +41,16 @@ def test_negative_timeout_rejected(self): with self.assertRaises(ValueError): Deadline(clock, timeout=-1.0) + def test_elapsed_tracks_fake_clock_uncapped(self): + clock = FakeClock() + deadline = Deadline(clock, timeout=0.5) + self.assertAlmostEqual(deadline.elapsed(), 0.0) + clock.sleep(0.4) + self.assertAlmostEqual(deadline.elapsed(), 0.4) + clock.sleep(0.4) + self.assertAlmostEqual(deadline.elapsed(), 0.8) + self.assertGreater(deadline.elapsed(), 0.5) + if __name__ == "__main__": unittest.main()