"""ASV benchmarks for the output rendering framework. Measures materialiser throughput, progress handle overhead, and session lifecycle costs across all six output formats. """ from __future__ import annotations import importlib import sys from pathlib import Path # Ensure the local *source* tree is importable even when ASV has an # older build of the package installed. _SRC = str(Path(__file__).resolve().parents[1] / "src") if _SRC not in sys.path: sys.path.insert(0, _SRC) import cleveragents # noqa: E402 importlib.reload(cleveragents) from cleveragents.cli.output.handles import DiffLine # noqa: E402 from cleveragents.cli.output.materializers import ( # noqa: E402 ColorMaterializer, JsonMaterializer, PlainMaterializer, RichMaterializer, TableMaterializer, YamlMaterializer, ) from cleveragents.cli.output.session import OutputSession # noqa: E402 _STRATEGY_MAP: dict[str, type] = { "rich": RichMaterializer, "color": ColorMaterializer, "table": TableMaterializer, "plain": PlainMaterializer, "json": JsonMaterializer, "yaml": YamlMaterializer, } class MaterializerThroughputSuite: """Benchmark materialiser throughput for panel + table rendering.""" params: list[str] = ["rich", "color", "table", "plain", "json", "yaml"] # noqa: RUF012 param_names: list[str] = ["format"] # noqa: RUF012 def time_panel_render(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: panel = session.panel("Benchmark Panel") for i in range(50): panel.set_entry(f"key-{i}", f"value-{i}") panel.close() strategy.get_output() def time_table_render(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: table = session.table(columns=["Name", "Status", "Count"]) for i in range(100): table.add_row( {"Name": f"item-{i}", "Status": "active", "Count": str(i)} ) table.close() strategy.get_output() class ProgressHandleSuite: """Benchmark progress handle update overhead.""" params: list[str] = ["plain", "json"] # noqa: RUF012 param_names: list[str] = ["format"] # noqa: RUF012 def time_progress_ticks(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: prog = session.progress("Ticking", total=1000) for _ in range(1000): prog.tick() prog.close() def time_progress_set(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: prog = session.progress("Stepping", total=500) for i in range(500): prog.set_progress(i, 500) prog.close() class SessionLifecycleSuite: """Benchmark session open/close overhead.""" params: list[str] = ["plain", "json"] # noqa: RUF012 param_names: list[str] = ["format"] # noqa: RUF012 def time_empty_session(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy): pass def time_session_with_handles(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: p = session.panel("P") p.set_entry("k", "v") p.close() t = session.table(columns=["A"]) t.add_row({"A": "1"}) t.close() s = session.status("ok") s.close() class TreeThroughputSuite: """Benchmark tree-building throughput with deep/wide trees.""" params: list[str] = ["plain", "json"] # noqa: RUF012 param_names: list[str] = ["format"] # noqa: RUF012 def time_wide_tree(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: tree = session.tree("Root") for i in range(100): tree.add_child("Root", f"child-{i}") tree.close() strategy.get_output() def time_deep_tree(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: tree = session.tree("Root") path = "Root" for i in range(50): label = f"level-{i}" path = tree.add_child(path, label) tree.close() strategy.get_output() class DiffThroughputSuite: """Benchmark diff rendering with multi-hunk diffs.""" params: list[str] = ["plain", "color", "json"] # noqa: RUF012 param_names: list[str] = ["format"] # noqa: RUF012 def time_multi_hunk_diff(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: diff = session.diff(file_a="old.py", file_b="new.py") for h in range(20): lines = [] for j in range(10): lines.append(DiffLine(type="context", content=f"ctx-{j}")) lines.append(DiffLine(type="add", content=f"add-{j}")) lines.append(DiffLine(type="remove", content=f"rem-{j}")) diff.add_hunk(f"@@ -{h * 30},{30} +{h * 30},{30} @@", lines) diff.set_stats(200, 200) diff.close() strategy.get_output() class MixedElementSuite: """Benchmark session throughput with all 10 element types.""" params: list[str] = ["plain", "json"] # noqa: RUF012 param_names: list[str] = ["format"] # noqa: RUF012 def time_all_element_types(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: p = session.panel("P") p.set_entry("k", "v") p.close() t = session.table(columns=["A"]) t.add_row({"A": "1"}) t.close() s = session.status("ok") s.close() pr = session.progress("go", total=10) pr.set_progress(5) pr.close() tr = session.tree("Root") tr.add_child("Root", "leaf") tr.close() tx = session.text("hello world") tx.close() co = session.code("x = 1", language="python") co.close() d = session.diff(file_a="a", file_b="b") d.add_hunk("@@", [DiffLine(type="add", content="new")]) d.close() session.separator("line") session.action_hint(["cmd"]) strategy.get_output() class TextAppendSuite: """Benchmark TextHandle.append() throughput (N10: detect O(N²) regressions).""" params: list[str] = ["plain", "json"] # noqa: RUF012 param_names: list[str] = ["format"] # noqa: RUF012 def time_text_append_1000(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: tx = session.text("") for i in range(1000): tx.append(f"Line {i}\n") tx.close() strategy.get_output() def time_text_append_5000(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: tx = session.text("") for i in range(5000): tx.append(f"Line {i}\n") tx.close() strategy.get_output() class LargeTableSuite: """Benchmark table rendering at 10K+ rows (N10).""" params: list[str] = ["plain", "json"] # noqa: RUF012 param_names: list[str] = ["format"] # noqa: RUF012 def time_table_10k_rows(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: table = session.table(columns=["ID", "Name", "Value"]) for i in range(10_000): table.add_row({"ID": str(i), "Name": f"item-{i}", "Value": str(i * 10)}) table.close() strategy.get_output() class SnapshotCostSuite: """Benchmark snapshot() cost (N10).""" params: list[str] = ["plain", "json"] # noqa: RUF012 param_names: list[str] = ["format"] # noqa: RUF012 def time_snapshot_with_many_elements(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: for i in range(50): p = session.panel(f"Panel-{i}") p.set_entry("k", "v") p.close() for _ in range(20): session.snapshot() def time_snapshot_with_large_table(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: table = session.table(columns=["A", "B"]) for i in range(5000): table.add_row({"A": str(i), "B": str(i)}) table.close() for _ in range(10): session.snapshot() class DeepTreeLookupSuite: """Benchmark deep tree path lookups (N10).""" params: list[str] = ["plain", "json"] # noqa: RUF012 param_names: list[str] = ["format"] # noqa: RUF012 def time_deep_tree_add_and_lookup(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: tree = session.tree("Root") path = "Root" for i in range(50): label = f"level-{i}" path = tree.add_child(path, label) # Now do lookups at various depths tree.add_child("Root", "wide-child-1") tree.add_child("Root", "wide-child-2") tree.close() strategy.get_output() class ElementUpdatedOverheadSuite: """Benchmark ElementUpdated event dispatch overhead (N10).""" params: list[str] = ["plain", "json"] # noqa: RUF012 param_names: list[str] = ["format"] # noqa: RUF012 def time_panel_many_updates(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: panel = session.panel("Update Panel") for i in range(500): panel.set_entry(f"key-{i % 50}", f"value-{i}") panel.close() def time_progress_many_ticks(self, fmt: str) -> None: strategy = _STRATEGY_MAP[fmt]() with OutputSession(format=fmt, command="bench", strategy=strategy) as session: prog = session.progress("Ticking", total=5000) for _ in range(5000): prog.tick() prog.close()