Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,7 @@ __marimo__/

# Output artifacts
decoded/
tests/ACMS80217/
tests/ACMS80221/
tests/ACMS80227/
tests/ACMS*/
.qc_cache/
8-00014-OIS_100-Software/
8-00015-Other-HS32-Software/
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ combine-as-imports = true
known-first-party = ["hardsector_tool"]

[tool.pytest.ini_options]
pythonpath = ["src"]
testpaths = ["tests"]
addopts = "-ra"
markers = [
Expand Down
27 changes: 27 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from __future__ import annotations

import importlib.util
import sys
from pathlib import Path

import pytest


def repo_src_path() -> Path:
"""Return the repository's ``src`` directory."""

return Path(__file__).resolve().parents[1] / "src"


def _ensure_repo_on_path() -> None:
if importlib.util.find_spec("hardsector_tool") is None:
sys.path.insert(0, str(repo_src_path()))


_ensure_repo_on_path()


def require_fixture(path: Path) -> Path:
if not path.exists():
pytest.skip(f"fixture not available: {path}", allow_module_level=True)
return path
17 changes: 17 additions & 0 deletions tests/fixtures/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# SCP fixtures

Large SCP captures used by the slow tests are not checked into git. To exercise
those tests, place the files in the following locations:

- `tests/ACMS80217/ACMS80217-HS32.scp`
- `tests/ACMS80221/ACMS80221-HS32.scp`
- `tests/ACMS80227/ACMS80227-HS32.scp`

Additional ACMS fixture directories follow the same pattern under `tests/ACMS*/`.

By default the tests will skip scenarios that require these captures when the
files are absent. To run them explicitly, supply the slow marker:

```bash
pytest -m slow
```
9 changes: 6 additions & 3 deletions tests/test_dominant_prefix_rescue.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

import pytest

from conftest import require_fixture
from hardsector_tool.scp import SCPImage
from hardsector_tool.wang import checksum_prefixes, dominant_prefix, reconstruct_track

FIXTURE = Path("tests/ACMS80221/ACMS80221-HS32.scp")
pytestmark = pytest.mark.slow

FIXTURE = Path("tests/ACMS80221/ACMS80221-HS32.scp")

@pytest.mark.skipif(not FIXTURE.exists(), reason="ACMS80221 fixture missing")
def test_dominant_prefix_rescue() -> None:
image = SCPImage.from_file(FIXTURE)
fixture = require_fixture(FIXTURE)

image = SCPImage.from_file(fixture)
sector_map, _, _ = reconstruct_track(
image,
track_number=0,
Expand Down
7 changes: 6 additions & 1 deletion tests/test_fm_decode.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from pathlib import Path

import pytest

from conftest import require_fixture
from hardsector_tool.fm import (
best_aligned_bytes,
brute_force_mark_payloads,
Expand All @@ -14,7 +17,9 @@
)
from hardsector_tool.scp import SCPImage

FIXTURE = Path("tests/ACMS80217/ACMS80217-HS32.scp")
pytestmark = pytest.mark.slow

FIXTURE = require_fixture(Path("tests/ACMS80217/ACMS80217-HS32.scp"))


def test_fm_decode_produces_bytes() -> None:
Expand Down
9 changes: 7 additions & 2 deletions tests/test_gw_hardsector_clockfactor.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
from pathlib import Path

from pathlib import Path

import pytest

from conftest import require_fixture
from hardsector_tool.fm import pll_decode_fm_bytes
from hardsector_tool.hardsector import payload_metrics
from hardsector_tool.scp import SCPImage


pytestmark = pytest.mark.slow


FIXTURES = [
Path("tests/ACMS80217/ACMS80217-HS32.scp"),
Path("tests/ACMS80221/ACMS80221-HS32.scp"),
Expand All @@ -19,8 +25,7 @@
ids=[path.parent.name for path in FIXTURES],
)
def test_clock_factor_one_increases_entropy(scp_path: Path) -> None:
if not scp_path.exists():
pytest.skip(f"Fixture not present: {scp_path}")
scp_path = require_fixture(scp_path)

image = SCPImage.from_file(scp_path)
track = image.read_track(0)
Expand Down
25 changes: 20 additions & 5 deletions tests/test_hardsector_grouping.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from pathlib import Path

import pytest

from conftest import require_fixture
from hardsector_tool.hardsector import (
FORMAT_PRESETS,
best_sector_map,
Expand All @@ -9,12 +12,16 @@
)
from hardsector_tool.scp import RevolutionEntry, SCPImage, TrackData

pytestmark = pytest.mark.slow

FIXTURE = Path("tests/ACMS80217/ACMS80217-HS32.scp")
FIXTURE_221 = Path("tests/ACMS80221/ACMS80221-HS32.scp")


def test_grouping_matches_expected_rotations() -> None:
image = SCPImage.from_file(FIXTURE)
fixture = require_fixture(FIXTURE)

image = SCPImage.from_file(fixture)
track = image.read_track(0)
assert track is not None

Expand All @@ -28,7 +35,9 @@ def test_grouping_matches_expected_rotations() -> None:


def test_grouping_pairs_holes_into_logical_sectors() -> None:
image = SCPImage.from_file(FIXTURE)
fixture = require_fixture(FIXTURE)

image = SCPImage.from_file(fixture)
track = image.read_track(0)
assert track is not None

Expand All @@ -40,7 +49,9 @@ def test_grouping_pairs_holes_into_logical_sectors() -> None:


def test_detects_short_pair_position() -> None:
image = SCPImage.from_file(FIXTURE)
fixture = require_fixture(FIXTURE)

image = SCPImage.from_file(fixture)
track = image.read_track(0)
assert track is not None

Expand All @@ -53,7 +64,9 @@ def test_detects_short_pair_position() -> None:


def test_short_pair_position_other_fixture() -> None:
image = SCPImage.from_file(FIXTURE_221)
fixture = require_fixture(FIXTURE_221)

image = SCPImage.from_file(fixture)
track = image.read_track(0)
assert track is not None

Expand Down Expand Up @@ -154,7 +167,9 @@ def test_merges_shortest_adjacent_intervals() -> None:


def test_normalization_yields_single_index_pair_per_rotation() -> None:
image = SCPImage.from_file(FIXTURE)
fixture = require_fixture(FIXTURE)

image = SCPImage.from_file(fixture)
track = image.read_track(0)
assert track is not None

Expand Down
15 changes: 13 additions & 2 deletions tests/test_reconstruct_disk_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

import pytest

from conftest import repo_src_path, require_fixture


pytestmark = pytest.mark.slow


FIXTURES = [
Path("tests/ACMS80217/ACMS80217-HS32.scp"),
Expand All @@ -17,9 +22,12 @@

@pytest.mark.parametrize("fixture_path", FIXTURES)
def test_reconstruct_disk_track0(tmp_path: Path, fixture_path: Path) -> None:
fixture_path = require_fixture(fixture_path)
out_dir = tmp_path / "reconstruct"
env = os.environ.copy()
env["PYTHONPATH"] = str(Path(__file__).resolve().parents[1] / "src")
env["PYTHONPATH"] = os.pathsep.join(
filter(None, [str(repo_src_path()), env.get("PYTHONPATH")])
)

cmd = [
sys.executable,
Expand Down Expand Up @@ -51,9 +59,12 @@ def test_reconstruct_disk_track0(tmp_path: Path, fixture_path: Path) -> None:

@pytest.mark.parametrize("fixture_path", FIXTURES)
def test_reconstruct_disk_track_mapping(tmp_path: Path, fixture_path: Path) -> None:
fixture_path = require_fixture(fixture_path)
out_dir = tmp_path / "reconstruct_mapping"
env = os.environ.copy()
env["PYTHONPATH"] = str(Path(__file__).resolve().parents[1] / "src")
env["PYTHONPATH"] = os.pathsep.join(
filter(None, [str(repo_src_path()), env.get("PYTHONPATH")])
)

cmd = [
sys.executable,
Expand Down
5 changes: 4 additions & 1 deletion tests/test_scp_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import pytest

from conftest import require_fixture
from hardsector_tool.scp import SCPImage

FIXTURE = Path("tests/ACMS80217/ACMS80217-HS32.scp")
pytestmark = pytest.mark.slow

FIXTURE = require_fixture(Path("tests/ACMS80217/ACMS80217-HS32.scp"))


@pytest.fixture(scope="session")
Expand Down
20 changes: 14 additions & 6 deletions tests/test_wang.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@

import pytest

from conftest import require_fixture
from hardsector_tool.hardsector import group_hard_sectors, pair_holes
from hardsector_tool.scp import SCPImage
from hardsector_tool.wang import reconstruct_track, scan_wang_frames

pytestmark = pytest.mark.slow

FIXTURE = Path("tests/ACMS80217/ACMS80217-HS32.scp")
FIXTURE_221 = Path("tests/ACMS80221/ACMS80221-HS32.scp")


def test_wang_scan_frame_detects_headers() -> None:
image = SCPImage.from_file(FIXTURE)
fixture = require_fixture(FIXTURE)

image = SCPImage.from_file(fixture)
track = image.read_track(0)
assert track is not None

Expand Down Expand Up @@ -44,7 +49,9 @@ def test_scan_wang_frames_accepts_plausible_offsets() -> None:


def test_reconstruct_track_sets_window_metadata() -> None:
image = SCPImage.from_file(FIXTURE)
fixture = require_fixture(FIXTURE)

image = SCPImage.from_file(fixture)
reconstructed, recon, _ = reconstruct_track(
image,
0,
Expand All @@ -61,7 +68,9 @@ def test_reconstruct_track_sets_window_metadata() -> None:


def test_reconstruct_track_supports_unpaired_sectors() -> None:
image = SCPImage.from_file(FIXTURE)
fixture = require_fixture(FIXTURE)

image = SCPImage.from_file(fixture)
reconstructed, _, _ = reconstruct_track(
image,
0,
Expand All @@ -78,10 +87,9 @@ def test_reconstruct_track_supports_unpaired_sectors() -> None:


def test_reconstruct_track_promotes_dominant_transform_family() -> None:
if not FIXTURE_221.exists():
pytest.skip("ACMS80221 fixture missing")
fixture = require_fixture(FIXTURE_221)

image = SCPImage.from_file(FIXTURE_221)
image = SCPImage.from_file(fixture)
reconstructed, _, _ = reconstruct_track(
image,
0,
Expand Down
Loading