107 lines
4.1 KiB
Python
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()
|
|
|