Skip to content

Commit 0781d16

Browse files
committed
improve coverage of policy and manager
1 parent dcedc67 commit 0781d16

5 files changed

Lines changed: 559 additions & 0 deletions

File tree

.coverage

108 KB
Binary file not shown.

qa/L0_pytorch_unittest/test.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,21 @@ run_test_step "pytest_test_attention.xml" "$TE_PATH/tests/pytorch/attention/test
138138
run_test_step "pytest_test_checkpoint.xml" "$TE_PATH/tests/pytorch/test_checkpoint.py" \
139139
"NVTE_TEST_CHECKPOINT_ARTIFACT_PATH=$TE_PATH/artifacts/tests/pytorch/test_checkpoint python3 -m pytest --tb=auto --junitxml=$XML_LOG_DIR/pytest_test_checkpoint.xml $TE_PATH/tests/pytorch/test_checkpoint.py" "test_checkpoint.py"
140140

141+
# ==============================================================================
142+
# New Step: Plugin Core
143+
# ==============================================================================
144+
145+
# Step: Plugin Policy
146+
run_test_step "pytest_test_plugin_policy.xml" "$TE_PATH/tests/pytorch/test_plugin_policy.py" \
147+
"python3 -m pytest -s -v --tb=auto --junitxml=$XML_LOG_DIR/pytest_test_plugin_policy.xml $TE_PATH/tests/pytorch/test_plugin_policy.py" "test_plugin_policy.py"
148+
149+
# Step: Plugin manager
150+
run_test_step "pytest_test_plugin_manager.xml" "$TE_PATH/tests/pytorch/test_plugin_manager.py" \
151+
"python3 -m pytest -s -v --tb=auto --junitxml=$XML_LOG_DIR/pytest_test_plugin_manager.xml $TE_PATH/tests/pytorch/test_plugin_manager.py" "test_plugin_manager.py"
152+
153+
154+
# ==============================================================================
155+
141156

142157
if [ "$FAIL" -ne 0 ]; then
143158
echo "Some tests failed."

tests/pytorch/.coverage

52 KB
Binary file not shown.
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
import os
2+
import pytest
3+
from unittest.mock import MagicMock, patch
4+
5+
from transformer_engine.plugin.core.types import BackendImplKind, OpImpl
6+
from transformer_engine.plugin.core.policy import SelectionPolicy
7+
from transformer_engine.plugin.core.registry import OpRegistry
8+
from transformer_engine.plugin.core.manager import (
9+
OpManager,
10+
get_default_manager,
11+
reset_default_manager,
12+
)
13+
14+
15+
# ==============================================================================
16+
# Fixtures & Mock Component Factories
17+
# ==============================================================================
18+
19+
@pytest.fixture(autouse=True)
20+
def clean_manager_singleton():
21+
"""Ensure a freshly cleared manager instance before and after each test."""
22+
reset_default_manager()
23+
yield
24+
reset_default_manager()
25+
26+
27+
def create_mock_impl(impl_id, kind, op_name="test_op", fn=None, priority=1, vendor=None):
28+
"""
29+
Factory to generate fully structured OpImpl instances for control injection.
30+
Ensures that VENDOR kinds satisfy internal post-init constraint validations.
31+
"""
32+
mock_fn = fn or MagicMock(return_value=f"res_{impl_id}")
33+
34+
# Satisfy __post_init__ requirement: VENDOR kind must specify a vendor name
35+
if kind == BackendImplKind.VENDOR and not vendor:
36+
vendor = "nvidia"
37+
38+
impl = OpImpl(
39+
op_name=op_name,
40+
impl_id=impl_id,
41+
kind=kind,
42+
fn=mock_fn,
43+
priority=priority,
44+
vendor=vendor
45+
)
46+
return impl
47+
48+
49+
# ==============================================================================
50+
# Part 1: Initialization, Fork Safety & Global Singleton Management
51+
# ==============================================================================
52+
53+
def test_manager_singleton_lifecycle():
54+
"""Verify singleton access, reset primitives, and Windows register_at_fork guards."""
55+
mgr1 = get_default_manager()
56+
mgr2 = get_default_manager()
57+
assert mgr1 is mgr2
58+
59+
# Force error branch covering missing register_at_fork (e.g. Windows platforms)
60+
with patch("os.register_at_fork", side_effect=AttributeError):
61+
custom_mgr = OpManager()
62+
assert custom_mgr is not None
63+
64+
65+
def test_lazy_initialization_flow():
66+
"""Trigger ensure_initialized, checking registry synchronization and tracking logs."""
67+
mock_registry = OpRegistry()
68+
mgr = OpManager(registry=mock_registry)
69+
70+
assert mgr.registry is mock_registry
71+
72+
mgr.ensure_initialized()
73+
assert mgr._state.initialized is True
74+
assert mgr._state.init_pid == os.getpid()
75+
76+
mgr.ensure_initialized()
77+
78+
79+
def test_process_fork_invalidation_handling():
80+
"""Force execute _reset_after_fork to clear transient states and step up policy epochs."""
81+
mgr = OpManager()
82+
mgr.ensure_initialized()
83+
84+
mgr._dispatch_cache[("op", "fp", 0)] = lambda: None
85+
mgr._impl_cache["op"] = MagicMock()
86+
87+
mgr._reset_after_fork()
88+
89+
assert mgr._state.initialized is False
90+
assert mgr._state.init_pid == -1
91+
assert len(mgr._dispatch_cache) == 0
92+
assert len(mgr._impl_cache) == 0
93+
94+
95+
# ==============================================================================
96+
# Part 2: Vendor Whitelist/Blacklist Filtering Engine
97+
# ==============================================================================
98+
99+
def test_vendor_policy_filter_matching():
100+
"""Trigger _matches_vendor_filters evaluating valid, blocked, and non-vendor impls."""
101+
mgr = OpManager()
102+
103+
non_vendor_impl = create_mock_impl("ref", BackendImplKind.REFERENCE)
104+
nvidia_impl = create_mock_impl("nv", BackendImplKind.VENDOR, vendor="nvidia")
105+
amd_impl = create_mock_impl("amd", BackendImplKind.VENDOR, vendor="amd")
106+
107+
# Manually instantiate a VENDOR bypass to simulate missing vendor string if allowed by logic
108+
# Direct instantiation bypassed since it would hit __post_init__ error otherwise
109+
with patch.object(OpImpl, "__post_init__", return_value=None):
110+
vendor_no_name = OpImpl(
111+
op_name="test_op",
112+
impl_id="vend_none",
113+
kind=BackendImplKind.VENDOR,
114+
fn=MagicMock(),
115+
priority=1,
116+
vendor=None
117+
)
118+
119+
# Scenario 1: Deny List Filtering
120+
policy_deny = SelectionPolicy.from_dict(deny_vendors={"amd"})
121+
assert mgr._matches_vendor_filters(non_vendor_impl, policy_deny) is True
122+
assert mgr._matches_vendor_filters(vendor_no_name, policy_deny) is False
123+
assert mgr._matches_vendor_filters(nvidia_impl, policy_deny) is True
124+
assert mgr._matches_vendor_filters(amd_impl, policy_deny) is False
125+
126+
# Scenario 2: Allow Whitelist Filtering
127+
policy_allow = SelectionPolicy.from_dict(allow_vendors={"nvidia"})
128+
assert mgr._matches_vendor_filters(nvidia_impl, policy_allow) is True
129+
assert mgr._matches_vendor_filters(amd_impl, policy_allow) is False
130+
131+
132+
# ==============================================================================
133+
# Part 3: Resolver Pipelines and Resolution Error Fallbacks
134+
# ==============================================================================
135+
136+
def test_resolve_with_cache_and_priority():
137+
"""Test operational resolve pathways, cache hits, priority sorting and empty states."""
138+
mock_registry = OpRegistry()
139+
mgr = OpManager(registry=mock_registry)
140+
141+
impl_low = create_mock_impl("v1", BackendImplKind.VENDOR, op_name="test_op", priority=1, vendor="nvidia")
142+
impl_high = create_mock_impl("v2", BackendImplKind.VENDOR, op_name="test_op", priority=10, vendor="nvidia")
143+
144+
mock_registry.register_impl(impl_low)
145+
mock_registry.register_impl(impl_high)
146+
147+
# Safe patch of object method on frozen dataclasses to return True
148+
with patch.object(OpImpl, "is_available", return_value=True):
149+
selected_fn = mgr.resolve("test_op")
150+
assert selected_fn == impl_high.fn
151+
assert mgr.get_selected_impl_id("test_op") == "v2"
152+
assert mgr.resolve("test_op") == selected_fn
153+
154+
155+
def test_resolution_failures_and_strict_modes():
156+
"""Provoke exception blocks when operators are missing or filtered out."""
157+
mock_registry = OpRegistry()
158+
mgr = OpManager(registry=mock_registry)
159+
160+
# nonexistent operator
161+
with pytest.raises(RuntimeError, match="No available implementation"):
162+
mgr.resolve("ghost_op")
163+
164+
with pytest.raises(RuntimeError, match="No available implementation"):
165+
mgr.resolve_candidates("ghost_op")
166+
167+
# availability check failure
168+
broken_impl = create_mock_impl(
169+
"broken",
170+
BackendImplKind.REFERENCE,
171+
op_name="broken_op",
172+
)
173+
mock_registry.register_impl(broken_impl)
174+
175+
with patch.object(
176+
OpImpl,
177+
"is_available",
178+
side_effect=Exception("HW Missing"),
179+
):
180+
with pytest.raises(RuntimeError, match="No available implementation"):
181+
mgr.resolve("broken_op")
182+
183+
# vendor policy filters out all candidates
184+
amd_impl = create_mock_impl(
185+
"amd_impl",
186+
BackendImplKind.VENDOR,
187+
op_name="strict_op",
188+
vendor="amd",
189+
)
190+
191+
mock_registry.register_impl(amd_impl)
192+
193+
policy = SelectionPolicy.from_dict(
194+
allow_vendors={"nvidia"},
195+
strict=True,
196+
)
197+
198+
with patch(
199+
"transformer_engine.plugin.core.manager.get_policy",
200+
return_value=policy,
201+
):
202+
with patch.object(OpImpl, "is_available", return_value=True):
203+
with pytest.raises(
204+
RuntimeError,
205+
match="No available implementation",
206+
):
207+
mgr.resolve("strict_op")
208+
209+
210+
# ==============================================================================
211+
# Part 4: High-Level Core Dispatch Invokers (call & fallback)
212+
# ==============================================================================
213+
214+
def test_call_with_fallback_and_invalidation():
215+
"""Route execution patterns through standard invoke, caching, errors, and fallbacks."""
216+
217+
# ------------------------------------------------------------------
218+
# Case 1:
219+
# vendor implementation fails
220+
# reference implementation succeeds (fallback path)
221+
# ------------------------------------------------------------------
222+
registry = OpRegistry()
223+
mgr = OpManager(registry=registry)
224+
225+
primary_impl = create_mock_impl(
226+
"v1",
227+
BackendImplKind.VENDOR,
228+
op_name="fallback_op",
229+
vendor="nvidia",
230+
)
231+
primary_impl.fn.side_effect = Exception("CUDA Out of Memory")
232+
233+
backup_impl = create_mock_impl(
234+
"ref",
235+
BackendImplKind.REFERENCE,
236+
op_name="fallback_op",
237+
)
238+
239+
registry.register_impl(primary_impl)
240+
registry.register_impl(backup_impl)
241+
242+
with patch.object(OpImpl, "is_available", return_value=True):
243+
result = mgr.call("fallback_op", 10, x=5)
244+
245+
assert result == "res_ref"
246+
247+
backup_impl.fn.assert_called_once_with(
248+
10,
249+
x=5,
250+
)
251+
252+
assert mgr._get_last_impl_id("fallback_op") == "ref"
253+
254+
# ------------------------------------------------------------------
255+
# Case 2:
256+
# strict mode (TE_FL_STRICT=0)
257+
# fallback disabled
258+
# vendor implementation failure should propagate directly
259+
# ------------------------------------------------------------------
260+
strict_registry = OpRegistry()
261+
262+
failing_impl = create_mock_impl(
263+
"strict_vendor",
264+
BackendImplKind.VENDOR,
265+
op_name="strict_op",
266+
vendor="nvidia",
267+
)
268+
269+
failing_impl.fn.side_effect = Exception("CUDA Out of Memory")
270+
271+
strict_registry.register_impl(failing_impl)
272+
273+
strict_mgr = OpManager(registry=strict_registry)
274+
275+
with patch("os.getenv", return_value="0"):
276+
with patch.object(OpImpl, "is_available", return_value=True):
277+
with pytest.raises(Exception, match="CUDA Out of Memory"):
278+
strict_mgr.call("strict_op")
279+
280+
# ==============================================================================
281+
# Part 5: Cache Stability and Helper Primitives
282+
# ==============================================================================
283+
284+
def test_cache_validation_and_epoch_bumps():
285+
"""Cover _is_cache_valid, _update_cache and bump_policy_epoch."""
286+
mgr = OpManager()
287+
288+
assert mgr._is_cache_valid("unknown_op") is False
289+
290+
impl = create_mock_impl(
291+
"v1",
292+
BackendImplKind.VENDOR,
293+
op_name="validated_op",
294+
vendor="nvidia",
295+
)
296+
297+
mgr._update_cache("validated_op", impl)
298+
299+
assert mgr._is_cache_valid("validated_op") is True
300+
301+
mgr.bump_policy_epoch()
302+
303+
assert mgr._is_cache_valid("validated_op") is False
304+
305+
assert mgr._get_last_impl_id("validated_op") == "v1"
306+
307+
308+
def test_get_selected_impl_id():
309+
"""Verify selected impl id lookup through resolve()."""
310+
311+
registry = OpRegistry()
312+
313+
impl = create_mock_impl(
314+
"v1",
315+
BackendImplKind.VENDOR,
316+
op_name="validated_op",
317+
vendor="nvidia",
318+
)
319+
320+
registry.register_impl(impl)
321+
322+
mgr = OpManager(registry=registry)
323+
324+
with patch.object(mgr, "ensure_initialized"):
325+
with patch.object(OpImpl, "is_available", return_value=True):
326+
assert mgr.get_selected_impl_id("validated_op") == "v1"

0 commit comments

Comments
 (0)