Files
matrix-screen-controller/发布更新相关/镜像编辑器/编辑器源代码/tests/test_config.py
T

107 lines
4.1 KiB
Python

from __future__ import annotations
import copy
import importlib.util
from pathlib import Path
import sys
import unittest
SOURCE_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(SOURCE_ROOT / "src"))
from matrix_image_editor.config import ( # noqa: E402
HEADER_BYTES,
SLOT_BYTES,
ImageConfigError,
create_config_file,
read_config_file,
update_config_file,
validate_config,
)
def sample_config() -> dict:
return {
"schema_version": 1,
"product": "matrix-screen-controller-walnutpi",
"software_version": "1.0.3",
"account": {"username": "matrix", "password": "password123"},
"wifi": {"ssid": "测试 WiFi", "password": "wifi-pass-123"},
"ipv4": {"mode": "dhcp", "address": "", "prefix": 0, "gateway": "", "dns": []},
}
class ConfigTests(unittest.TestCase):
def test_round_trip_and_update_inactive_slot(self) -> None:
original = create_config_file(sample_config())
config, generation, slot = read_config_file(original)
self.assertEqual((generation, slot), (1, 0))
config["wifi"]["ssid"] = "新网络"
updated = update_config_file(original, config)
verified, generation, slot = read_config_file(updated)
self.assertEqual((generation, slot, verified["wifi"]["ssid"]), (2, 1, "新网络"))
self.assertEqual(updated[HEADER_BYTES : HEADER_BYTES + SLOT_BYTES], original[HEADER_BYTES : HEADER_BYTES + SLOT_BYTES])
def test_corrupt_active_slot_falls_back_and_both_corrupt_fail(self) -> None:
original = create_config_file(sample_config())
updated = bytearray(update_config_file(original, sample_config()))
updated[HEADER_BYTES + SLOT_BYTES + 60] ^= 0x01
_config, generation, slot = read_config_file(bytes(updated))
self.assertEqual((generation, slot), (1, 0))
updated[HEADER_BYTES + 60] ^= 0x01
with self.assertRaises(ImageConfigError):
read_config_file(bytes(updated))
def test_rejects_unknown_product_schema_and_fields(self) -> None:
for field, value in (("product", "wrong"), ("schema_version", 2)):
config = sample_config()
config[field] = value
with self.assertRaises(ImageConfigError):
validate_config(config)
config = sample_config()
config["unexpected"] = True
with self.assertRaises(ImageConfigError):
validate_config(config)
def test_utf8_ssid_and_utf16_secret_lengths_match_contract(self) -> None:
config = sample_config()
config["wifi"]["ssid"] = "中" * 10
validate_config(config)
config["wifi"]["ssid"] = "中" * 11
with self.assertRaises(ImageConfigError):
validate_config(config)
config = sample_config()
config["account"]["password"] = "😀" * 4
validate_config(config)
def test_static_ipv4_requires_same_subnet_and_valid_dns(self) -> None:
config = sample_config()
config["ipv4"] = {
"mode": "static",
"address": "192.168.10.20",
"prefix": 24,
"gateway": "192.168.10.1",
"dns": ["1.1.1.1", "8.8.8.8"],
}
validate_config(config)
config["ipv4"]["gateway"] = "192.168.11.1"
with self.assertRaises(ImageConfigError):
validate_config(config)
def test_project_codec_cross_compatibility_when_repository_is_present(self) -> None:
project_codec = SOURCE_ROOT.parents[2] / "核桃派软件源代码" / "scripts" / "image_config.py"
if not project_codec.is_file():
self.skipTest("独立源码副本中没有主工程交叉校验器")
spec = importlib.util.spec_from_file_location("project_image_config", project_codec)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
ours = create_config_file(copy.deepcopy(sample_config()))
theirs = module.create_config_file(copy.deepcopy(sample_config()))
self.assertEqual(module.read_config_file(ours)[0], read_config_file(theirs)[0])
if __name__ == "__main__":
unittest.main()