fix(factory): stabilize multi-device automatic flashing

This commit is contained in:
chenjintao
2026-09-24 19:28:13 +08:00
parent 01d52cc3d6
commit 3dcfa2442b
6 changed files with 457 additions and 32 deletions
+96 -3
View File
@@ -44,11 +44,104 @@ class FactoryHostTests(unittest.TestCase):
self.assertIn("-a", args)
self.assertNotIn("{port}", args)
def test_auto_flash_binds_each_detected_port(self):
def test_auto_flash_starts_one_usb_listener_before_power_on(self):
args = factory_host.DEFAULT_CONFIG["flash"]["auto_args"]
self.assertIn("-p", args)
self.assertIn("-u", args)
self.assertIn("-a", args)
self.assertIn("{port}", args)
self.assertNotIn("-q", args)
self.assertNotIn("-m", args)
self.assertNotIn("{port}", args)
def test_auto_flash_classifies_persistent_downloader_results(self):
self.assertEqual(
factory_host.classify_downloader_line(
"<COM8> all finished. total time: 23.500s"),
"烧录成功",
)
self.assertEqual(
factory_host.classify_downloader_line("failed to parse configuration"),
"失败",
)
self.assertIsNone(factory_host.classify_downloader_line(
"<COM8> OFFLINE device open failed num = 0, device list size = 0"))
def test_auto_flash_assigns_untagged_result_to_unfinished_port(self):
activity = {"COM38": 8, "COM8": 9}
self.assertEqual(
factory_host.resolve_downloader_result_port(
"", "COM8", activity, {"COM8"}),
"COM38",
)
self.assertEqual(
factory_host.resolve_downloader_result_port(
"COM38", "COM8", activity, {"COM8"}),
"COM38",
)
self.assertEqual(
factory_host.resolve_downloader_result_port(
"COM8", "COM8", activity, {"COM8"}),
"",
)
self.assertEqual(
factory_host.resolve_downloader_result_port(
"", "COM8", activity, {"COM8", "COM38"}),
"",
)
def test_auto_flash_accepts_only_asr_arom_port(self):
arom = factory_host.SerialPortInfo(
device="COM8", hwid=r"USB\VID_2ECC&PID_3004\1234", vid=0x2ECC, pid=0x3004)
arom_from_hwid = factory_host.SerialPortInfo(
device="COM22", hwid=r"USB\VID_2ECC&PID_3004\5678")
runtime = factory_host.SerialPortInfo(
device="COM13", hwid=r"USB\VID_2ECC&PID_3017&MI_00\1234", vid=0x2ECC, pid=0x3017,
description="ASR Serial Download Device")
unrelated = factory_host.SerialPortInfo(
device="COM3", hwid=r"USB\VID_1A86&PID_7523\5678", vid=0x1A86, pid=0x7523)
misleading = factory_host.SerialPortInfo(
device="COM4", description="Generic BootROM Serial Device")
self.assertTrue(factory_host.is_arom_download_port(arom))
self.assertTrue(factory_host.is_arom_download_port(arom_from_hwid))
self.assertFalse(factory_host.is_arom_download_port(runtime))
self.assertFalse(factory_host.is_arom_download_port(unrelated))
self.assertFalse(factory_host.is_arom_download_port(misleading))
def test_auto_flash_ignores_eg800_runtime_interfaces(self):
roles = (
("COM13", 0x03, "Quectel USB AT Port"),
("COM14", 0x05, "USB Serial Device"),
("COM15", 0x04, "Quectel USB Modem"),
("COM16", 0x02, "Quectel USB DIAG Port"),
)
for device, interface, description in roles:
info = factory_host.SerialPortInfo(
device=device,
description=description,
hwid=rf"USB\VID_2C7C&PID_6002&MI_{interface:02X}\1234",
vid=0x2C7C,
pid=0x6002,
)
with self.subTest(device=device):
self.assertFalse(factory_host.is_arom_download_port(info))
def test_auto_flash_groups_interfaces_by_physical_location(self):
first = factory_host.SerialPortInfo(
device="COM8", vid=0x2ECC, pid=0x3004, location="1-4:1.0")
second = factory_host.SerialPortInfo(
device="COM9", vid=0x2ECC, pid=0x3004, location="1-4:1.1")
ports = factory_host.arom_download_ports([second, first])
self.assertEqual(len(ports), 1)
self.assertEqual(next(iter(ports.values())).device, "COM8")
def test_auto_flash_port_identity_survives_com_number_change(self):
before = factory_host.SerialPortInfo(
device="COM8", vid=0x2ECC, pid=0x3004, location="Port_#0004.Hub_#0001")
after = factory_host.SerialPortInfo(
device="COM21", vid=0x2ECC, pid=0x3004, location="Port_#0004.Hub_#0001")
self.assertEqual(
factory_host.download_device_key(before),
factory_host.download_device_key(after),
)
def test_project_root_and_relative_path(self):
self.assertTrue((factory_host.FACTORY_HOST_DIR / "vendor" / "aboot-tools-2024.08.08-win-x64").is_dir())