File size: 7,112 Bytes
bbb6388 | 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 | """Tests for `setup.py --update` (#475, UPDATE.bat / update.sh): it refreshes what a start would - the Python packages,
the engine, each model's config and draft subset - and never starts the model. Every outside effect is mocked: no
GPU, no downloads, nothing written outside a temp folder.
python -m unittest tools.test_setup_update
"""
from __future__ import annotations
import contextlib
import io
import json
import sys
import tempfile
import unittest
from pathlib import Path
from unittest import mock
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
import setup # noqa: E402
class Update(unittest.TestCase):
def config(self, d: Path, **extra) -> Path:
rt = d / "mtp" / "rt"
rt.mkdir(parents=True)
p = d / "strata-iq3_s.json"
p.write_text(json.dumps({"model_name": "Qwen IQ3_S", "exe": str(d / "engine" / setup.EXE),
"args": ["--native", "x", "--mtp", str(rt)], **extra}), encoding="utf-8")
return p
def run_update(self, have, argv=()):
a = mock.Mock(**{"build": False, "prebuilt": "URL", **dict(argv)})
out = io.StringIO()
with mock.patch.object(setup, "pip_install") as pip, \
mock.patch.object(setup, "update_installed_engine") as eng, \
mock.patch.object(setup, "refresh_draft_vocab") as dv, \
mock.patch.object(setup, "start") as start, \
mock.patch("subprocess.call") as call, \
contextlib.redirect_stdout(out):
rc = setup.update_install(have, a)
return rc, pip, eng, dv, start, call, out.getvalue()
def test_refreshes_without_starting(self):
with tempfile.TemporaryDirectory() as d:
p = self.config(Path(d), draft_vocab="en")
rc, pip, eng, dv, start, call, out = self.run_update([p])
self.assertEqual(rc, 0)
pip.assert_called_once()
eng.assert_called_once_with("URL")
dv.assert_called_once()
self.assertEqual(dv.call_args[0][1], "en") # the model's own subset is kept
start.assert_not_called()
call.assert_not_called()
self.assertIn("Strata is updated", out)
def test_build_keeps_the_compiled_engine_path(self):
with tempfile.TemporaryDirectory() as d:
p = self.config(Path(d))
rc, pip, eng, dv, *_ = self.run_update([p], {"build": True})
self.assertEqual(rc, 0)
eng.assert_not_called()
self.assertEqual(dv.call_args[0][1], "cjk")
def test_nothing_installed(self):
rc, pip, eng, dv, start, call, out = self.run_update([])
self.assertEqual(rc, 0)
for m in (pip, eng, dv, start, call):
m.assert_not_called()
self.assertIn("START-HERE.bat", out)
def test_a_json_that_is_no_model_config_is_skipped(self):
"""#549: a strata-*.json without "args" (not written by setup) stopped update.sh with KeyError: 'args'."""
with tempfile.TemporaryDirectory() as d:
p = self.config(Path(d))
other = Path(d) / "strata-notes.json"
other.write_text(json.dumps({"note": "mine"}), encoding="utf-8")
broken = Path(d) / "strata-cut.json"
broken.write_text("{\"args\": [", encoding="utf-8")
rc, pip, eng, dv, start, call, out = self.run_update([other, broken, p])
self.assertEqual(rc, 0, out)
self.assertIn('skipped strata-notes.json (no "exe" or "args"): it is not a Strata model config', out)
self.assertIn("skipped strata-cut.json (not valid JSON)", out)
dv.assert_called_once() # the real model is still refreshed
self.assertIn("Qwen IQ3_S: up to date", out)
self.assertIn("Strata is updated", out)
def test_installed_configs_lists_only_model_configs(self):
with tempfile.TemporaryDirectory() as d:
p = self.config(Path(d))
(Path(d) / "strata-notes.json").write_text("{}", encoding="utf-8")
with mock.patch.object(setup, "ROOT", Path(d)), contextlib.redirect_stdout(io.StringIO()):
self.assertEqual(setup.installed_configs(), [p])
def test_main_update_never_starts(self):
with tempfile.TemporaryDirectory() as d:
p = self.config(Path(d))
with mock.patch.object(sys, "argv", ["setup.py", "--update"]), \
mock.patch.object(setup, "data_folder", return_value=(Path(d), [])), \
mock.patch.object(setup, "installed_configs", return_value=[p]), \
mock.patch.object(setup, "update_install", return_value=0) as up, \
mock.patch.object(setup, "start") as start, \
contextlib.redirect_stdout(io.StringIO()):
self.assertEqual(setup.main(), 0)
up.assert_called_once()
start.assert_not_called()
class SettingsLine(unittest.TestCase):
"""#564: a start prints the settings it uses (the engine options without the model's paths, and the server's
fields), so a change made by hand to strata-<model>.json shows without reading the log."""
CFG = {"exe": "x", "host": "0.0.0.0", "port": 8081, "api_key": "secret", "gpu": [0, 1], "fit_max_tokens": True,
"args": ["--native", "E:\\Strata\\packs\\iq3_s", "--mtp", "/s/mtp/rt", "m.gguf", "--kv", "int8",
"--kv-resident", "32768", "--spec-min-p", "0.5", "--vram-reserve-mib", "2048", "--mmap-experts",
"--prefill", "auto"]}
def test_summary(self):
s = setup.settings_summary(self.CFG)
self.assertEqual(s, "--kv int8 --kv-resident 32768 --spec-min-p 0.5 --vram-reserve-mib 2048 --mmap-experts "
"--prefill auto; server 0.0.0.0:8081, api key set, gpu 0,1, fit_max_tokens true")
self.assertNotIn("secret", s)
self.assertIn("127.0.0.1:9000", setup.settings_summary({"args": []}, 9000))
def test_a_start_prints_it(self):
with tempfile.TemporaryDirectory() as d:
exe = Path(d) / "strata.exe"
exe.write_bytes(b"")
p = Path(d) / "strata-iq3_s.json"
args = [x for x in self.CFG["args"] if x != "m.gguf"] # no model file here
p.write_text(json.dumps({**self.CFG, "exe": str(exe), "gpu": 0, "args": args}), encoding="utf-8")
out = io.StringIO()
with mock.patch.object(setup, "gpus", lambda: []), \
mock.patch.object(setup, "refresh_draft_vocab"), \
mock.patch.object(setup, "is_wsl", lambda: False), \
mock.patch.object(setup.subprocess, "call", return_value=0), \
contextlib.redirect_stdout(out):
setup.start(p, None, open_browser=False, yes=True)
text = " ".join(out.getvalue().split())
self.assertIn("Settings (strata-iq3_s.json): --kv int8 --kv-resident 32768", text)
self.assertIn("--vram-reserve-mib 2048", text)
if __name__ == "__main__":
unittest.main()
|