@@ -66,68 +66,32 @@ def test_keyboard_main_invokes_listener(self, monkeypatch, sys_modules_patch):
6666
6767 def test_keyboard_module_fallback_import (self , monkeypatch , sys_modules_patch ):
6868 """
69- Test that keyboard.py falls back to importing implementations.baseop.BaseOp
70- if kvm_serial.backend.implementations.baseop import fails with ModuleNotFoundError.
71-
72- This is a horrible test that by necessity has to mess around with both
73- __import__ and sys.modules. It makes an effort to restore these afterwards.
69+ Test that _load_implementation falls back to backend.implementations.*
70+ when kvm_serial.backend.implementations.* import fails.
7471
7572 Mocks:
76- - builtins.__import__: Raises ModuleNotFoundError for kvm_serial.backend.implementations.baseop
77- - sys.modules: Injects a mock implementations.baseop.BaseOp
78- - KeyboardListener: Mocked to avoid running threads
79- - logging.basicConfig: Avoids side effects
73+ - keyboard.import_module: Raises ModuleNotFoundError for kvm_serial.*,
74+ returns a mock module for the backend.* fallback path
8075 Asserts:
81- - The fallback import path is used (implementations.baseop.BaseOp)
82- - KeyboardListener can still be constructed and used
76+ - The fallback import path is used and returns the expected class
8377 """
84- import importlib
85-
86- # Save original sys.modules entries to restore after test
87- orig_kvm_serial = sys .modules .get ("kvm_serial" )
88- orig_baseop = sys .modules .get ("kvm_serial.backend.implementations.baseop" )
89-
90- try :
91- # Remove relevant modules from sys.modules
92- sys .modules .pop ("kvm_serial.backend.implementations.baseop" , None )
93- sys .modules .pop ("kvm_serial" , None )
94-
95- # Patch __import__ to raise ModuleNotFoundError for the primary import
96- orig_import = __import__
97-
98- def import_side_effect (name , * args , ** kwargs ):
99- if name == "kvm_serial.backend.implementations.baseop" :
100- raise ModuleNotFoundError
101- return orig_import (name , * args , ** kwargs )
102-
103- monkeypatch .setattr ("builtins.__import__" , import_side_effect )
104-
105- # Inject a mock implementations.baseop.BaseOp
106- mock_baseop = MagicMock ()
107- sys .modules ["implementations.baseop" ] = MagicMock (BaseOp = mock_baseop )
108-
109- # Patch KeyboardListener and logging.basicConfig
110- from kvm_serial .backend import keyboard as kb_mod
111-
112- # Ensure module is in sys.modules before reload
113- sys .modules ["kvm_serial.backend.keyboard" ] = kb_mod
114-
115- with (
116- patch .object (kb_mod , "KeyboardListener" , MagicMock ()),
117- patch ("logging.basicConfig" , lambda * a , ** k : None ),
118- ):
119- # Reload the module to trigger the import logic
120- importlib .reload (kb_mod )
121- # Now, BaseOp should be the mock from implementations.baseop
122- assert hasattr (kb_mod , "BaseOp" )
123- assert kb_mod .BaseOp is mock_baseop
124- finally :
125- # Restore sys.modules to its original state
126- sys .modules .pop ("implementations.baseop" , None )
127- if orig_kvm_serial is not None :
128- sys .modules ["kvm_serial" ] = orig_kvm_serial
129- if orig_baseop is not None :
130- sys .modules ["kvm_serial.backend.implementations.baseop" ] = orig_baseop
78+ from importlib import import_module as real_import_module
79+ from kvm_serial .backend import keyboard as kb_mod
80+
81+ mock_module = MagicMock ()
82+ mock_handler = MagicMock ()
83+ mock_module .TestHandler = mock_handler
84+
85+ def import_side_effect (name , * args , ** kwargs ):
86+ if name .startswith ("kvm_serial.backend.implementations." ):
87+ raise ModuleNotFoundError (name )
88+ if name == "backend.implementations.testmod" :
89+ return mock_module
90+ return real_import_module (name , * args , ** kwargs )
91+
92+ with patch .object (kb_mod , "import_module" , side_effect = import_side_effect ):
93+ result = kb_mod ._load_implementation ("testmod" , "TestHandler" )
94+ assert result is mock_handler
13195
13296
13397# Mock Serial
0 commit comments