124 lines
3.6 KiB
Python
124 lines
3.6 KiB
Python
"""Check progress reporter (TTY and NO_COLOR behaviour)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import io
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from applepy.check_progress import (
|
|
CheckProgressReporter,
|
|
_ascii_progress_bar,
|
|
_completion_percent,
|
|
check_run_failed,
|
|
)
|
|
from applepy.context import RunContext
|
|
from applepy.findings import Finding, Severity
|
|
from applepy.registry import CheckRegistry
|
|
from applepy.runner import run_phase
|
|
|
|
|
|
def test_ascii_progress_bar_edges() -> None:
|
|
assert _ascii_progress_bar(0, 4, 8) == "[--------]"
|
|
assert _ascii_progress_bar(4, 4, 8) == "[########]"
|
|
assert "?" in _ascii_progress_bar(0, 0, 6)
|
|
|
|
|
|
def test_completion_percent_saturates() -> None:
|
|
assert _completion_percent(0, 10) == 0
|
|
assert _completion_percent(5, 10) == 50
|
|
assert _completion_percent(10, 10) == 100
|
|
assert _completion_percent(0, 0) == 100
|
|
|
|
|
|
def test_check_done_includes_percent_and_bar(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
buf = io.StringIO()
|
|
monkeypatch.setenv("NO_COLOR", "1")
|
|
r = CheckProgressReporter(stream=buf)
|
|
r.check_done(3, 10, "short_name", 0.05, 0, False)
|
|
line = buf.getvalue()
|
|
assert " 30%" in line
|
|
assert "[###" in line or "[##" in line
|
|
|
|
|
|
def test_phase_end_colours_nonzero_counts_when_force_colour(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
buf = io.StringIO()
|
|
monkeypatch.delenv("NO_COLOR", raising=False)
|
|
monkeypatch.setenv("FORCE_COLOR", "1")
|
|
monkeypatch.setattr(buf, "isatty", lambda: True)
|
|
r = CheckProgressReporter(stream=buf)
|
|
f = Finding(
|
|
id="h1",
|
|
title="t",
|
|
category="c",
|
|
severity=Severity.HIGH,
|
|
description="d",
|
|
evidence="e",
|
|
worksheet="W",
|
|
)
|
|
r.phase_end("unprivileged", [f])
|
|
text = buf.getvalue()
|
|
assert "\033[33m" in text
|
|
assert "high:" in text
|
|
|
|
|
|
def test_phase_lines_contain_no_escapes_when_colour_disabled(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
buf = io.StringIO()
|
|
monkeypatch.setenv("NO_COLOR", "1")
|
|
r = CheckProgressReporter(stream=buf)
|
|
r.phase_begin("unprivileged", 2)
|
|
r.check_start(1, 2, "a")
|
|
r.check_done(1, 2, "a", 0.1, 1, False)
|
|
f = Finding(
|
|
id="x",
|
|
title="t",
|
|
category="c",
|
|
severity=Severity.INFORMATIONAL,
|
|
description="d",
|
|
evidence="e",
|
|
worksheet="W",
|
|
)
|
|
r.phase_end("unprivileged", [f])
|
|
r.scan_complete([f], "/tmp/out")
|
|
text = buf.getvalue()
|
|
assert "\033" not in text
|
|
assert "Unprivileged phase" in text
|
|
assert "Scan complete" in text
|
|
assert "critical: 0" in text
|
|
assert "informational: 1" in text
|
|
|
|
|
|
def test_check_run_failed_detects_run_prefix() -> None:
|
|
f = Finding(
|
|
id="run-deadbeef01",
|
|
title="Check failed",
|
|
category="Scanner reliability",
|
|
severity=Severity.LOW,
|
|
description="d",
|
|
evidence="e",
|
|
worksheet="Scanner reliability",
|
|
)
|
|
assert check_run_failed([f]) is True
|
|
assert check_run_failed([]) is False
|
|
|
|
|
|
def test_run_phase_invokes_progress_sequential(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
buf = io.StringIO()
|
|
monkeypatch.setenv("NO_COLOR", "1")
|
|
r = CheckProgressReporter(stream=buf)
|
|
|
|
reg = CheckRegistry()
|
|
|
|
def _one(_ctx: RunContext) -> list[Finding]:
|
|
return []
|
|
|
|
reg.register("z_check", _one, phases=("unprivileged",))
|
|
base = RunContext(home=tmp_path, output_dir=tmp_path, phase="unprivileged")
|
|
run_phase(reg, "unprivileged", base, parallel=False, progress=r)
|
|
out = buf.getvalue()
|
|
assert "z_check" in out
|
|
assert "running" in out
|
|
assert "ok" in out
|