"""E2E: KERNEL → XPU dispatch chain against a local CPU XPU-Server stub.
Tests the FULL dispatch chain via direct function calls (no subprocess, no NPU):
_extract_spec_providers → EndpointView.resolve_providers → _build_spec
→ collect_xpu_results → dispatch_to_remote → standalone server → executor
→ _resolve_3party_api / spec-mode → result.
Covers:
- No spec (discovery): server-advertised providers (torch+tf).
- No spec + --provider torch: narrowed to torch only.
- Spec third_party = "torch.add" (single API string).
- Spec third_party = {"torch": ..., "tf": ...} (dict of API strings).
- Spec third_party = {"torch": AddImpl} (dict of impl class, spec mode).
- Spec third_party = None (spec exists, no third_party → fallback resolve).
No NPU needed — this tests the XPU dispatch chain, not the kernel compile.
"""
import http.client
import json
import os
import socket
import subprocess
import sys
import time
import numpy as np
import pytest
from ttk.core_modules.npu.op.profiling import _build_spec, _extract_spec_providers
from ttk.remote import DATA, PERF
from ttk.remote.endpoint_view import EndpointView
from ttk.remote.xpu_collector import collect_xpu_results
from ttk.test_spec import get_spec_attr, get_spec_class_meta
from ttk.utilities.singleton import Singleton
import importlib.util as _ilu
has_torch = _ilu.find_spec("torch") is not None
has_tf = _ilu.find_spec("tensorflow") is not None
if has_tf:
try:
has_tf = subprocess.run([sys.executable, "-c", "import tensorflow"],
capture_output=True, timeout=90).returncode == 0
except Exception:
has_tf = False
needs_torch = pytest.mark.skipif(not has_torch, reason="torch not installed")
needs_tf = pytest.mark.skipif(not has_tf, reason="tensorflow not installed")
needs_both = pytest.mark.skipif(not (has_torch and has_tf), reason="needs both torch+tf")
pytestmark = pytest.mark.e2e
_SPEC_DIR = os.path.join(os.path.dirname(__file__), "specs")
_TENANT_ID = "e2e_xpu"
def _free_port():
s = socket.socket()
s.bind(("127.0.0.1", 0))
p = s.getsockname()[1]
s.close()
return p
@pytest.fixture(scope="module")
def xpu_server():
"""Start a standalone XPU-Server (CPU stub, same as production deploy)."""
port = _free_port()
env = dict(os.environ)
ttk_remote = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "..", "ttk", "remote"))
env["PYTHONPATH"] = os.pathsep.join(
p for p in (ttk_remote, env.get("PYTHONPATH", "")) if p)
proc = subprocess.Popen(
[sys.executable, "-m", "server.xpu_server",
"--port", str(port), "--devices", "cpu"],
env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
for _ in range(20):
time.sleep(0.5)
try:
c = http.client.HTTPConnection("127.0.0.1", port, timeout=1)
c.request("GET", f"/v1/heartbeat?tenant_id={_TENANT_ID}")
c.getresponse().read()
c.close()
break
except (ConnectionRefusedError, OSError):
continue
yield port
proc.terminate()
try:
proc.wait(timeout=15)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait()
def _probe_heartbeat(xpu_port):
"""GET /v1/heartbeat?tenant_id=e2e_xpu once: registers tenant + returns
{providers, hardware}. Used to seed the health file for EndpointView."""
c = http.client.HTTPConnection("127.0.0.1", xpu_port, timeout=5)
c.request("GET", f"/v1/heartbeat?tenant_id={_TENANT_ID}")
resp = c.getresponse()
body = json.loads(resp.read())
c.close()
return body
def _dispatch(xpu_port, tmp_path, monkeypatch, spec_subdir=None, cli_providers=None):
"""Build specs (via real EndpointView resolution) + dispatch to server.
Returns (results_dict, inputs_tuple).
`_dispatch` is a plain helper (no pytest fixtures of its own): tmp_path +
monkeypatch are passed in by the 6 caller tests so the health-file path +
env wiring are auto-cleaned per test.
"""
body = _probe_heartbeat(xpu_port)
providers = body.get("providers", [])
hardware = body.get("hardware", "")
health_path = str(tmp_path / "xpu_health.json")
ep_key = f"127.0.0.1:{xpu_port}"
os.makedirs(os.path.dirname(health_path), exist_ok=True)
with open(health_path, "w") as f:
json.dump({"endpoints": {
ep_key: {"alive": True, "providers": providers,
"hardware": hardware, "ts": time.time()}}}, f)
monkeypatch.setenv("TTK_XPU_HEALTH_PATH", health_path)
yaml_path = tmp_path / "ttk.conf.yaml"
yaml_path.write_text(
f"remote:\n endpoints:\n - host: 127.0.0.1\n port: {xpu_port}\n")
import ttk.config.loader as loader
from ttk.config.loader import load_config
load_config(str(yaml_path))
Singleton._instances.clear()
search = (os.path.join(_SPEC_DIR, spec_subdir),) if spec_subdir else ()
tp = get_spec_attr("add", "third_party", search) if search else None
meta = get_spec_class_meta("add", search) if search else None
spec_file = meta["spec_file"] if meta else None
spec_class = meta["class_name"] if meta else None
ev = EndpointView()
resolved = ev.resolve_providers(_extract_spec_providers(tp), cli_providers)
specs = [_build_spec(p, tp, spec_file, spec_class, "add", "Add") for p in resolved]
x = np.random.randn(4, 8).astype(np.float32)
y = np.random.randn(4, 8).astype(np.float32)
results = collect_xpu_results(
specs, inputs=[x, y], input_names=["x", "y"],
mode=DATA | PERF, tenant_id=_TENANT_ID,
op_name="add", op_type="Add",
)
return results, (x, y), (resolved[0] if resolved else None)
def _assert_pass(results, provider):
assert provider in results, f"{provider} missing from results {list(results)}"
assert results[provider]["status"] == "PASS", f"{provider}: {results[provider]}"
def _assert_priority_output(results, x, y, priority):
"""Priority provider (specs[0]) gets DATA in DATA|PERF → verify its output.
resolve_providers returns survivors in spec insertion order when a spec is
given (app-declared priority, spec §③), or sorted when no spec. priority is
resolved[0] in either case; we assert on the actual priority, not a hard-
coded provider name.
"""
assert priority is not None, "no provider resolved (priority unknown)"
outs = results[priority].get("outputs")
assert outs, f"priority {priority} has no outputs (expected DATA for priority)"
np.testing.assert_allclose(outs[0], np.add(x, y), rtol=1e-5,
err_msg=f"priority {priority} output mismatch")
@needs_both
def test_no_spec_discovery_both_providers(xpu_server, tmp_path, monkeypatch):
"""No spec, no --provider: both torch+tf dispatched + priority output verified."""
results, (x, y), priority = _dispatch(xpu_server, tmp_path, monkeypatch)
_assert_pass(results, "torch")
_assert_pass(results, "tf")
_assert_priority_output(results, x, y, priority)
@needs_torch
def test_no_spec_provider_torch_only(xpu_server, tmp_path, monkeypatch):
"""--provider torch on no-spec op: only torch dispatched (torch is priority)."""
results, (x, y), priority = _dispatch(xpu_server, tmp_path, monkeypatch,
cli_providers=["torch"])
_assert_pass(results, "torch")
assert "tf" not in results, f"tf should be absent (--provider torch): {list(results)}"
_assert_priority_output(results, x, y, priority)
@needs_tf
def test_no_spec_provider_tf_only(xpu_server, tmp_path, monkeypatch):
"""--provider tf on no-spec op: only tf dispatched (tf is priority)."""
results, (x, y), priority = _dispatch(xpu_server, tmp_path, monkeypatch,
cli_providers=["tf"])
_assert_pass(results, "tf")
assert "torch" not in results, f"torch should be absent (--provider tf): {list(results)}"
_assert_priority_output(results, x, y, priority)
@needs_torch
def test_spec_tp_str(xpu_server, tmp_path, monkeypatch):
"""third_party = 'torch.add' (single API string); torch is priority."""
results, (x, y), priority = _dispatch(xpu_server, tmp_path, monkeypatch,
spec_subdir="tp_str")
_assert_pass(results, "torch")
_assert_priority_output(results, x, y, priority)
@needs_both
def test_spec_tp_dict_api(xpu_server, tmp_path, monkeypatch):
"""third_party = {'torch': '...', 'tf': '...'} (dict of API strings)."""
results, (x, y), priority = _dispatch(xpu_server, tmp_path, monkeypatch,
spec_subdir="tp_dict_api")
_assert_pass(results, "torch")
_assert_pass(results, "tf")
_assert_priority_output(results, x, y, priority)
@needs_torch
def test_spec_tp_dict_impl(xpu_server, tmp_path, monkeypatch):
"""third_party = {'torch': AddImpl} (dict of impl class, spec mode)."""
results, (x, y), priority = _dispatch(xpu_server, tmp_path, monkeypatch,
spec_subdir="tp_dict_impl")
_assert_pass(results, "torch")
_assert_priority_output(results, x, y, priority)
@needs_both
def test_spec_tp_none_fallback(xpu_server, tmp_path, monkeypatch):
"""third_party = None: spec exists but no third_party → fallback resolve."""
results, (x, y), priority = _dispatch(xpu_server, tmp_path, monkeypatch,
spec_subdir="tp_none")
_assert_pass(results, "torch")
_assert_pass(results, "tf")
_assert_priority_output(results, x, y, priority)
@needs_both
def test_spec_tp_tensorflow_key(xpu_server, tmp_path, monkeypatch):
"""third_party = {'tensorflow': ..., 'torch': ...} → tensorflow 归一化为 tf."""
results, (x, y), priority = _dispatch(xpu_server, tmp_path, monkeypatch,
spec_subdir="tp_tensorflow_key")
_assert_pass(results, "torch")
_assert_pass(results, "tf")
_assert_priority_output(results, x, y, priority)
@needs_tf
def test_spec_tp_impl_tf(xpu_server, tmp_path, monkeypatch):
"""third_party = {'tf': AddTfImpl} (TF spec mode)."""
results, (x, y), priority = _dispatch(xpu_server, tmp_path, monkeypatch,
spec_subdir="tp_impl_tf")
_assert_pass(results, "tf")
_assert_priority_output(results, x, y, priority)
@needs_both
def test_hardware_profile_penetration(xpu_server, tmp_path, monkeypatch):
"""heartbeat hardware field flows through health file → EndpointView."""
body = _probe_heartbeat(xpu_server)
assert "hardware" in body, f"heartbeat missing hardware: {body}"
assert body["hardware"] == "cpu", f"expected cpu (no device), got {body['hardware']}"
assert "providers" in body, f"heartbeat missing providers: {body}"
assert "torch" in body["providers"], f"torch not in providers {body['providers']}"