Source: tests/test_opportunity_analysis.py · Plain text

tests/test_opportunity_analysis.py

import csv
import itertools
import json

import pytest

from mcss.vision_probe.opportunity_analysis import analyze, opportunity_summary


def synthetic():
    rows = []
    for split in ("discovery", "validation"):
        for sequence in ("one", "two"):
            for slot in (1, 2):
                for path in itertools.product(("OFF", "A", "B", "ALL"), repeat=2):
                    gain = (0.02 if slot == 1 else 0.08) if path == ("OFF", "A") else 0.0
                    selected = ["best_fixed", "visible_selector"] if path == ("OFF", "A") else []
                    if path == ("OFF", "OFF"):
                        selected.append("rgb_selector")
                    rows.append(
                        {
                            "resolution": 448,
                            "split": split,
                            "sequence": sequence,
                            "target_slot": slot,
                            "trajectory": list(path),
                            "J": 0.4 + gain,
                            "F": 0.3,
                            "JF": 0.35 + gain / 2,
                            "token_iou": 0.2,
                            "visible": {"rgb_decrease": gain},
                            "selected_methods": selected,
                            "objects": [
                                {
                                    "object_id": 1,
                                    "J": 0.4 + gain,
                                    "F": 0.3,
                                    "JF": 0.35 + gain / 2,
                                    "size_bucket": "small",
                                }
                            ],
                        }
                    )
    return rows


def test_every_target_and_primary_j():
    report = opportunity_summary(synthetic())["448/validation"]
    stat = report["oracle_minus_off"]
    assert stat["sequence_bootstrap"]["mean"] == pytest.approx(0.05)
    assert stat["improved_pairs"] == 4
    assert stat["all_loso_ci_lower_positive"] is True
    assert set(stat["leave_one_sequence_out_ci"]) == {"one", "two"}
    for result in stat["leave_one_sequence_out_ci"].values():
        assert result["ci95"] == pytest.approx([0.05, 0.05])
        assert result["positive_sequence_count"] == 1
        assert result["top1_positive_gain_share"] == 1.0
        assert result["directional_ci_positive"] is True
    assert report["trajectory_minus_constant"]["sequence_bootstrap"]["mean"] == pytest.approx(0.05)


def test_reject_missing_slot_and_duplicate_trajectory():
    rows = synthetic()
    with pytest.raises(ValueError, match="Both target"):
        opportunity_summary([r for r in rows if r["target_slot"] == 1])
    with pytest.raises(ValueError, match="Incomplete or duplicate"):
        opportunity_summary(rows + [rows[0]])


def test_reproducibility_capture_and_outputs(tmp_path):
    raw = tmp_path / "raw.jsonl"
    raw.write_text("\n".join(json.dumps(r) for r in synthetic()))
    a = analyze(raw, tmp_path / "a", {"primary_metric": "J"})
    b = analyze(raw, tmp_path / "b", {"primary_metric": "J"})
    assert a == b
    selected = a["selector_analysis"]["448/validation/visible_selector"]
    assert selected["opportunity_capture"] == pytest.approx(1.0)
    assert selected["net_gain"] == pytest.approx(0.05)
    assert selected["beneficial_write_precision"] == 1.0
    with (tmp_path / "a/per_sequence_results.csv").open() as f:
        fixed = [r for r in csv.DictReader(f) if r["method"] == "best_fixed"]
    assert all(float(r["J"]) == pytest.approx(0.45) for r in fixed)
    assert (tmp_path / "a/signal_scatter.csv").exists()
    with (tmp_path / "a/visible_features.csv").open() as f:
        visible = list(csv.DictReader(f))
    assert len(visible) == len(synthetic())
    assert set(visible[0]) == {
        "resolution",
        "split",
        "sequence",
        "target_slot",
        "trajectory",
        "rgb_decrease",
    }
    assert {r["target_slot"] for r in visible} == {"1", "2"}
    assert (tmp_path / "a/visible_features.csv").read_bytes() == (
        tmp_path / "b/visible_features.csv"
    ).read_bytes()


def test_capture_is_positive_gain_not_net_gain(tmp_path):
    rows = synthetic()
    for row in rows:
        if row["trajectory"] == ["B", "B"]:
            row["J"] = 0.3
            if row["target_slot"] == 2:
                row["selected_methods"].append("visible_selector")
        if row["target_slot"] == 2 and row["trajectory"] == ["OFF", "A"]:
            row["selected_methods"].remove("visible_selector")
    raw = tmp_path / "raw.jsonl"
    raw.write_text("\n".join(json.dumps(r) for r in rows))
    selected = analyze(raw, tmp_path / "out", {})["selector_analysis"][
        "448/validation/visible_selector"
    ]
    assert selected["opportunity_capture"] == pytest.approx(0.2)
    assert selected["net_gain"] < 0
    assert selected["H2"] == "NOT_ESTABLISHED"