"""Phase 1 spec test for GQA figure renderers (sub-cycle 4c). ADR-0057 D3 sub-cycle 4c adds 6 figure renderers; this test pins the 5 of 6 that don't depend on sub-cycle 4b's Q/cube sweep: - 4 per-panel op_log_summary PNGs (one per panel of v1's sweep.json) - 1 cross-panel ``gqa_comparison.png`` (4-panel grouped bars over the 5 op_log_summary counts: gemm, ipcq_send, ipcq_recv, dma_read, dma_write) The 6th, ``gqa_scaling.png``, needs the Q/cube ∈ {1, 2, 4} sweep from sub-cycle 4b and is deferred. Each test depends on the committed ``benches/1H_milestone_output/gqa/sweep.json`` (landed in commit ``e748a62``); they assert the renderer writes a non-empty PNG at the expected path. Phase 1 expectation: tests fail at import (renderer functions don't exist yet on the bench module). Phase 2 lands them and the tests turn green. """ from __future__ import annotations from pathlib import Path import pytest from tests.gqa._gqa_plot_helpers import ( GQA_PLOTS_DIR, GQA_SWEEP_JSON, emit_all_gqa_plots, emit_gqa_comparison, emit_panel_op_log_summary, ) _PANELS = ( "single_user_prefill", "multi_user_prefill", "single_user_decode", "multi_user_decode", ) @pytest.mark.skipif( not GQA_SWEEP_JSON.exists(), reason="gqa sweep.json absent; run milestone-gqa-llama70b first", ) @pytest.mark.parametrize("panel", _PANELS) def test_emit_panel_op_log_summary_writes_png_for_each_panel(panel): out = emit_panel_op_log_summary(panel) assert out is not None, f"{panel}: renderer returned None" path = Path(out) assert path.exists(), f"{panel}: expected PNG at {path}" assert path.suffix == ".png", f"{panel}: not a PNG: {path}" assert path.stat().st_size > 0, f"{panel}: empty PNG: {path}" assert panel in path.stem, ( f"{panel}: panel name not in filename {path.name}" ) @pytest.mark.skipif( not GQA_SWEEP_JSON.exists(), reason="gqa sweep.json absent; run milestone-gqa-llama70b first", ) def test_emit_gqa_comparison_writes_png(): out = emit_gqa_comparison() assert out is not None path = Path(out) assert path.exists() assert path.name == "gqa_comparison.png" assert path.stat().st_size > 0 @pytest.mark.skipif( not GQA_SWEEP_JSON.exists(), reason="gqa sweep.json absent; run milestone-gqa-llama70b first", ) def test_emit_all_gqa_plots_writes_five_figures(): """emit_all returns a list of 5 written PNG paths (deferring the 6th gqa_scaling.png to after sub-cycle 4b lands the Q/cube sweep).""" paths = emit_all_gqa_plots() assert isinstance(paths, list) # 4 per-panel + 1 comparison. assert len(paths) == 5, f"expected 5 PNGs, got {len(paths)}: {paths}" for p in paths: assert Path(p).exists() and Path(p).stat().st_size > 0 names = {Path(p).name for p in paths} assert "gqa_comparison.png" in names for panel in _PANELS: assert any(panel in n for n in names), ( f"no per-panel PNG for {panel} in {names}" ) def test_emit_all_gqa_plots_output_dir_matches_bench_output_dir(): """The renderers must write under the bench's own _OUTPUT_DIR so MILESTONE_FAST=1 reuse (and committed baselines) all point at the same on-disk location.""" # Stub assertion that fails until emit_all_gqa_plots exists with a # default ``out_dir`` argument identical to GQA_PLOTS_DIR. import inspect sig = inspect.signature(emit_all_gqa_plots) assert "out_dir" in sig.parameters default = sig.parameters["out_dir"].default assert Path(default) == GQA_PLOTS_DIR, ( f"default out_dir {default} != bench _OUTPUT_DIR {GQA_PLOTS_DIR}" )