import logging
import shutil
from types import SimpleNamespace
import numpy as np
import pytest
from ttk.core_modules.framework_api.input_generation import generate_inputs
from ttk.core_modules.manual_data import (
ManualDataError,
ManualDataStore,
load_manual_data_case,
manual_data_store,
prepare_manual_data_store,
register_manual_data_directory_provider,
replay_manual_data_store,
unregister_manual_data_directory_provider,
)
from ttk.core_modules.npu.op_api.input_generation import InputGenerator
from ttk.core_modules.testcase_manager.testcase_aclnn import TestcaseAclnn
from ttk.core_modules.testcase_manager.testcase_e2e import TestcaseE2e
from ttk.utilities import resolve_custom_numpy_dtypes
def _e2e_case(name="case/with spaces"):
case = TestcaseE2e()
case.testcase_name = name
case.api_name = "torch.add"
case.tensor_view_shapes = ((2, 2), (2, 2))
case.tensor_dtypes = ("float32", "float32")
case.tensor_formats = ("ND", "ND")
case.tensor_storage_shapes = ((3, 3), (2, 2))
case.tensor_view_offsets = (1, 0)
case.tensor_view_strides = ((3, 1), (2, 1))
case.output_tensor_indexes = (1,)
case.inplace_input_indexes = ()
case.attributes = {"alpha": 1}
case.input_data_ranges = ((-1, 1), (-1, 1))
case.golden_api = ""
case._tensor_list_dist = (0, 0)
case._pure_output_indexes = [1]
return case
def _aclnn_case():
case = TestcaseAclnn()
case.testcase_name = "aclnn_case"
case.api_name = "aclnnAdd"
case.tensor_view_shapes = ((2,), (2,))
case.tensor_dtypes = ("float32", "float32")
case.tensor_formats = ("ND", "ND")
case.tensor_storage_shapes = ((2,), (2,))
case.tensor_view_offsets = (0, 0)
case.tensor_view_strides = ((1,), (1,))
case.output_tensor_indexes = (1,)
case.output_inplace_indexes = ()
case.inplace_input_indexes = ()
case.attributes = {}
case.input_data_ranges = ((-1, 1), (-1, 1))
case.scalar_dtypes = ("float32",)
case.scalar_data_ranges = ((0, 1),)
case._tensor_list_dist = (0, 0)
case._scalar_list_dist = (0,)
case._pure_output_indexes = [1]
return case
def _kernel_case(name="kernel_case"):
return SimpleNamespace(
testcase_name=name,
flat_input_shapes=((2,), None),
flat_input_dtypes=("float32", "float32"),
flat_output_shapes=((2,),),
flat_output_dtypes=("float32",),
)
@pytest.mark.parametrize("file_format", ["bin", "npy", "pt"])
def test_round_trip_uses_only_typed_data_files(tmp_path, file_format):
case = _aclnn_case()
inputs = [np.arange(2, dtype=np.float32), np.zeros(2, dtype=np.float32)]
scalars = [np.array(0.25, dtype=np.float32)]
goldens = [np.array([3.0, 4.0], dtype=np.float32)]
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case, "aclnn", inputs, goldens, scalars=scalars,
file_format=file_format,
)
loaded = store.load_case(case, "aclnn")
loaded_goldens = loaded.load_goldens(references=goldens)
np.testing.assert_array_equal(loaded.inputs[0], inputs[0])
np.testing.assert_array_equal(loaded.inputs[1], inputs[1])
np.testing.assert_array_equal(loaded.scalars[0], scalars[0])
np.testing.assert_array_equal(loaded_goldens[0], goldens[0])
assert {path.name for path in case_dir.iterdir()} == {
f"input_0_float32.{file_format}",
f"input_1_float32.{file_format}",
f"scalar_0_float32.{file_format}",
(
"golden_0_float32__shape_2.bin"
if file_format == "bin"
else f"golden_0_float32.{file_format}"
),
}
@pytest.mark.parametrize("file_format", ["bin", "npy", "pt"])
def test_complete_dataset_remains_loadable_after_directory_move(tmp_path, file_format):
case = _aclnn_case()
inputs = [np.arange(2, dtype=np.float32), np.zeros(2, dtype=np.float32)]
scalars = [np.array(0.25, dtype=np.float32)]
goldens = [np.array([3.0, 4.0], dtype=np.float32)]
source = tmp_path / "source"
destination = tmp_path / "destination"
ManualDataStore(source).write_case(
case, "aclnn", inputs, goldens, scalars=scalars, file_format=file_format
)
shutil.copytree(source, destination)
loaded = ManualDataStore(destination).load_case(case, "aclnn")
loaded_goldens = loaded.load_goldens(references=goldens)
assert loaded.case_dir.parent == destination.resolve()
np.testing.assert_array_equal(loaded.inputs[0], inputs[0])
np.testing.assert_array_equal(loaded.scalars[0], scalars[0])
np.testing.assert_array_equal(loaded_goldens[0], goldens[0])
def test_npy_round_trip_restores_custom_bfloat16_dtype(tmp_path):
case = _e2e_case("bfloat16_npy")
case.tensor_dtypes = ("bfloat16", "bfloat16")
dtype = resolve_custom_numpy_dtypes(("bfloat16",))[0]
inputs = [np.arange(9, dtype=np.float32).astype(dtype).reshape(3, 3),
np.zeros((2, 2), dtype=dtype)]
golden = [np.ones((2, 2), dtype=dtype)]
store = ManualDataStore(tmp_path)
store.write_case(case, "e2e", inputs, golden, file_format="npy")
loaded = store.load_case(case, "e2e")
loaded_golden = loaded.load_goldens(references=golden)
assert loaded.inputs[0].dtype.name == "bfloat16"
assert loaded_golden[0].dtype.name == "bfloat16"
np.testing.assert_array_equal(loaded.inputs[0], inputs[0])
@pytest.mark.parametrize("file_format", ["bin", "npy", "pt"])
@pytest.mark.parametrize(
"logical_dtype",
["complex32", "uint1", "int4", "float8_e4m3fn", "float8_e5m2"],
)
def test_special_physical_storage_round_trip(tmp_path, file_format, logical_dtype):
case = _e2e_case(f"{logical_dtype}_{file_format}")
case.tensor_dtypes = (logical_dtype, "float32")
if logical_dtype == "complex32":
storage = np.arange(18, dtype=np.float16).reshape(3, 3, 2)
elif logical_dtype == "uint1":
storage = np.array([0x55, 0x01], dtype=np.uint8)
else:
dtype = resolve_custom_numpy_dtypes((logical_dtype,))[0]
storage = np.arange(-4, 5, dtype=np.int8).astype(dtype).reshape(3, 3)
store = ManualDataStore(tmp_path)
store.write_case(
case,
"e2e",
[storage, np.zeros((2, 2), np.float32)],
[np.zeros((2, 2), np.float32)],
file_format=file_format,
)
loaded = store.load_case(case, "e2e")
assert loaded.inputs[0].dtype.name == storage.dtype.name
assert loaded.inputs[0].shape == storage.shape
if logical_dtype == "int4" and file_format == "bin":
np.testing.assert_array_equal(loaded.inputs[0], storage)
else:
assert loaded.inputs[0].tobytes() == storage.tobytes()
@pytest.mark.parametrize("file_format", ["bin", "npy", "pt"])
def test_kernel_round_trip_uses_kernel_csv_shapes(tmp_path, file_format):
case = _kernel_case(f"kernel_{file_format}")
inputs = [np.array([1.0, 2.0], np.float32), None]
goldens = [np.array([3.0, 4.0], np.float32)]
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case, "kernel", inputs, goldens, file_format=file_format
)
loaded = store.load_case(case, "kernel")
loaded_goldens = loaded.load_goldens(
shapes=case.flat_output_shapes,
dtypes=case.flat_output_dtypes,
)
assert loaded.file_format == file_format
assert (case_dir / f"input_1_none.{file_format}").stat().st_size == 0
np.testing.assert_array_equal(loaded.inputs[0], inputs[0])
assert loaded.inputs[1] is None
np.testing.assert_array_equal(loaded_goldens[0], goldens[0])
def test_e2e_noncontiguous_backing_storage_and_none_slot_round_trip(tmp_path):
case = _e2e_case()
first = np.arange(9, dtype=np.float32).reshape(3, 3)
second = np.ones((2, 2), dtype=np.float32)
golden = [np.ones((2, 2), dtype=np.float32), None]
store = ManualDataStore(tmp_path)
store.write_case(case, "e2e", [first, second], golden)
loaded = store.load_case(case, "e2e")
loaded_golden = loaded.load_goldens(references=golden)
np.testing.assert_array_equal(loaded.inputs[0], first)
assert loaded_golden[1] is None
assert (loaded.case_dir / "golden_1_none.bin").stat().st_size == 0
def test_e2e_none_golden_suppresses_optional_device_output(tmp_path):
case = _e2e_case("none_golden_optional_device_output")
store = ManualDataStore(tmp_path)
store.write_case(
case,
"e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.ones((2, 2), np.float32), None],
)
loaded = store.load_case(case, "e2e")
goldens = loaded.load_goldens(references=[
np.zeros((2, 2), np.float32),
np.zeros((1, 2), np.float32),
])
np.testing.assert_array_equal(goldens[0], np.ones((2, 2), np.float32))
assert goldens[1] is None
def test_tensor_list_grouping_is_rebuilt_from_csv_structure(tmp_path):
case = _e2e_case("tensor_list")
case.tensor_view_shapes = (((2,), (3,)), (2,))
case.tensor_dtypes = (("float32", "int32"), "float32")
case.tensor_formats = (("ND", "ND"), "ND")
case.tensor_storage_shapes = (((2,), (3,)), (2,))
case.tensor_view_offsets = ((0, 0), 0)
case.tensor_view_strides = (((1,), (1,)), (1,))
case.output_tensor_indexes = (1,)
case._tensor_list_dist = (2, 0)
case._pure_output_indexes = [2]
inputs = [
np.array([1.0, 2.0], np.float32),
np.array([3, 4, 5], np.int32),
np.zeros(2, np.float32),
]
store = ManualDataStore(tmp_path)
store.write_case(case, "e2e", inputs, [np.ones(2, np.float32)])
loaded = store.load_case(case, "e2e")
switches = SimpleNamespace(plugin_path=("must-not-be-scanned",))
backend = SimpleNamespace(alias=lambda: "npu")
generate_inputs(case, switches, backend, object(), stored_inputs=loaded.inputs)
assert isinstance(case.tensors[0], list)
assert len(case.tensors[0]) == 2
assert tuple(case.tensors[0][0].shape) == (2,)
assert tuple(case.tensors[0][1].shape) == (3,)
assert tuple(case.tensors[1].shape) == (2,)
def test_changed_csv_dtype_is_rejected_by_filename(tmp_path):
case = _e2e_case("contract_case")
store = ManualDataStore(tmp_path)
store.write_case(
case, "e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.zeros((2, 2), np.float32)],
)
case.tensor_dtypes = ("float16", "float32")
case.invalidate_flat_cache("tensor_dtypes")
with pytest.raises(ManualDataError, match="filename dtype"):
store.load_case(case, "e2e")
def test_precision_fields_can_change_without_invalidating_prepared_data(tmp_path):
case = _e2e_case("precision_case")
case.precision_tolerances = ((0.001, 0.001),)
case.absolute_precision = (1e-5,)
store = ManualDataStore(tmp_path)
store.write_case(
case, "e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.zeros((2, 2), np.float32)],
)
case.precision_tolerances = ((0.005, 0.005),)
case.absolute_precision = (1e-4,)
assert store.load_case(case, "e2e").case_dir.name == "precision_case"
def test_corrupt_file_is_rejected_before_loading(tmp_path):
case = _e2e_case("hash_case")
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case, "e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.zeros((2, 2), np.float32)],
)
with (case_dir / "input_0_float32.bin").open("ab") as stream:
stream.write(b"corrupt")
with pytest.raises(ManualDataError, match="byte size"):
store.load_case(case, "e2e")
@pytest.mark.parametrize("file_format", ["npy", "pt"])
def test_self_describing_format_rejects_same_size_wrong_shape(tmp_path, file_format):
case = _e2e_case(f"wrong_{file_format}_shape")
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case,
"e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.zeros((2, 2), np.float32)],
file_format=file_format,
)
path = case_dir / f"input_0_float32.{file_format}"
wrong_shape = np.zeros((1, 9), np.float32)
if file_format == "npy":
np.save(path, wrong_shape)
else:
import torch
torch.save(torch.from_numpy(wrong_shape), path)
with pytest.raises(ManualDataError, match="stored shape"):
store.load_case(case, "e2e")
@pytest.mark.parametrize("file_format", ["npy", "pt"])
def test_self_describing_format_rejects_same_size_wrong_dtype(tmp_path, file_format):
case = _e2e_case(f"wrong_{file_format}_dtype")
case.tensor_dtypes = ("bfloat16", "bfloat16")
dtype = resolve_custom_numpy_dtypes(("bfloat16",))[0]
inputs = [
np.zeros((3, 3), dtype=dtype),
np.zeros((2, 2), dtype=dtype),
]
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case,
"e2e",
inputs,
[np.zeros((2, 2), dtype=dtype)],
file_format=file_format,
)
path = case_dir / f"input_0_bfloat16.{file_format}"
wrong_dtype = np.zeros((3, 3), np.int16)
if file_format == "npy":
np.save(path, wrong_dtype)
else:
import torch
torch.save(torch.from_numpy(wrong_dtype), path)
with pytest.raises(ManualDataError, match="dtype .* != filename dtype"):
store.load_case(case, "e2e")
def test_pt_raw_byte_payload_keeps_shape_inside_data_file(tmp_path):
import torch
case = _e2e_case("raw_byte_pt_shape")
case.tensor_dtypes = ("float128", "float32")
inputs = [
np.arange(9, dtype=np.float128).reshape(3, 3),
np.zeros((2, 2), np.float32),
]
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case,
"e2e",
inputs,
[np.zeros((2, 2), np.float32)],
file_format="pt",
)
path = case_dir / "input_0_float128.pt"
payload = torch.load(path, map_location="cpu", weights_only=True)
assert set(payload) == {"ttk_raw_bytes", "ttk_shape"}
assert tuple(payload["ttk_shape"]) == (3, 3)
payload["ttk_shape"] = (1, 9)
torch.save(payload, path)
with pytest.raises(ManualDataError, match="stored shape"):
store.load_case(case, "e2e")
def test_pt_legacy_torch_load_warns_about_pickle_fallback(tmp_path, monkeypatch, caplog):
import torch
case = _e2e_case("legacy_pt_load")
store = ManualDataStore(tmp_path)
inputs = [np.ones((3, 3), np.float32), np.zeros((2, 2), np.float32)]
store.write_case(case, "e2e", inputs, [np.zeros((2, 2), np.float32)], file_format="pt")
original_load = torch.load
def legacy_load(*args, **kwargs):
if "weights_only" in kwargs:
raise TypeError("weights_only is unsupported")
return original_load(*args, **kwargs)
monkeypatch.setattr(torch, "load", legacy_load)
with caplog.at_level(logging.WARNING):
loaded = store.load_case(case, "e2e")
np.testing.assert_array_equal(loaded.inputs[0], inputs[0])
assert "falling back to pickle-based loading" in caplog.text
def test_pt_load_type_error_does_not_fall_back_to_pickle(tmp_path, monkeypatch, caplog):
import torch
case = _e2e_case("pt_load_type_error")
store = ManualDataStore(tmp_path)
inputs = [np.ones((3, 3), np.float32), np.zeros((2, 2), np.float32)]
store.write_case(case, "e2e", inputs, [np.zeros((2, 2), np.float32)], file_format="pt")
def broken_load(path, map_location, weights_only):
raise TypeError("payload decoder failed")
monkeypatch.setattr(torch, "load", broken_load)
with caplog.at_level(logging.WARNING):
with pytest.raises(ManualDataError, match="payload decoder failed"):
store.load_case(case, "e2e")
assert "falling back to pickle-based loading" not in caplog.text
@pytest.mark.parametrize("file_format", ["bin", "npy", "pt"])
def test_none_slots_are_explicit_empty_data_files(tmp_path, file_format):
case = _e2e_case("none_input")
case.tensor_view_shapes = (None, (2, 2))
case.tensor_storage_shapes = (None, (2, 2))
case.tensor_view_offsets = (None, 0)
case.tensor_view_strides = (None, (2, 1))
case._flat_tensor_view_shapes = None
case._flat_tensor_storage_shapes = None
case._flat_tensor_view_offsets = None
case._flat_tensor_view_strides = None
case_dir = ManualDataStore(tmp_path).write_case(
case,
"e2e",
[None, np.ones((2, 2), np.float32)],
[np.ones((2, 2), np.float32)],
file_format=file_format,
)
loaded = ManualDataStore(tmp_path).load_case(case, "e2e")
assert loaded.inputs[0] is None
assert (case_dir / f"input_0_none.{file_format}").stat().st_size == 0
@pytest.mark.parametrize("file_format", ["bin", "npy", "pt"])
def test_missing_none_marker_is_rejected(tmp_path, file_format):
case = _e2e_case("missing_none_marker")
case.tensor_view_shapes = (None, (2, 2))
case.tensor_storage_shapes = (None, (2, 2))
case.tensor_view_offsets = (None, 0)
case.tensor_view_strides = (None, (2, 1))
case._flat_tensor_view_shapes = None
case._flat_tensor_storage_shapes = None
case._flat_tensor_view_offsets = None
case._flat_tensor_view_strides = None
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case,
"e2e",
[None, np.ones((2, 2), np.float32)],
[np.ones((2, 2), np.float32)],
file_format=file_format,
)
(case_dir / f"input_0_none.{file_format}").unlink()
with pytest.raises(ManualDataError, match="slot count|contiguous"):
store.load_case(case, "e2e")
@pytest.mark.parametrize("file_format", ["bin", "npy", "pt"])
def test_zero_element_tensor_is_not_confused_with_none(tmp_path, file_format):
case = _e2e_case("zero_element_tensor")
case.tensor_view_shapes = ((0, 3), (2, 2))
case.tensor_storage_shapes = ((0, 3), (2, 2))
case.tensor_view_offsets = (0, 0)
case.tensor_view_strides = ((3, 1), (2, 1))
case._flat_tensor_view_shapes = None
case._flat_tensor_storage_shapes = None
case._flat_tensor_view_offsets = None
case._flat_tensor_view_strides = None
empty = np.empty((0, 3), np.float32)
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case,
"e2e",
[empty, np.ones((2, 2), np.float32)],
[np.ones((2, 2), np.float32)],
file_format=file_format,
)
loaded = store.load_case(case, "e2e")
assert loaded.inputs[0].shape == (0, 3)
assert loaded.inputs[0].dtype == np.dtype("float32")
assert (case_dir / f"input_0_float32.{file_format}").is_file()
def test_unknown_sidecar_is_rejected(tmp_path):
case = _e2e_case("no_sidecars")
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case, "e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.zeros((2, 2), np.float32)],
)
(case_dir / "manifest.json").write_text("{}", encoding="utf-8")
with pytest.raises(ManualDataError, match="unexpected file"):
store.load_case(case, "e2e")
@pytest.mark.parametrize(
"formats, expected_format, expected_value",
[
(("pt", "npy"), "npy", 2.0),
(("pt", "npy", "bin"), "bin", 3.0),
],
)
def test_mixed_data_formats_use_whole_dataset_priority(
tmp_path, formats, expected_format, expected_value):
case = _e2e_case("mixed_formats")
values = {"pt": 1.0, "npy": 2.0, "bin": 3.0}
target_store = ManualDataStore(tmp_path / "mixed")
target_dir = target_store.case_dir(case.testcase_name)
target_dir.mkdir(parents=True)
for file_format in formats:
source_store = ManualDataStore(tmp_path / f"source-{file_format}")
source_dir = source_store.write_case(
case,
"e2e",
[
np.full((3, 3), values[file_format], np.float32),
np.zeros((2, 2), np.float32),
],
[np.full((2, 2), values[file_format], np.float32)],
file_format=file_format,
)
for path in source_dir.iterdir():
shutil.copy2(path, target_dir / path.name)
loaded = target_store.load_case(case, "e2e")
loaded_golden = loaded.load_goldens(
references=[np.zeros((2, 2), np.float32)]
)
assert loaded.file_format == expected_format
np.testing.assert_array_equal(
loaded.inputs[0], np.full((3, 3), expected_value, np.float32)
)
np.testing.assert_array_equal(
loaded_golden[0], np.full((2, 2), expected_value, np.float32)
)
def test_incomplete_high_priority_format_does_not_fall_back(tmp_path):
case = _e2e_case("incomplete_priority")
target_store = ManualDataStore(tmp_path / "mixed")
bin_dir = ManualDataStore(tmp_path / "bin").write_case(
case,
"e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.zeros((2, 2), np.float32)],
file_format="bin",
)
npy_dir = ManualDataStore(tmp_path / "npy").write_case(
case,
"e2e",
[np.ones((3, 3), np.float32), np.ones((2, 2), np.float32)],
[np.ones((2, 2), np.float32)],
file_format="npy",
)
target_dir = target_store.case_dir(case.testcase_name)
target_dir.mkdir(parents=True)
for source_dir in (bin_dir, npy_dir):
for path in source_dir.iterdir():
shutil.copy2(path, target_dir / path.name)
(target_dir / "golden_0_float32__shape_2x2.bin").unlink()
loaded = target_store.load_case(case, "e2e")
assert loaded.file_format == "bin"
with pytest.raises(ManualDataError, match="golden slot count"):
loaded.load_goldens(references=[np.zeros((2, 2), np.float32)])
def test_long_case_name_maps_stably_within_directory_limit(tmp_path):
name = "kernel command / " + "very-long-case-" * 20
case = _e2e_case(name)
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case,
"e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.zeros((2, 2), np.float32)],
)
assert len(case_dir.name) <= 120
assert store.case_dir(name) == case_dir
assert store.load_case(case, "e2e").case_dir == case_dir
assert store.case_dir(name + "changed") != case_dir
@pytest.mark.parametrize(
"filename", ["input_1_float32.bin", "golden_0_float32__shape_2x2.bin"]
)
def test_missing_data_slot_is_rejected(tmp_path, filename):
case = _e2e_case("missing_slot")
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case, "e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.zeros((2, 2), np.float32)],
)
(case_dir / filename).unlink()
with pytest.raises(ManualDataError, match="slot count|contiguous"):
loaded = store.load_case(case, "e2e")
loaded.load_goldens(references=[np.zeros((2, 2), np.float32)])
def test_extra_data_slot_is_rejected(tmp_path):
case = _e2e_case("extra_slot")
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case, "e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.zeros((2, 2), np.float32)],
)
(case_dir / "input_2_float32.bin").write_bytes(b"")
with pytest.raises(ManualDataError, match="slot count"):
store.load_case(case, "e2e")
@pytest.mark.parametrize("file_format", ["bin", "npy", "pt"])
def test_nonempty_none_marker_is_rejected(tmp_path, file_format):
case = _e2e_case("none_golden")
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case, "e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[None],
file_format=file_format,
)
(case_dir / f"golden_0_none.{file_format}").write_bytes(b"not-empty")
loaded = store.load_case(case, "e2e")
with pytest.raises(ManualDataError, match="must be empty"):
loaded.load_goldens(references=[None])
def test_bin_golden_filename_shape_matches_device_output(tmp_path):
case = _e2e_case("deferred_bin_shape")
store = ManualDataStore(tmp_path)
golden = np.arange(4, dtype=np.float32).reshape(2, 2)
case_dir = store.write_case(
case, "e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[golden],
)
loaded = store.load_case(case, "e2e")
restored = loaded.load_goldens(references=[np.zeros((2, 2), np.float32)])
assert restored[0].shape == (2, 2)
assert (case_dir / "golden_0_float32__shape_2x2.bin").is_file()
np.testing.assert_array_equal(restored[0], golden)
def test_bin_golden_rejects_same_numel_wrong_device_shape(tmp_path):
case = _e2e_case("same_numel_wrong_shape")
store = ManualDataStore(tmp_path)
store.write_case(
case,
"e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.arange(6, dtype=np.float32).reshape(2, 3)],
)
loaded = store.load_case(case, "e2e")
with pytest.raises(ManualDataError, match="saved shape .*device output shape"):
loaded.load_goldens(references=[np.zeros((3, 2), np.float32)])
@pytest.mark.parametrize(
"shape, token",
[
((), "scalar"),
((0,), "0"),
((2, 0, 3), "2x0x3"),
],
)
def test_bin_golden_shape_filename_handles_scalar_and_zero_dimensions(
tmp_path, shape, token):
case = _e2e_case(f"shape_{token}")
store = ManualDataStore(tmp_path)
golden = np.zeros(shape, dtype=np.float32)
case_dir = store.write_case(
case,
"e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[golden],
)
loaded = store.load_case(case, "e2e")
restored = loaded.load_goldens(references=[np.zeros(shape, np.float32)])
assert (case_dir / f"golden_0_float32__shape_{token}.bin").is_file()
expected_storage_shape = (1,) if shape == () else shape
assert restored[0].shape == expected_storage_shape
@pytest.mark.parametrize("file_format", ["bin", "npy", "pt"])
def test_scalar_golden_round_trip_preserves_shape(tmp_path, file_format):
case = _e2e_case(f"scalar_{file_format}")
store = ManualDataStore(tmp_path)
golden = np.array(1.0, dtype=np.float32)
store.write_case(
case,
"e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[golden],
file_format=file_format,
)
loaded = store.load_case(case, "e2e")
restored = loaded.load_goldens(references=[np.array(0.0, np.float32)])
assert restored[0].shape == (1,)
np.testing.assert_array_equal(restored[0], golden)
def test_bin_golden_without_shape_suffix_is_rejected(tmp_path):
case = _e2e_case("bin_golden_without_shape")
store = ManualDataStore(tmp_path)
case_dir = store.write_case(
case,
"e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.arange(4, dtype=np.float32).reshape(2, 2)],
)
current = case_dir / "golden_0_float32__shape_2x2.bin"
current.rename(case_dir / "golden_0_float32.bin")
with pytest.raises(ManualDataError, match="must include a shape suffix"):
store.load_case(case, "e2e")
def test_golden_sentinel_never_publishes_case_directory(tmp_path):
case = _e2e_case("failed_prepare")
store = ManualDataStore(tmp_path)
with pytest.raises(ManualDataError, match="sentinel"):
store.write_case(
case, "e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
["GOLDEN_FAILURE"],
)
assert not store.case_dir(case.testcase_name).exists()
def test_failed_reprepare_invalidates_previous_case(tmp_path):
case = _e2e_case("failed_reprepare")
store = ManualDataStore(tmp_path)
inputs = [np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)]
old_golden = np.ones((2, 2), np.float32)
store.write_case(case, "e2e", inputs, [old_golden])
with pytest.raises(ManualDataError, match="sentinel"):
store.write_case(case, "e2e", inputs, ["GOLDEN_FAILURE"])
assert not store.case_dir(case.testcase_name).exists()
def test_invalidate_case_unlinks_directory_symlink_without_touching_target(tmp_path):
case = _e2e_case("symlinked_case")
store = ManualDataStore(tmp_path / "prepared")
external = tmp_path / "external"
external.mkdir()
marker = external / "keep.txt"
marker.write_text("keep", encoding="utf-8")
case_dir = store.case_dir(case.testcase_name)
case_dir.parent.mkdir(parents=True)
case_dir.symlink_to(external, target_is_directory=True)
store.invalidate_case(case.testcase_name)
assert marker.read_text(encoding="utf-8") == "keep"
assert not case_dir.is_symlink()
def test_prepare_and_replay_helpers_share_store_policy(tmp_path):
prepare_case = _e2e_case("prepare_helper")
prepare_switches = SimpleNamespace(
manual_data_mode="prepare", manual_data_dirs=(str(tmp_path),)
)
prepare_store = prepare_manual_data_store(prepare_case, "e2e", prepare_switches)
assert isinstance(prepare_store, ManualDataStore)
assert not prepare_store.case_dir(prepare_case.testcase_name).exists()
replay_case = _e2e_case("replay_helper")
inputs = [np.ones((3, 3), np.float32), np.zeros((2, 2), np.float32)]
ManualDataStore(tmp_path).write_case(
replay_case, "e2e", inputs, [np.zeros((2, 2), np.float32)]
)
replay_switches = SimpleNamespace(
manual_data_mode="replay",
manual_data_dirs=(str(tmp_path),),
golden_mode="Enable",
validate_only=False,
force_cpu=False,
)
loaded_status = []
manual_case = load_manual_data_case(
replay_case, "e2e", replay_switches, before_load=lambda: loaded_status.append(True)
)
assert loaded_status == [True]
np.testing.assert_array_equal(manual_case.inputs[0], inputs[0])
def test_registered_provider_is_an_extension_point_for_future_csv_sources(tmp_path):
case = _e2e_case("provider_case")
ManualDataStore(tmp_path).write_case(
case, "e2e",
[np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)],
[np.zeros((2, 2), np.float32)],
)
switches = SimpleNamespace(manual_data_dirs=())
def provider(testcase, case_type, current_switches):
assert testcase is case
assert case_type == "e2e"
assert current_switches is switches
return tmp_path
register_manual_data_directory_provider(provider)
try:
loaded = manual_data_store(case, "e2e", switches).load_case(case, "e2e")
finally:
unregister_manual_data_directory_provider(provider)
assert loaded.case_dir == ManualDataStore(tmp_path).case_dir(case.testcase_name)
def test_case_provider_takes_priority_over_cli_batch_directories(tmp_path):
case = _e2e_case("provider_priority")
inputs = [np.zeros((3, 3), np.float32), np.zeros((2, 2), np.float32)]
cli_root = tmp_path / "cli"
provider_root = tmp_path / "provider"
ManualDataStore(cli_root).write_case(
case, "e2e", inputs, [np.full((2, 2), 1, np.float32)]
)
ManualDataStore(provider_root).write_case(
case, "e2e", inputs, [np.full((2, 2), 2, np.float32)]
)
switches = SimpleNamespace(manual_data_dirs=(cli_root,))
def provider(*_):
return provider_root
register_manual_data_directory_provider(provider)
try:
loaded = manual_data_store(case, "e2e", switches).load_case(case, "e2e")
finally:
unregister_manual_data_directory_provider(provider)
assert loaded.case_dir.parent == provider_root.resolve()
np.testing.assert_array_equal(
loaded.load_goldens(references=[np.zeros((2, 2), np.float32)])[0],
np.full((2, 2), 2, np.float32),
)
def test_provider_replay_obeys_device_stage_constraints(tmp_path):
case = _e2e_case("provider_constraints")
switches = SimpleNamespace(
manual_data_dirs=(), golden_mode="Enable", validate_only=False, force_cpu=True
)
def provider(*_):
return tmp_path
register_manual_data_directory_provider(provider)
try:
with pytest.raises(ManualDataError, match="cannot use --cpu"):
replay_manual_data_store(case, "e2e", switches)
finally:
unregister_manual_data_directory_provider(provider)
def test_e2e_restore_rebuilds_view_without_running_input_plugin():
case = _e2e_case("restore_e2e")
storage = np.arange(9, dtype=np.float32).reshape(3, 3)
output = np.zeros((2, 2), dtype=np.float32)
switches = SimpleNamespace(plugin_path=("must-not-be-scanned",))
backend = SimpleNamespace(alias=lambda: "npu")
raw = generate_inputs(
case, switches, backend, object(), stored_inputs=[storage, output]
)
assert tuple(case.tensors[0].stride()) == (3, 1)
assert float(case.tensors[0][0, 0]) == float(storage.ravel()[1])
assert np.shares_memory(raw[0], storage)
def test_aclnn_restore_rebuilds_tensor_and_scalar_without_plugins(monkeypatch):
case = _aclnn_case()
switches = SimpleNamespace(plugin_path=("must-not-be-scanned",))
monkeypatch.setattr(
"ttk.core_modules.npu.op_api.input_generation.get_global_storage",
lambda: switches,
)
inputs = [np.arange(2, dtype=np.float32), np.zeros(2, dtype=np.float32)]
scalar = np.array(0.5, dtype=np.float32)
InputGenerator(case).gen(stored_inputs=inputs, stored_scalars=[scalar])
assert tuple(case.tensors[0].stride()) == (1,)
assert float(case.scalars[0]) == pytest.approx(0.5)