"""
Regression test for the KERNEL builtin-golden dispatch path in
output_generation.__generate_golden:
get_plugin_function(op, "golden", "kernel") -> None
-> KERNEL_GOLDEN[op] resolves (e.g. "neg" -> numpy.negative)
-> framework_of(numpy.negative) == "numpy"
-> __call_numpy_api
-> __invoke_golden returns numeric numpy arrays
This guards the refactor (T2) that unified KERNEL dispatch through
__invoke_golden + framework_of. If the dispatch regressed such that a
builtin op fell through to "UNSUPPORTED" / "GOLDEN_FAILURE", the
assertion that the first golden element is a numeric numpy.ndarray
(not a sentinel string) would fail.
"""
import numpy as np
import pytest
from unittest.mock import patch, MagicMock
from ttk.core_modules.testcase_manager.testcase_op import TestcaseOp
from ttk.core_modules.npu.op import output_generation as _mod
_generate_golden = getattr(_mod, '__generate_golden')
KERNEL_GOLDEN = _mod.KERNEL_GOLDEN
def _make_testcase(op_name="neg", input_shapes=((4,),),
input_dtypes=("float32",),
output_shapes=((4,),),
output_dtypes=("float32",)):
case = TestcaseOp()
case.testcase_name = f"test_{op_name}_builtin_golden"
case.op_name = op_name
case.input_shapes = input_shapes
case.input_dtypes = input_dtypes
case.output_shapes = output_shapes
case.output_dtypes = output_dtypes
case.input_ori_shapes = input_shapes
case.output_ori_shapes = output_shapes
n_in = len(input_shapes)
n_out = len(output_shapes or ())
case.input_formats = ("ND",) * n_in
case.input_ori_formats = ("ND",) * n_in
case.output_formats = ("ND",) * n_out
case.output_ori_formats = ("ND",) * n_out
case.input_data_ranges = (None,) * n_in
case.attributes = {}
case.input_arrays = tuple(np.ones(s, dtype=d) for s, d in zip(input_shapes, input_dtypes))
case.original_input_arrays = None
return case
def _mock_switches():
sw = MagicMock()
sw.dev_plat = "Ascend910B2"
sw.short_soc_version = "Ascend910B"
sw.golden_mode = "Enable"
sw.plugin_path = None
sw.overflow_mode = 0
return sw
@pytest.fixture(autouse=True)
def _mock_env(monkeypatch):
monkeypatch.delenv("ASCEND_HOME_PATH", raising=False)
monkeypatch.delenv("ASCEND_TOOLKIT_HOME", raising=False)
monkeypatch.delenv("ASCEND_OPP_PATH", raising=False)
@patch('ttk.core_modules.npu.op.output_generation.OpInfoKeeper')
@patch('ttk.core_modules.npu.op.output_generation.get_global_storage')
@patch('ttk.core_modules.npu.op.output_generation.get_plugin_function')
class TestKernelBuiltinGoldenDispatch:
"""KERNEL_GOLDEN[op] → numpy ufunc → numeric result (not a sentinel)."""
def test_neg_resolves_and_returns_numeric_array(self, mock_get_plugin, mock_sw, mock_op_info):
"""`neg` (numpy.negative, unary) end-to-end through __generate_golden."""
mock_get_plugin.return_value = None
mock_sw.return_value = _mock_switches()
mock_op_info.return_value.info_of.return_value = {"inputs": []}
case = _make_testcase(op_name="neg", input_shapes=((4,),),
input_dtypes=("float32",),
output_shapes=((4,),),
output_dtypes=("float32",))
assert "neg" in KERNEL_GOLDEN
assert KERNEL_GOLDEN["neg"] is np.negative
out = np.array([1.0, -2.0, 3.0, -4.0], dtype="float32")
case.input_arrays = (out,)
golden = _generate_golden(case, ["float32"])
assert len(golden) >= 1
assert isinstance(golden[0], np.ndarray), \
f"expected ndarray, got sentinel/string: {golden[0]!r}"
np.testing.assert_array_equal(golden[0], np.array([-1.0, 2.0, -3.0, 4.0],
dtype="float32"))
mock_get_plugin.assert_called_once()
def test_acos_path_numeric_not_sentinel(self, mock_get_plugin, mock_sw, mock_op_info):
"""`acos` (numpy.arccos, unary) — second builtin op covers regression breadth."""
mock_get_plugin.return_value = None
mock_sw.return_value = _mock_switches()
mock_op_info.return_value.info_of.return_value = {"inputs": []}
case = _make_testcase(op_name="acos")
assert KERNEL_GOLDEN["acos"] is np.arccos
out = np.array([1.0, 0.0, 0.5], dtype="float32")
case.input_arrays = (out,)
golden = _generate_golden(case, ["float32"])
assert len(golden) >= 1
assert isinstance(golden[0], np.ndarray), \
f"expected ndarray, got sentinel/string: {golden[0]!r}"
np.testing.assert_allclose(golden[0], np.arccos(out), rtol=1e-5)
@patch('ttk.core_modules.npu.op.output_generation.get_plugin_function')
class TestKernelGoldenFallbackBranchCoverage:
"""Lighter-weight unit on the fallback decision itself.
Even without a fully-validated TestcaseOp we want to prove that, when
get_plugin_function yields None, KERNEL_GOLDEN still resolves the op
and the resolved callable is the documented numpy ufunc — i.e. the
regression surface (the dict + its wiring) is exercised directly.
"""
def test_kernel_golden_dict_resolves_builtin_ufuncs(self, _mock_get_plugin):
expected = {
"neg": np.negative,
"acos": np.arccos,
"floor_div": np.floor_divide,
"is_nan": np.isnan,
}
for op, ufunc in expected.items():
assert op in KERNEL_GOLDEN, f"builtin op {op!r} missing from KERNEL_GOLDEN"
assert KERNEL_GOLDEN[op] is ufunc, \
f"KERNEL_GOLDEN[{op!r}] is {KERNEL_GOLDEN[op]!r}, expected {ufunc!r}"
def test_builtin_ufunc_routes_to_numpy(self, _mock_get_plugin):
from ttk.utilities import framework_of
for op in ("neg", "acos", "floor_div"):
assert framework_of(KERNEL_GOLDEN[op]) == "numpy", \
f"{op} must classify as 'numpy' to hit the builtin path"