167 """Send one command line to ota_service; return its trimmed reply."""
168 with socket.create_connection((
"127.0.0.1", port), timeout=REPLY_WAIT_S)
as s:
169 s.settimeout(REPLY_WAIT_S)
170 s.sendall(line.encode() + b
"\n")
171 s.shutdown(socket.SHUT_WR)
176 except socket.timeout:
183 return buf.decode(errors=
"replace").strip()
208 data = bytes(random.Random(0xB0A2D + size).randbytes(size))
209 path = os.path.join(tmpdir, name)
210 with open(path,
"wb")
as f:
215def run_flash(port: int, ip: str, path: str, behavior: str, r: Results, label: str,
216 expect_ok: bool, verify_bytes: bytes |
None) ->
None:
217 """Start a fake board, issue OTA_FLASH, and assert reply + received bytes."""
220 r.check(label,
False, board.error
or "bind failed")
223 board._ready.wait(2.0)
225 board.join(timeout=REPLY_WAIT_S)
227 got_ok = reply.startswith(
"OK")
229 r.check(f
"{label}: service reply OK", got_ok, reply)
231 r.check(f
"{label}: service reply ERR", reply.startswith(
"ERR"), reply)
233 if verify_bytes
is not None:
235 f
"{label}: size header (big-endian) matches file",
236 board.declared_size == len(verify_bytes),
237 f
"declared={board.declared_size} expected={len(verify_bytes)}",
240 f
"{label}: received bytes identical to firmware",
241 board.received == verify_bytes,
242 f
"got {len(board.received)}/{len(verify_bytes)} bytes"
243 + (f
"; board error: {board.error}" if board.error
else ""),
252 print(f
"[test_ota_flash] binary={bin_path}", flush=
True)
253 print(f
"[test_ota_flash] ota_service command port={ota_port}, board port={OTA_BOARD_PORT}",
258 probe = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
259 probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
261 probe.bind((BOARD_IP, OTA_BOARD_PORT))
264 f
"[test_ota_flash] port {OTA_BOARD_PORT} is in use ({e}); a real stack or "
265 "another test may be running. Aborting.",
272 proc = subprocess.Popen(
273 [bin_path,
"--port", str(ota_port)],
274 stdout=subprocess.PIPE,
275 stderr=subprocess.STDOUT,
282 print(
"[test_ota_flash] ota_service never started listening", flush=
True)
285 with tempfile.TemporaryDirectory()
as tmp:
287 big_path, big_data =
make_firmware(tmp,
"firmware_big.bin", 10_000)
289 aligned_path, aligned_data =
make_firmware(tmp,
"firmware_aligned.bin", 8192)
292 run_flash(ota_port, BOARD_IP, big_path,
"ok", r,
293 "flash_ok (10KB, multi-chunk)", expect_ok=
True, verify_bytes=big_data)
296 run_flash(ota_port, BOARD_IP, aligned_path,
"ok", r,
297 "flash_ok (8KB, chunk-aligned)", expect_ok=
True, verify_bytes=aligned_data)
300 run_flash(ota_port, BOARD_IP, big_path,
"silent", r,
301 "flash_silent_reboot", expect_ok=
True, verify_bytes=big_data)
304 run_flash(ota_port, BOARD_IP, big_path,
"error", r,
305 "flash_board_error", expect_ok=
False, verify_bytes=big_data)
308 reply =
send_command(ota_port, f
"OTA_FLASH:{BOARD_IP}:{tmp}/does_not_exist.bin")
309 r.check(
"flash_missing_file: ERR cannot open",
310 reply.startswith(
"ERR")
and "cannot open" in reply, reply)
313 reply =
send_command(ota_port, f
"OTA_FLASH:{BOARD_IP}:{big_path}")
314 r.check(
"flash_connect_refused: ERR connect",
315 reply.startswith(
"ERR")
and "connect" in reply, reply)
319 r.check(
"bad_format: ERR bad OTA_FLASH",
320 reply.startswith(
"ERR")
and "OTA_FLASH" in reply, reply)
324 r.check(
"unknown_command: ERR",
325 reply.startswith(
"ERR")
and "unknown" in reply, reply)
330 except subprocess.TimeoutExpired:
333 print(f
"\n[test_ota_flash] {r.passed} passed, {r.failed} failed", flush=
True)
334 return 0
if r.failed == 0
else 1
None run_flash(int port, str ip, str path, str behavior, Results r, str label, bool expect_ok, bytes|None verify_bytes)