81 lines
3.7 KiB
Python
81 lines
3.7 KiB
Python
import importlib.util
|
|||
|
|
import json
|
||
|
|
from pathlib import Path
|
||
|
|
import sys
|
||
|
|
import tempfile
|
||
|
|
import unittest
|
||
|
|
|
||
|
|
TOOLS = Path(__file__).resolve().parents[1] / "tools"
|
||
|
|
sys.path.insert(0, str(TOOLS))
|
||
|
|
import device_prepare as prepare
|
||
|
|
|
||
|
|
|
||
|
|
class PrepareTests(unittest.TestCase):
|
||
|
|
def setUp(self):
|
||
|
|
self.directory = tempfile.TemporaryDirectory()
|
||
|
|
self.addCleanup(self.directory.cleanup)
|
||
|
|
self.root = Path(self.directory.name)
|
||
|
|
self.credential = self.root / "credential.json"
|
||
|
|
self.value = {"factory_id": "KP01-0123456789AB", "model": "ECHOEAR-S3",
|
||
|
|
"origin": "https://example.com", "key_version": 1,
|
||
|
|
"device_key_hex": "01" * 32}
|
||
|
|
self.save()
|
||
|
|
|
||
|
|
def save(self):
|
||
|
|
self.credential.write_text(json.dumps(self.value), encoding="utf-8-sig")
|
||
|
|
|
||
|
|
def test_accepts_actual_web_download(self):
|
||
|
|
self.assertEqual(prepare.read_credential(self.credential)["device_id"], self.value["factory_id"])
|
||
|
|
|
||
|
|
def test_legacy_id(self):
|
||
|
|
self.value["device_id"] = self.value.pop("factory_id")
|
||
|
|
self.save()
|
||
|
|
self.assertEqual(prepare.read_credential(self.credential)["device_id"], self.value["device_id"])
|
||
|
|
|
||
|
|
def test_rejects_conflicting_identity(self):
|
||
|
|
self.value["device_id"] = "KP01-AAAAAAAAAAAA"
|
||
|
|
self.save()
|
||
|
|
with self.assertRaises(ValueError):
|
||
|
|
prepare.read_credential(self.credential)
|
||
|
|
|
||
|
|
def test_rejects_bad_configuration(self):
|
||
|
|
for field, value in [("model", "different-board"), ("key_version", True),
|
||
|
|
("key_version", 0), ("device_key_hex", "bad"),
|
||
|
|
("origin", "http://example.com"),
|
||
|
|
("origin", "https://user:password@example.com"),
|
||
|
|
("origin", "https://example.com/path"),
|
||
|
|
("origin", "https://example.com:invalid")]:
|
||
|
|
original = self.value[field]
|
||
|
|
self.value[field] = value
|
||
|
|
self.save()
|
||
|
|
with self.subTest(field=field, value=value), self.assertRaises(ValueError):
|
||
|
|
prepare.read_credential(self.credential)
|
||
|
|
self.value[field] = original
|
||
|
|
|
||
|
|
def test_mac_independent_of_factory_id(self):
|
||
|
|
self.assertEqual(prepare.normalize_mac("B4-3A-45-16-34-A8"), "b4:3a:45:16:34:a8")
|
||
|
|
|
||
|
|
@unittest.skipUnless(importlib.util.find_spec("esp_idf_nvs_partition_gen"), "NVS generator unavailable")
|
||
|
|
def test_real_nvs_generation_and_no_overwrite(self):
|
||
|
|
output = self.root / "private"
|
||
|
|
manifest = prepare.prepare(self.credential, output, "B4:3A:45:16:34:A8")
|
||
|
|
image = (output / "ble_factory.bin").read_bytes()
|
||
|
|
self.assertEqual(len(image), 0x6000)
|
||
|
|
self.assertIn(bytes.fromhex(self.value["device_key_hex"]), image)
|
||
|
|
self.assertEqual(manifest["expected_mac"], "b4:3a:45:16:34:a8")
|
||
|
|
self.assertNotIn("device_key_hex", json.dumps(manifest))
|
||
|
|
self.assertNotIn(self.value["device_key_hex"], (output / "setup-label.json").read_text())
|
||
|
|
if (TOOLS.parent / "build" / "flasher_args.json").is_file():
|
||
|
|
import device_flash
|
||
|
|
plan = device_flash.build_plan(TOOLS.parent / "build", output,
|
||
|
|
identity_only=True, reset_runtime=True,
|
||
|
|
expect_mac="B4:3A:45:16:34:A8")
|
||
|
|
self.assertEqual({item.name for item in plan.images}, {"reset-runtime", "ble_factory"})
|
||
|
|
self.assertEqual(next(item.data for item in plan.images if item.name == "ble_factory"), image)
|
||
|
|
with self.assertRaises(FileExistsError):
|
||
|
|
prepare.prepare(self.credential, output, "B4:3A:45:16:34:A8")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
unittest.main()
|