diff --git a/src/config.h b/src/config.h index a7fad35..a0516a9 100644 --- a/src/config.h +++ b/src/config.h @@ -31,6 +31,11 @@ #define NVS_KEY_IMG_ID "img_id" #define NVS_KEY_DRAW_NEEDED "draw" #define NVS_KEY_ERR_BORDER "err" // set when display is showing a sync-fail border; force a clean redraw on next 200/304 +#define NVS_KEY_SCHEMA_V "schema_v" + +// Bump when introducing a schema migration. Each new value can force a one-shot +// recovery action on first boot of the new firmware. +#define NVS_SCHEMA_VERSION 1 // Width of the sync-fail / no-WiFi border, in pixels. #define BORDER_THICKNESS_PX 16 diff --git a/src/operation.h b/src/operation.h index 37a9972..1e583ce 100644 --- a/src/operation.h +++ b/src/operation.h @@ -67,8 +67,22 @@ void normal_operation_impl(const String& mac, HTTP& http, const String& url, Pre int32_t currentImgId = prefs.getInt(NVS_KEY_IMG_ID, -1); bool drawNeeded = prefs.getInt(NVS_KEY_DRAW_NEEDED, 0) != 0; bool errBorder = prefs.getInt(NVS_KEY_ERR_BORDER, 0) != 0; + int schemaV = prefs.getInt(NVS_KEY_SCHEMA_V, 0); prefs.end(); + // Schema migration: on first boot under err-border-aware firmware, the + // display may be holding a stale full-screen yellow from the old buggy + // epd_fill(YELLOW) path. The old firmware never wrote NVS_KEY_ERR_BORDER, + // so we'd have no other signal that a clean repaint is needed. Force one + // by treating this boot as if errBorder were set, then bump schema_v so + // it doesn't fire again. + if (schemaV < NVS_SCHEMA_VERSION) { + errBorder = true; + prefs.begin(NVS_NAMESPACE, false); + prefs.putInt(NVS_KEY_SCHEMA_V, NVS_SCHEMA_VERSION); + prefs.end(); + } + if (currentImgId >= 0) { http.addHeader("X-Current-Image-Id", String(currentImgId)); } diff --git a/test/test_normal_operation/test_main.cpp b/test/test_normal_operation/test_main.cpp index b87ac5d..c5f11db 100644 --- a/test/test_normal_operation/test_main.cpp +++ b/test/test_normal_operation/test_main.cpp @@ -189,8 +189,9 @@ void test_fw06c_304_after_error_repaints_clean() { // regression the user reported: 304 was suspected of triggering yellow fill. void test_fw06d_304_steady_state_does_not_fill_yellow() { g_http_get_code = 304; - prefs.ints[NVS_KEY_IMG_ID] = 7; - LittleFS.files[IMAGE_PATH] = "IMGDATA"; + prefs.ints[NVS_KEY_IMG_ID] = 7; + prefs.ints[NVS_KEY_SCHEMA_V] = NVS_SCHEMA_VERSION; // post-migration steady state + LittleFS.files[IMAGE_PATH] = "IMGDATA"; // err_border = 0, draw_needed = 0 (defaults) normal_operation_impl(String("mac"), http, String("url"), prefs); @@ -214,6 +215,39 @@ void test_fw06e_200_after_error_clears_flag() { TEST_ASSERT_EQUAL(0, prefs.getInt(NVS_KEY_ERR_BORDER, -1)); } +// FW-06f: First boot of err-border-aware firmware (NVS schema_v missing) on a +// device that may be displaying a stale full-screen yellow from the previous +// buggy build → migration forces a clean redraw on the next 304 and bumps +// schema_v so it doesn't fire again. Locks in the upgrade-path recovery. +void test_fw06f_schema_migration_forces_redraw_on_first_boot() { + g_http_get_code = 304; + prefs.ints[NVS_KEY_IMG_ID] = 3; + LittleFS.files[IMAGE_PATH] = "IMGDATA"; + // No NVS_KEY_SCHEMA_V set → defaults to 0, triggers migration + + normal_operation_impl(String("mac"), http, String("url"), prefs); + + TEST_ASSERT_EQUAL(1, g_epd_init_count); + TEST_ASSERT_EQUAL(1, g_epd_draw_image_count); + TEST_ASSERT_EQUAL(NVS_SCHEMA_VERSION, prefs.getInt(NVS_KEY_SCHEMA_V, -1)); + TEST_ASSERT_EQUAL(0, prefs.getInt(NVS_KEY_ERR_BORDER, -1)); +} + +// FW-06g: Second boot under same firmware → migration does NOT fire again, +// 304 is a no-op as in the steady-state case. +void test_fw06g_schema_migration_does_not_fire_again() { + g_http_get_code = 304; + prefs.ints[NVS_KEY_IMG_ID] = 3; + prefs.ints[NVS_KEY_SCHEMA_V] = NVS_SCHEMA_VERSION; + LittleFS.files[IMAGE_PATH] = "IMGDATA"; + + normal_operation_impl(String("mac"), http, String("url"), prefs); + + TEST_ASSERT_EQUAL(0, g_epd_init_count); + TEST_ASSERT_EQUAL(0, g_epd_draw_image_count); + TEST_ASSERT_EQUAL(0, g_epd_fill_count); +} + // FW-07: NVS has saved img_id → X-Current-Image-Id header sent void test_fw07_current_image_id_sent_when_saved() { prefs.ints["img_id"] = 99; @@ -311,6 +345,8 @@ int main(int argc, char** argv) { RUN_TEST(test_fw06c_304_after_error_repaints_clean); RUN_TEST(test_fw06d_304_steady_state_does_not_fill_yellow); RUN_TEST(test_fw06e_200_after_error_clears_flag); + RUN_TEST(test_fw06f_schema_migration_forces_redraw_on_first_boot); + RUN_TEST(test_fw06g_schema_migration_does_not_fire_again); RUN_TEST(test_fw07_current_image_id_sent_when_saved); RUN_TEST(test_fw08_no_current_image_id_when_default); RUN_TEST(test_fw09_server_interval_honored);