Add jet, surface and fixed-patch tests and runnable examples
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""Context-stack logic with an explicit driver model; no CUDA initialization."""
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from types import ModuleType, SimpleNamespace
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
class ContextOwnership(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.stack = []
|
||||
stack = self.stack
|
||||
class Context:
|
||||
def __init__(self):
|
||||
self.detached = False
|
||||
stack.append(self)
|
||||
def push(self):
|
||||
if self.detached:
|
||||
raise RuntimeError("detached")
|
||||
stack.append(self)
|
||||
@staticmethod
|
||||
def pop():
|
||||
stack.pop()
|
||||
def detach(self):
|
||||
self.detached = True
|
||||
class Device:
|
||||
@staticmethod
|
||||
def count(): return 1
|
||||
def __init__(self, device): pass
|
||||
def make_context(self): return Context()
|
||||
driver = ModuleType("pycuda.driver")
|
||||
driver.Device, driver.init = Device, lambda: None
|
||||
pycuda = ModuleType("pycuda")
|
||||
pycuda.driver = driver
|
||||
path = Path(__file__).resolve().parents[2] / "src/CelerisLab/cuda/context.py"
|
||||
spec = importlib.util.spec_from_file_location("context_under_test", path)
|
||||
self.module = importlib.util.module_from_spec(spec)
|
||||
with patch.dict(sys.modules, {"pycuda": pycuda, "pycuda.driver": driver}):
|
||||
spec.loader.exec_module(self.module)
|
||||
|
||||
def test_interleaving_and_non_lifo_close_preserve_other_current_context(self):
|
||||
a, b = self.module.CudaContext(), self.module.CudaContext()
|
||||
self.assertEqual(self.stack, [])
|
||||
with b.activate():
|
||||
with a.activate():
|
||||
self.assertIs(self.stack[-1], a._ctx)
|
||||
self.assertIs(self.stack[-1], b._ctx)
|
||||
a.close() # closing A must not pop B
|
||||
self.assertIs(self.stack[-1], b._ctx)
|
||||
self.assertEqual(self.stack, [])
|
||||
b.close()
|
||||
b.close()
|
||||
|
||||
def test_exception_unwinds_and_active_close_is_rejected(self):
|
||||
a = self.module.CudaContext()
|
||||
with self.assertRaisesRegex(ValueError, "operation"):
|
||||
with a.activate():
|
||||
with self.assertRaises(RuntimeError):
|
||||
a.close()
|
||||
raise ValueError("operation")
|
||||
self.assertEqual(self.stack, [])
|
||||
a.close()
|
||||
with self.assertRaises(RuntimeError):
|
||||
with a.activate(): pass
|
||||
|
||||
def test_decorator_balances_nested_calls_and_supports_low_level_fields(self):
|
||||
bound = self.module.context_bound
|
||||
stack = self.stack
|
||||
class Owner:
|
||||
@bound
|
||||
def outer(self): return self.inner()
|
||||
@bound
|
||||
def inner(self): return stack[-1] if stack else None
|
||||
owner = Owner()
|
||||
self.assertIsNone(owner.outer())
|
||||
owner.ctx = self.module.CudaContext()
|
||||
self.assertIs(owner.outer(), owner.ctx._ctx)
|
||||
self.assertEqual(stack, [])
|
||||
owner.ctx.close()
|
||||
|
||||
def test_context_manager_owns_lifetime(self):
|
||||
with self.module.CudaContext() as ctx:
|
||||
self.assertIs(self.stack[-1], ctx._ctx)
|
||||
self.assertEqual(self.stack, [])
|
||||
self.assertIsNone(ctx._ctx)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user