diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml index abacc63c2..d05d6562f 100644 --- a/.github/workflows/run-unit-tests.yml +++ b/.github/workflows/run-unit-tests.yml @@ -159,6 +159,7 @@ jobs: - name: Verify receive recovery and pinned radio status transport run: | + python3 -B test/test_dispatcher_startup_watchdog.py -v python3 -B test/test_radio_receive_contract.py python3 - <<'PY' from pathlib import Path @@ -244,13 +245,21 @@ jobs: python3 -B test/test_esp32_usb_serial_hygiene.py python3 -B test/test_esp32_tinyusb_nonblocking.py python3 -B test/test_esp32_usb_session_fix.py + python3 -B test/test_hwcdc_tx_backport.py -v + python3 -B test/test_esp32_file_buffer_fix.py -v python3 -B test/test_companion_usb_logging_reply.py python3 -B test/test_esp32_tinyusb_cooperative_output.py python3 -B test/test_esp32_tinyusb_role_hygiene.py python3 -B test/test_stm32_float_conversion.py - name: Verify Companion contact persistence and OTA sleep guards - run: python3 -B test/test_companion_contact_sleep_contract.py -v + run: | + python3 -B test/test_companion_contact_sleep_contract.py -v + python3 -B test/test_esp32_cooperative_contacts.py -v + python3 -B test/test_esp32_contact_writer_batch.py -v + python3 -B test/test_esp32_contact_crc_batch.py -v + python3 -B test/test_esp32_contact_publication.py -v + python3 -B test/test_esp32_advert_cache.py -v - name: Verify Companion Bluetooth CLI connection protection run: python3 -B test/test_companion_bluetooth_control.py -v diff --git a/test/test_esp32_cooperative_contacts.py b/test/test_esp32_cooperative_contacts.py index 7bbb4f6cc..ec72814f4 100644 --- a/test/test_esp32_cooperative_contacts.py +++ b/test/test_esp32_cooperative_contacts.py @@ -81,10 +81,10 @@ class UncachedESPContactsTest(unittest.TestCase): '"/contacts3", companionPathPresence, true)', '"/contacts3", companionPathPresence, false)') elif disable == "crc_batch": - self.assertIn("_contact_write->serviceCommit(true, 8)", implementation) + anchor = "_contact_write->serviceCommit(true, 8, true)" + self.assertEqual(implementation.count(anchor), 1) implementation = implementation.replace( - "_contact_write->serviceCommit(true, 8)", - "_contact_write->serviceCommit()") + anchor, "_contact_write->serviceCommit(true, 1, true)") # Only the native SDK stat seam is substituted. The production # presence logic still distinguishes ENOENT from metadata failure. presence = method(source, "static bool companionPathPresence(") @@ -109,13 +109,17 @@ class UncachedESPContactsTest(unittest.TestCase): transaction = transaction.replace("ESP_VERIFY_BUFFER_SIZE = 128;", "ESP_VERIFY_BUFFER_SIZE = 64;") if disable == "writer": - self.assertIn("ok = _file.setBufferSize(ESP_WRITE_BUFFER_SIZE);", transaction) + anchor = ("ok = _file.setBufferSize(background_contact_write\n" + " ? ESP_BACKGROUND_WRITE_BUFFER_SIZE : ESP_WRITE_BUFFER_SIZE);") + self.assertEqual(transaction.count(anchor), 1) transaction = transaction.replace( - "ok = _file.setBufferSize(ESP_WRITE_BUFFER_SIZE);", + anchor, "ok = true; // Negative control: retain default writer buffer.") elif disable == "reader": - self.assertIn("ok = ok && _verify.setBufferSize(ESP_VERIFY_BUFFER_SIZE);", transaction) - transaction = transaction.replace("ok = ok && _verify.setBufferSize(ESP_VERIFY_BUFFER_SIZE);", + anchor = ("ok = ok && _verify.setBufferSize(\n" + " max_verify_chunks >= 8 ? 512 : ESP_VERIFY_BUFFER_SIZE);") + self.assertEqual(transaction.count(anchor), 1) + transaction = transaction.replace(anchor, "// Negative control: retain the SDK's default reader buffer.") transaction = transaction.replace('#include "IdentityStore.h"', '#include ') @@ -167,7 +171,7 @@ class UncachedESPContactsTest(unittest.TestCase): return measurements else: self.assertNotEqual(result.returncode, 0) - marker = ("startup_operations" if disable == "begin" else + marker = ("SPIFFS.startupOperations() - before == 1" if disable == "begin" else "crc_read_passes == 104" if disable == "crc_batch" else "largest_backend_" + ("write" if disable == "writer" else "read")) self.assertIn(marker, result.stderr) diff --git a/test/test_packet_manager/test_rx_reserve_packet_manager.cpp b/test/test_packet_manager/test_rx_reserve_packet_manager.cpp index 47e54db2d..0f293dd4a 100644 --- a/test/test_packet_manager/test_rx_reserve_packet_manager.cpp +++ b/test/test_packet_manager/test_rx_reserve_packet_manager.cpp @@ -1040,10 +1040,27 @@ TEST(Dispatcher, RadioOutsideReceiveModeEscalatesOnSecondAttempt) { TestDispatcher dispatcher(radio, clock, manager); dispatcher.begin(); + // The non-RX watchdog starts when loop() first services the radio. + dispatcher.loop(); + EXPECT_EQ(0, radio.soft_recoveries); + EXPECT_EQ(0, radio.hard_recoveries); + EXPECT_EQ(0, dispatcher.getErrFlags() & ERR_EVENT_STARTRX_TIMEOUT); + + clock.now = 8000; + dispatcher.loop(); + EXPECT_EQ(0, radio.soft_recoveries); + EXPECT_EQ(0, radio.hard_recoveries); + clock.now = 8001; dispatcher.loop(); EXPECT_EQ(1, radio.soft_recoveries); EXPECT_EQ(0, radio.hard_recoveries); + EXPECT_NE(0, dispatcher.getErrFlags() & ERR_EVENT_STARTRX_TIMEOUT); + + clock.now = 16001; + dispatcher.loop(); + EXPECT_EQ(1, radio.soft_recoveries); + EXPECT_EQ(0, radio.hard_recoveries); clock.now = 16002; dispatcher.loop();