forked from lightningpixel/modly
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_extension_process.py
More file actions
159 lines (115 loc) · 5.14 KB
/
Copy pathtest_extension_process.py
File metadata and controls
159 lines (115 loc) · 5.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import io
import platform
import queue
import unittest
from pathlib import Path
from services.extension_process import ExtensionProcess, _venv_python
def _make_proc() -> ExtensionProcess:
return ExtensionProcess(ext_dir=None, manifest={"id": "demo"}) # type: ignore[arg-type]
class ExtensionProcessTests(unittest.TestCase):
def test_read_loop_writes_sentinel_to_own_queue_only(self) -> None:
proc = _make_proc()
old_queue: queue.Queue = queue.Queue()
new_queue: queue.Queue = queue.Queue()
proc._queue = new_queue
fake_proc = type("FakeProc", (), {"stdout": io.StringIO("")})()
proc._read_loop(fake_proc, old_queue)
self.assertFalse(old_queue.empty())
self.assertTrue(new_queue.empty())
def test_stop_kills_and_verifies_subprocess_exit(self) -> None:
proc = _make_proc()
class FakeProcess:
def __init__(self) -> None:
self.alive = True
self.kill_called = False
self.wait_called = False
def poll(self):
return None if self.alive else -9
def kill(self) -> None:
self.kill_called = True
self.alive = False
def wait(self, timeout: float):
self.wait_called = True
return -9
child = FakeProcess()
proc._proc = child # type: ignore[assignment]
proc._loaded = True
proc.stop()
self.assertTrue(child.kill_called)
self.assertTrue(child.wait_called)
self.assertIsNone(proc._proc)
self.assertFalse(proc._loaded)
def test_stop_failure_keeps_live_process_reference_and_raises(self) -> None:
proc = _make_proc()
class StuckProcess:
def poll(self):
return None
def kill(self) -> None:
raise PermissionError("cannot kill")
def wait(self, timeout: float):
raise AssertionError("wait must not run after kill failure")
child = StuckProcess()
proc._proc = child # type: ignore[assignment]
proc._loaded = True
with self.assertRaisesRegex(RuntimeError, "Could not stop"):
proc.stop()
self.assertIs(proc._proc, child)
self.assertFalse(proc._loaded)
class VenvPythonTests(unittest.TestCase):
def test_resolves_interpreter_path_for_current_platform(self) -> None:
result = _venv_python(Path("/tmp/ext"))
if platform.system() == "Windows":
self.assertEqual(result, Path("/tmp/ext") / "venv" / "Scripts" / "python.exe")
else:
self.assertEqual(result, Path("/tmp/ext") / "venv" / "bin" / "python")
class BuildEnvTests(unittest.TestCase):
def test_forces_utf8_stdio_on_worker(self) -> None:
proc = _make_proc()
env = proc._build_env()
self.assertEqual(env.get("PYTHONUTF8"), "1")
def test_sets_worker_model_dir_when_known(self) -> None:
proc = _make_proc()
proc.model_dir = Path("/tmp/models/ext/node")
env = proc._build_env()
self.assertEqual(env.get("MODEL_DIR"), str(Path("/tmp/models/ext/node")))
class MissingModuleExtractionTests(unittest.TestCase):
def test_extracts_module_name_from_message(self) -> None:
proc = _make_proc()
name = proc._extract_missing_module({"message": "No module named 'PIL'"})
self.assertEqual(name, "PIL")
def test_extracts_module_name_from_traceback(self) -> None:
proc = _make_proc()
name = proc._extract_missing_module(
{"message": "boom", "traceback": "...\nModuleNotFoundError: No module named \"numpy\"\n"}
)
self.assertEqual(name, "numpy")
def test_returns_none_when_no_missing_module(self) -> None:
proc = _make_proc()
self.assertIsNone(proc._extract_missing_module({"message": "some other error"}))
class AutoRepairPackageTests(unittest.TestCase):
"""Safety: only known modules map to a package; never guess arbitrary names."""
def test_maps_known_module_to_package(self) -> None:
proc = _make_proc()
self.assertEqual(proc._resolve_auto_repair_package("PIL"), "Pillow")
def test_maps_known_module_via_root_package(self) -> None:
proc = _make_proc()
self.assertEqual(proc._resolve_auto_repair_package("PIL.Image"), "Pillow")
def test_returns_none_for_unknown_module(self) -> None:
proc = _make_proc()
self.assertIsNone(proc._resolve_auto_repair_package("totally_unknown_pkg"))
class RecvTests(unittest.TestCase):
def test_returns_message_from_queue(self) -> None:
proc = _make_proc()
proc._queue.put({"type": "ready"})
self.assertEqual(proc._recv(timeout=1.0), {"type": "ready"})
def test_none_sentinel_raises_runtime_error(self) -> None:
proc = _make_proc()
proc._queue.put(None)
with self.assertRaises(RuntimeError):
proc._recv(timeout=1.0)
def test_empty_queue_raises_timeout_error(self) -> None:
proc = _make_proc()
with self.assertRaises(TimeoutError):
proc._recv(timeout=0.05)
if __name__ == "__main__":
unittest.main()