# SPDX-License-Identifier: Apache-2.0 """ Unit tests for the L2 adapter factory registry and PluginL2AdapterConfig. """ # Standard import os import tempfile import types # Third Party import pytest # First Party from lmcache.v1.distributed.l2_adapters.config import ( L2AdapterConfigBase, get_type_name_for_config, ) from lmcache.v1.distributed.l2_adapters.factory import ( create_l2_adapter_from_registry, register_l2_adapter_factory, ) from lmcache.v1.distributed.l2_adapters.mock_l2_adapter import MockL2AdapterConfig from lmcache.v1.distributed.l2_adapters.plugin_l2_adapter import PluginL2AdapterConfig from lmcache.v1.distributed.l2_adapters.raw_block_l2_adapter import ( RawBlockL2AdapterConfig, ) from lmcache.v1.platform import create_event_notifier def _has_raw_block_ext() -> bool: try: # Third Party import lmcache_rust_raw_block_io # noqa: F401 return True except Exception: return False # ========================================================= # Helpers # ========================================================= class _FakeL2Adapter: """Minimal stub that passes issubclass check.""" def __init__(self, params=None, **kwargs): self.params = params self.kwargs = kwargs class _NotAnAdapter: """Class that does NOT subclass L2AdapterInterface.""" pass def _make_fake_module( adapter_cls: type, class_name: str = "FakeL2Adapter", ) -> types.ModuleType: """Create a fake Python module containing *adapter_cls*.""" mod = types.ModuleType("fake_l2_module") setattr(mod, class_name, adapter_cls) return mod # ========================================================= # Tests: factory registry basics # ========================================================= class TestFactoryRegistry: """Tests for register/create via factory registry.""" def test_mock_factory_is_registered(self): """Mock adapter factory should be auto-registered on import.""" config = MockL2AdapterConfig( max_size_gb=0.001, mock_bandwidth_gb=10.0, ) name = get_type_name_for_config(config) assert name == "mock" def test_raw_block_factory_is_registered(self): config = RawBlockL2AdapterConfig( device_path="/tmp/raw-block-dev", slot_bytes=64 * 1024, use_odirect=False, meta_enable_periodic=False, ) name = get_type_name_for_config(config) assert name == "raw_block" def test_create_mock_via_registry(self): """create_l2_adapter_from_registry creates a MockL2Adapter.""" # First Party from lmcache.v1.distributed.l2_adapters.mock_l2_adapter import ( MockL2Adapter, ) config = MockL2AdapterConfig( max_size_gb=0.001, mock_bandwidth_gb=10.0, ) adapter = create_l2_adapter_from_registry(config) assert isinstance(adapter, MockL2Adapter) adapter.close() @pytest.mark.skipif( not _has_raw_block_ext(), reason="lmcache_rust_raw_block_io extension not installed", ) def test_create_raw_block_via_registry(self): # First Party from lmcache.v1.distributed.l2_adapters.raw_block_l2_adapter import ( RawBlockL2Adapter, ) with tempfile.TemporaryDirectory() as td: dev_path = os.path.join(td, "dev.bin") with open(dev_path, "wb") as f: f.truncate(8 * 1024 * 1024) config = RawBlockL2AdapterConfig( device_path=dev_path, slot_bytes=64 * 1024, use_odirect=False, meta_total_bytes=1 * 1024 * 1024, meta_enable_periodic=False, ) adapter = create_l2_adapter_from_registry(config) assert isinstance(adapter, RawBlockL2Adapter) adapter.close() def test_duplicate_factory_raises(self): """Registering the same factory name twice should raise ValueError.""" with pytest.raises(ValueError, match="already registered"): register_l2_adapter_factory("mock", lambda c, **kw: None) def test_unknown_config_raises(self): """Config class not in the registry should fail.""" class _UnknownConfig: pass with pytest.raises(ValueError, match="Unregistered"): get_type_name_for_config(_UnknownConfig()) # ========================================================= # Tests: PluginL2AdapterConfig parsing # ========================================================= class TestPluginL2AdapterConfig: """Tests for PluginL2AdapterConfig.from_dict.""" def test_valid_config(self): d = { "type": "plugin", "module_path": "my.module", "class_name": "MyAdapter", "adapter_params": {"host": "localhost"}, } cfg = PluginL2AdapterConfig.from_dict(d) assert cfg.module_path == "my.module" assert cfg.class_name == "MyAdapter" assert cfg.adapter_params == {"host": "localhost"} def test_default_adapter_params(self): d = { "type": "plugin", "module_path": "my.module", "class_name": "MyAdapter", } cfg = PluginL2AdapterConfig.from_dict(d) assert cfg.adapter_params == {} def test_missing_module_path_raises(self): d = {"type": "plugin", "class_name": "X"} with pytest.raises(ValueError, match="module_path"): PluginL2AdapterConfig.from_dict(d) def test_missing_class_name_raises(self): d = {"type": "plugin", "module_path": "x"} with pytest.raises(ValueError, match="class_name"): PluginL2AdapterConfig.from_dict(d) def test_invalid_adapter_params_raises(self): d = { "type": "plugin", "module_path": "x", "class_name": "X", "adapter_params": "not_a_dict", } with pytest.raises(ValueError, match="adapter_params"): PluginL2AdapterConfig.from_dict(d) # ========================================================= # Tests: plugin adapter factory dynamic loading # ========================================================= class TestPluginAdapterFactory: """Tests for the plugin adapter factory using monkeypatch to mock importlib.import_module.""" def test_load_external_adapter(self, monkeypatch): """Successfully load an external adapter class.""" # First Party from lmcache.v1.distributed.l2_adapters import plugin_l2_adapter as plugin_mod # Make _FakeL2Adapter pass issubclass check monkeypatch.setattr( plugin_mod, "_L2AI", _FakeL2Adapter, ) fake_mod = _make_fake_module(_FakeL2Adapter) monkeypatch.setattr( "importlib.import_module", lambda path: fake_mod, ) config = PluginL2AdapterConfig( module_path="fake_l2_module", class_name="FakeL2Adapter", adapter_params={"host": "localhost"}, ) adapter = create_l2_adapter_from_registry(config) assert isinstance(adapter, _FakeL2Adapter) assert adapter.params["host"] == "localhost" def test_import_error_raises(self, monkeypatch): """ImportError propagates when module not found.""" monkeypatch.setattr( "importlib.import_module", lambda p: (_ for _ in ()).throw(ImportError("no such module")), ) config = PluginL2AdapterConfig( module_path="nonexistent", class_name="X", ) with pytest.raises(ImportError, match="nonexistent"): create_l2_adapter_from_registry(config) def test_missing_class_raises(self, monkeypatch): """AttributeError when class not in module.""" fake_mod = types.ModuleType("empty_mod") monkeypatch.setattr( "importlib.import_module", lambda p: fake_mod, ) config = PluginL2AdapterConfig( module_path="empty_mod", class_name="NoSuchClass", ) with pytest.raises(AttributeError, match="NoSuchClass"): create_l2_adapter_from_registry(config) def test_not_subclass_raises(self, monkeypatch): """TypeError when class is not an L2AdapterInterface subclass.""" fake_mod = _make_fake_module(_NotAnAdapter, "BadAdapter") monkeypatch.setattr( "importlib.import_module", lambda p: fake_mod, ) config = PluginL2AdapterConfig( module_path="fake_mod", class_name="BadAdapter", ) with pytest.raises(TypeError, match="not a subclass"): create_l2_adapter_from_registry(config) # ========================================================= # Tests: plugin registration and initialization # ========================================================= class _FakeL2AdapterWithDesc: """Stub that records l1_memory_desc.""" def __init__(self, config, **kwargs): self.config = config self.l1_memory_desc = kwargs.get("l1_memory_desc") class _FakeConfig(L2AdapterConfigBase): """Minimal config subclass for discovery tests.""" def __init__(self, **kwargs): self.kwargs = kwargs @classmethod def from_dict(cls, d: dict) -> "_FakeConfig": return cls(**d) @classmethod def help(cls) -> str: return "fake" class TestPluginRegistration: """Verify plugin self-registration on import.""" def test_plugin_type_registered(self): """'plugin' config type should be registered.""" cfg = PluginL2AdapterConfig(module_path="x", class_name="X") name = get_type_name_for_config(cfg) assert name == "plugin" def test_plugin_factory_registered(self): """'plugin' factory should be callable via create_l2_adapter_from_registry (smoke test with ImportError).""" cfg = PluginL2AdapterConfig( module_path="nonexistent.module", class_name="X", ) with pytest.raises(ImportError): create_l2_adapter_from_registry(cfg) class TestPluginInitialization: """Verify config-class discovery and l1_memory_desc forwarding during plugin initialization.""" def _patch_base(self, monkeypatch, adapter_cls): """Make *adapter_cls* pass issubclass check.""" # First Party from lmcache.v1.distributed.l2_adapters import plugin_l2_adapter as plugin_mod monkeypatch.setattr( plugin_mod, "_L2AI", adapter_cls, ) # -- l1_memory_desc forwarding -- def test_l1_memory_desc_forwarded(self, monkeypatch): """l1_memory_desc should reach the adapter constructor when provided.""" self._patch_base(monkeypatch, _FakeL2AdapterWithDesc) fake_mod = _make_fake_module( _FakeL2AdapterWithDesc, "Adapter", ) monkeypatch.setattr( "importlib.import_module", lambda _: fake_mod, ) cfg = PluginL2AdapterConfig( module_path="m", class_name="Adapter", adapter_params={"k": "v"}, ) sentinel = object() adapter = create_l2_adapter_from_registry(cfg, l1_memory_desc=sentinel) assert adapter.l1_memory_desc is sentinel def test_l1_memory_desc_omitted(self, monkeypatch): """l1_memory_desc should be None when not provided.""" self._patch_base(monkeypatch, _FakeL2AdapterWithDesc) fake_mod = _make_fake_module( _FakeL2AdapterWithDesc, "Adapter", ) monkeypatch.setattr( "importlib.import_module", lambda _: fake_mod, ) cfg = PluginL2AdapterConfig( module_path="m", class_name="Adapter", ) adapter = create_l2_adapter_from_registry(cfg) assert adapter.l1_memory_desc is None # -- config class discovery -- def test_init_with_explicit_config_class(self, monkeypatch): """Explicit config_class_name should be used.""" self._patch_base(monkeypatch, _FakeL2AdapterWithDesc) fake_mod = _make_fake_module( _FakeL2AdapterWithDesc, "Adapter", ) fake_mod.MyCfg = _FakeConfig # type: ignore[attr-defined] monkeypatch.setattr( "importlib.import_module", lambda _: fake_mod, ) cfg = PluginL2AdapterConfig( module_path="m", class_name="Adapter", config_class_name="MyCfg", adapter_params={"x": 1}, ) adapter = create_l2_adapter_from_registry(cfg) assert isinstance(adapter.config, _FakeConfig) assert adapter.config.kwargs == {"x": 1} def test_init_with_convention_config_class(self, monkeypatch): """Config class discovered via ClassName+'Config' convention.""" self._patch_base(monkeypatch, _FakeL2AdapterWithDesc) fake_mod = _make_fake_module( _FakeL2AdapterWithDesc, "Adapter", ) # Convention: "Adapter" + "Config" fake_mod.AdapterConfig = _FakeConfig # type: ignore[attr-defined] monkeypatch.setattr( "importlib.import_module", lambda _: fake_mod, ) cfg = PluginL2AdapterConfig( module_path="m", class_name="Adapter", adapter_params={"y": 2}, ) adapter = create_l2_adapter_from_registry(cfg) assert isinstance(adapter.config, _FakeConfig) def test_init_fallback_raw_dict(self, monkeypatch): """When no config class is found, adapter receives a raw dict.""" self._patch_base(monkeypatch, _FakeL2AdapterWithDesc) fake_mod = _make_fake_module( _FakeL2AdapterWithDesc, "Adapter", ) monkeypatch.setattr( "importlib.import_module", lambda _: fake_mod, ) cfg = PluginL2AdapterConfig( module_path="m", class_name="Adapter", adapter_params={"z": 3}, ) adapter = create_l2_adapter_from_registry(cfg) assert isinstance(adapter.config, dict) assert adapter.config == {"z": 3} # ========================================================= # Tests: lazy loading (add_pending_module, # ensure_adapter_loaded, load_all_adapters, # get_all_registered_names) # ========================================================= class TestLazyLoading: """Tests for the deferred module import mechanism.""" @pytest.fixture(autouse=True) def _isolate_registry(self, monkeypatch): """Snapshot and restore the factory registry and pending-module list around each test.""" # First Party from lmcache.v1.distributed.l2_adapters import factory as fmod orig_reg = fmod._L2_ADAPTER_FACTORY_REGISTRY.copy() orig_pending = list(fmod._PENDING_MODULES) # Start each test with a clean slate fmod._PENDING_MODULES.clear() yield fmod._L2_ADAPTER_FACTORY_REGISTRY.clear() fmod._L2_ADAPTER_FACTORY_REGISTRY.update(orig_reg) fmod._PENDING_MODULES.clear() fmod._PENDING_MODULES.extend(orig_pending) # -- add_pending_module -- def test_add_pending_module_appends(self): """Module path is appended to the pending list.""" # First Party from lmcache.v1.distributed.l2_adapters.factory import ( _PENDING_MODULES, add_pending_module, ) add_pending_module("fake.mod.a") assert "fake.mod.a" in _PENDING_MODULES def test_add_pending_module_dedup(self): """Duplicate paths are not added twice.""" # First Party from lmcache.v1.distributed.l2_adapters.factory import ( _PENDING_MODULES, add_pending_module, ) add_pending_module("fake.mod.b") add_pending_module("fake.mod.b") assert _PENDING_MODULES.count("fake.mod.b") == 1 # -- ensure_adapter_loaded -- def test_ensure_already_registered(self): """No import happens when name is already in the registry.""" # First Party from lmcache.v1.distributed.l2_adapters.factory import ( _L2_ADAPTER_FACTORY_REGISTRY, _PENDING_MODULES, ensure_adapter_loaded, ) _L2_ADAPTER_FACTORY_REGISTRY["pre"] = lambda c, d: None _PENDING_MODULES.append("should.not.import") ensure_adapter_loaded("pre") # Pending list untouched assert "should.not.import" in _PENDING_MODULES def test_ensure_imports_until_found(self, monkeypatch): """Modules are imported one-by-one until the requested name appears in the registry.""" # First Party from lmcache.v1.distributed.l2_adapters import factory as fmod imported: list[str] = [] def _fake_import(path): imported.append(path) if path == "mod_b": fmod._L2_ADAPTER_FACTORY_REGISTRY["lazy_b"] = lambda c, d: None monkeypatch.setattr("importlib.import_module", _fake_import) fmod._PENDING_MODULES.extend(["mod_a", "mod_b", "mod_c"]) fmod.ensure_adapter_loaded("lazy_b") assert imported == ["mod_a", "mod_b"] # mod_c should remain pending assert "mod_c" in fmod._PENDING_MODULES def test_ensure_raises_last_import_error(self, monkeypatch): """When all pending modules fail, the last ImportError is raised.""" # First Party from lmcache.v1.distributed.l2_adapters import factory as fmod def _fail_import(path): raise ImportError(path) monkeypatch.setattr("importlib.import_module", _fail_import) fmod._PENDING_MODULES.extend(["bad_a", "bad_b"]) with pytest.raises(ImportError, match="bad_b"): fmod.ensure_adapter_loaded("missing") def test_ensure_no_error_when_not_found_silently(self, monkeypatch): """When pending modules import OK but name is still missing, no error is raised (no last_err). """ # First Party from lmcache.v1.distributed.l2_adapters import factory as fmod monkeypatch.setattr("importlib.import_module", lambda p: None) fmod._PENDING_MODULES.extend(["ok_a", "ok_b"]) # Should not raise fmod.ensure_adapter_loaded("nonexistent") # -- load_all_adapters -- def test_load_all_adapters_imports_everything(self, monkeypatch): """All pending modules are imported.""" # First Party from lmcache.v1.distributed.l2_adapters import factory as fmod imported: list[str] = [] monkeypatch.setattr( "importlib.import_module", lambda p: imported.append(p), ) fmod._PENDING_MODULES.extend(["m1", "m2", "m3"]) fmod.load_all_adapters() assert imported == ["m1", "m2", "m3"] assert len(fmod._PENDING_MODULES) == 0 def test_load_all_adapters_skips_failures(self, monkeypatch): """Modules that fail to import are silently skipped.""" # First Party from lmcache.v1.distributed.l2_adapters import factory as fmod imported: list[str] = [] def _selective_import(path): if path == "bad": raise ImportError(path) imported.append(path) monkeypatch.setattr("importlib.import_module", _selective_import) fmod._PENDING_MODULES.extend(["good1", "bad", "good2"]) fmod.load_all_adapters() assert imported == ["good1", "good2"] assert len(fmod._PENDING_MODULES) == 0 # -- get_all_registered_names -- def test_get_all_registered_names_sorted(self, monkeypatch): """Returns sorted list after loading all pending modules.""" # First Party from lmcache.v1.distributed.l2_adapters import factory as fmod def _register_import(path): fmod._L2_ADAPTER_FACTORY_REGISTRY[path] = lambda c, d: None monkeypatch.setattr("importlib.import_module", _register_import) fmod._PENDING_MODULES.extend(["z_mod", "a_mod"]) names = fmod.get_all_registered_names() # Should contain at least the two we just added assert "z_mod" in names assert "a_mod" in names # Must be sorted assert names == sorted(names) def test_lazy_module_not_in_sys_modules(self, monkeypatch): """Pending modules are NOT in sys.modules until ensure_adapter_loaded triggers the import.""" # Standard import sys # First Party from lmcache.v1.distributed.l2_adapters import factory as fmod sentinel = "test.lazy.sentinel.module" fmod._PENDING_MODULES.append(sentinel) assert sentinel not in sys.modules def _fake_import(path): mod = types.ModuleType(path) sys.modules[path] = mod fmod._L2_ADAPTER_FACTORY_REGISTRY["sentinel"] = lambda c, d: None monkeypatch.setattr("importlib.import_module", _fake_import) fmod.ensure_adapter_loaded("sentinel") assert sentinel in sys.modules # Cleanup sys.modules.pop(sentinel, None) # ========================================================= # Tests: create_l2_adapter public API # ========================================================= class TestCreateL2Adapter: """Tests for the public create_l2_adapter function.""" def test_create_mock_adapter(self): """create_l2_adapter dispatches to MockL2Adapter.""" # First Party from lmcache.v1.distributed.l2_adapters import ( create_l2_adapter, ) from lmcache.v1.distributed.l2_adapters.mock_l2_adapter import ( MockL2Adapter, ) config = MockL2AdapterConfig( max_size_gb=0.001, mock_bandwidth_gb=10.0, ) adapter = create_l2_adapter(config) assert isinstance(adapter, MockL2Adapter) adapter.close() # ========================================================= # Tests: NativePluginL2AdapterConfig factory # ========================================================= class _FakeNativeConnector: """Minimal mock that satisfies the native connector interface check.""" def __init__(self, **kwargs): self.kwargs = kwargs def event_fd(self): return -1 def submit_batch_get(self, keys, memoryviews): return 0 def submit_batch_set(self, keys, memoryviews): return 0 def submit_batch_exists(self, keys): return 0 def drain_completions(self): return [] def close(self): pass class _MissingMethodConnector: """Connector missing the 'close' method.""" def event_fd(self): return -1 def submit_batch_get(self, keys, memoryviews): return 0 def submit_batch_set(self, keys, memoryviews): return 0 def submit_batch_exists(self, keys): return 0 def drain_completions(self): return [] class TestNativePluginFactory: """Tests for the native_plugin adapter factory.""" def test_load_native_connector(self, monkeypatch): """Successfully load a native connector.""" # First Party from lmcache.v1.distributed.l2_adapters.native_connector_l2_adapter import ( NativeConnectorL2Adapter, ) from lmcache.v1.distributed.l2_adapters.native_plugin_l2_adapter import ( NativePluginL2AdapterConfig, ) fake_mod = types.ModuleType("fake_native_mod") fake_mod.FakeClient = _FakeNativeConnector # type: ignore[attr-defined] monkeypatch.setattr( "importlib.import_module", lambda p: fake_mod, ) cfg = NativePluginL2AdapterConfig( module_path="fake_native_mod", class_name="FakeClient", adapter_params={"host": "localhost"}, ) adapter = create_l2_adapter_from_registry(cfg) assert isinstance(adapter, NativeConnectorL2Adapter) adapter.close() def test_import_error_raises(self, monkeypatch): """ImportError propagates when module not found.""" # First Party from lmcache.v1.distributed.l2_adapters.native_plugin_l2_adapter import ( NativePluginL2AdapterConfig, ) monkeypatch.setattr( "importlib.import_module", lambda p: (_ for _ in ()).throw(ImportError("no such module")), ) cfg = NativePluginL2AdapterConfig( module_path="nonexistent", class_name="X", ) with pytest.raises(ImportError, match="nonexistent"): create_l2_adapter_from_registry(cfg) def test_missing_class_raises(self, monkeypatch): """AttributeError when class not in module.""" # First Party from lmcache.v1.distributed.l2_adapters.native_plugin_l2_adapter import ( NativePluginL2AdapterConfig, ) fake_mod = types.ModuleType("empty_mod") monkeypatch.setattr( "importlib.import_module", lambda p: fake_mod, ) cfg = NativePluginL2AdapterConfig( module_path="empty_mod", class_name="NoSuchClass", ) with pytest.raises(AttributeError, match="NoSuchClass"): create_l2_adapter_from_registry(cfg) def test_missing_method_raises(self, monkeypatch): """TypeError when connector is missing a required method.""" # First Party from lmcache.v1.distributed.l2_adapters.native_plugin_l2_adapter import ( NativePluginL2AdapterConfig, ) fake_mod = types.ModuleType("incomplete_mod") fake_mod.Bad = _MissingMethodConnector # type: ignore[attr-defined] monkeypatch.setattr( "importlib.import_module", lambda p: fake_mod, ) cfg = NativePluginL2AdapterConfig( module_path="incomplete_mod", class_name="Bad", ) with pytest.raises(TypeError, match="close"): create_l2_adapter_from_registry(cfg) def test_adapter_params_forwarded(self, monkeypatch): """adapter_params are forwarded as kwargs to the connector class.""" # First Party from lmcache.v1.distributed.l2_adapters.native_connector_l2_adapter import ( NativeConnectorL2Adapter, ) from lmcache.v1.distributed.l2_adapters.native_plugin_l2_adapter import ( NativePluginL2AdapterConfig, ) fake_mod = types.ModuleType("param_mod") fake_mod.Client = _FakeNativeConnector # type: ignore[attr-defined] monkeypatch.setattr( "importlib.import_module", lambda p: fake_mod, ) cfg = NativePluginL2AdapterConfig( module_path="param_mod", class_name="Client", adapter_params={"host": "myhost", "port": 42}, ) adapter = create_l2_adapter_from_registry(cfg) assert isinstance(adapter, NativeConnectorL2Adapter) adapter.close() def test_native_plugin_type_registered(self): """'native_plugin' config type should be registered.""" # First Party from lmcache.v1.distributed.l2_adapters.native_plugin_l2_adapter import ( NativePluginL2AdapterConfig, ) cfg = NativePluginL2AdapterConfig( module_path="x", class_name="X", ) name = get_type_name_for_config(cfg) assert name == "native_plugin" # ========================================================= # Tests: FSNativeL2AdapterConfig factory # ========================================================= class _FakeLMCacheFSClient: """Mock for lmcache.lmcache_fs.LMCacheFSClient.""" def __init__( self, base_path, num_workers, relative_tmp_dir="", use_odirect=False, read_ahead_size=0, ): self.base_path = base_path self.num_workers = num_workers self.relative_tmp_dir = relative_tmp_dir self.use_odirect = use_odirect self.read_ahead_size = read_ahead_size self._efd = create_event_notifier() self._closed = False def event_fd(self): return self._efd.fileno() def submit_batch_get(self, keys, memoryviews): return 0 def submit_batch_set(self, keys, memoryviews): return 0 def submit_batch_exists(self, keys): return 0 def drain_completions(self): return [] def close(self): if not self._closed: self._closed = True self._efd.close() class TestFSNativeAdapterFactory: """Tests for the fs_native adapter factory.""" def test_factory_creates_adapter(self, monkeypatch): """Factory creates a NativeConnectorL2Adapter wrapping the FS client.""" # First Party # Re-register so registry points to patched fn from lmcache.v1.distributed.l2_adapters.factory import ( _L2_ADAPTER_FACTORY_REGISTRY, ) from lmcache.v1.distributed.l2_adapters.fs_native_l2_adapter import ( FSNativeL2AdapterConfig, ) from lmcache.v1.distributed.l2_adapters.native_connector_l2_adapter import ( NativeConnectorL2Adapter, ) old = _L2_ADAPTER_FACTORY_REGISTRY["fs_native"] _L2_ADAPTER_FACTORY_REGISTRY["fs_native"] = _patched_fs_factory try: cfg = FSNativeL2AdapterConfig( base_path="/tmp/lmcache_test_factory", num_workers=2, ) adapter = create_l2_adapter_from_registry(cfg) assert isinstance(adapter, NativeConnectorL2Adapter) adapter.close() finally: _L2_ADAPTER_FACTORY_REGISTRY["fs_native"] = old def test_factory_forwards_all_params(self, monkeypatch): """All config params are forwarded to the FS client constructor.""" # First Party from lmcache.v1.distributed.l2_adapters.fs_native_l2_adapter import ( FSNativeL2AdapterConfig, ) captured: dict = {} class _CaptureFSClient(_FakeLMCacheFSClient): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) captured["args"] = args captured["kwargs"] = kwargs # First Party from lmcache.v1.distributed.l2_adapters.factory import ( _L2_ADAPTER_FACTORY_REGISTRY, ) def _capture_factory(config, l1_memory_desc=None): assert isinstance(config, FSNativeL2AdapterConfig) client = _CaptureFSClient( config.base_path, config.num_workers, config.relative_tmp_dir, config.use_odirect, config.read_ahead_size or 0, ) # First Party from lmcache.v1.distributed.l2_adapters.native_connector_l2_adapter import ( NativeConnectorL2Adapter, ) return NativeConnectorL2Adapter(client) old = _L2_ADAPTER_FACTORY_REGISTRY["fs_native"] _L2_ADAPTER_FACTORY_REGISTRY["fs_native"] = _capture_factory try: cfg = FSNativeL2AdapterConfig( base_path="/data/kv", num_workers=8, relative_tmp_dir=".tmp", use_odirect=True, read_ahead_size=4096, ) adapter = create_l2_adapter_from_registry(cfg) assert captured["args"] == ( "/data/kv", 8, ".tmp", True, 4096, ) adapter.close() finally: _L2_ADAPTER_FACTORY_REGISTRY["fs_native"] = old def test_factory_import_error_raises(self, monkeypatch): """RuntimeError raised when C++ FS extension is not available.""" # First Party from lmcache.v1.distributed.l2_adapters.fs_native_l2_adapter import ( FSNativeL2AdapterConfig, ) # Patch the actual factory to simulate missing # C++ extension def _broken_import(name, *args, **kwargs): if name == "lmcache.lmcache_fs": raise ImportError("no C++ FS extension") return _original_import(name, *args, **kwargs) # Standard import builtins _original_import = builtins.__import__ monkeypatch.setattr(builtins, "__import__", _broken_import) cfg = FSNativeL2AdapterConfig( base_path="/tmp/test", ) with pytest.raises(RuntimeError, match="C\\+\\+ FS"): create_l2_adapter_from_registry(cfg) def test_fs_native_type_registered(self): """'fs_native' config type should be registered.""" # First Party from lmcache.v1.distributed.l2_adapters.fs_native_l2_adapter import ( FSNativeL2AdapterConfig, ) cfg = FSNativeL2AdapterConfig( base_path="/tmp/test", ) name = get_type_name_for_config(cfg) assert name == "fs_native" def test_fs_native_factory_registered(self): """'fs_native' factory should be callable via create_l2_adapter_from_registry (smoke test with RuntimeError from missing C++ ext).""" # First Party from lmcache.v1.distributed.l2_adapters.fs_native_l2_adapter import ( FSNativeL2AdapterConfig, ) cfg = FSNativeL2AdapterConfig( base_path="/tmp/test_smoke", ) # Without the C++ extension built, expect # RuntimeError try: adapter = create_l2_adapter_from_registry(cfg) adapter.close() except RuntimeError: pass # Expected when C++ ext not built def test_read_ahead_size_none_becomes_zero(self, monkeypatch): """read_ahead_size=None should be passed as 0 to the native client.""" # First Party from lmcache.v1.distributed.l2_adapters.fs_native_l2_adapter import ( FSNativeL2AdapterConfig, ) captured: dict = {} class _CaptureFSClient2(_FakeLMCacheFSClient): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) captured["read_ahead"] = args[4] # First Party from lmcache.v1.distributed.l2_adapters.factory import ( _L2_ADAPTER_FACTORY_REGISTRY, ) def _capture_factory2(config, l1_memory_desc=None): assert isinstance(config, FSNativeL2AdapterConfig) client = _CaptureFSClient2( config.base_path, config.num_workers, config.relative_tmp_dir, config.use_odirect, config.read_ahead_size or 0, ) # First Party from lmcache.v1.distributed.l2_adapters.native_connector_l2_adapter import ( NativeConnectorL2Adapter, ) return NativeConnectorL2Adapter(client) old = _L2_ADAPTER_FACTORY_REGISTRY["fs_native"] _L2_ADAPTER_FACTORY_REGISTRY["fs_native"] = _capture_factory2 try: cfg = FSNativeL2AdapterConfig( base_path="/tmp/test_ra", ) assert cfg.read_ahead_size is None adapter = create_l2_adapter_from_registry(cfg) assert captured["read_ahead"] == 0 adapter.close() finally: _L2_ADAPTER_FACTORY_REGISTRY["fs_native"] = old def _patched_fs_factory(config, l1_memory_desc=None): """Factory that uses _FakeLMCacheFSClient instead of the real C++ extension.""" # First Party from lmcache.v1.distributed.l2_adapters.fs_native_l2_adapter import ( FSNativeL2AdapterConfig, ) from lmcache.v1.distributed.l2_adapters.native_connector_l2_adapter import ( NativeConnectorL2Adapter, ) assert isinstance(config, FSNativeL2AdapterConfig) client = _FakeLMCacheFSClient( config.base_path, config.num_workers, config.relative_tmp_dir, config.use_odirect, config.read_ahead_size or 0, ) return NativeConnectorL2Adapter(client)