From 685cafa8f976fdd94168cfc933ad5389f8b4c617 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Tue, 29 Sep 2026 18:04:01 +0100 Subject: Hold an app uncertain whatever its save changed, not only a list A save whose tree commit may or may not be stored leaves the app apart from its store: no save may follow until it starts again from what the store has. The app was marked so only when the save also logged a list's change, so a save of numbers alone that came back uncertain let the next event (a list's alone) save on the state the memory had rolled back to: after a restart, count 1 and a row made from count 0 (review 004, F2). Now any uncertain tree commit marks it; a failed one still takes back the logged entry. test-module's uncertain_numbers does the review's case: the list's event is refused, and the store is the commit's. --- core/module-save.c | 11 +++++------ core/test-module.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) (limited to 'core') diff --git a/core/module-save.c b/core/module-save.c index dbf45a9..d38437f 100644 --- a/core/module-save.c +++ b/core/module-save.c @@ -895,12 +895,11 @@ PureResult pure_save_commit(PureApp *app, const double *values, void *const *doc free(payload); if (result == PURE_OK && tree) { result = app->data.save(app->data.context, documents, document_count, drafts, count, revisions); - /* The logged entry is on the device: taken back when the tree's commit - * failed, or, when that may yet be stored, no save follows. */ - if (result != PURE_OK && payload_size) { - if (result == PURE_UNCERTAIN) app->uncertain = true; - else pure_log_retract(app->log); - } + /* A commit that may yet be stored leaves the app apart from its store, + * whatever the save changed: no save follows until it starts again. + * One that failed takes back the logged entry, which is on the device. */ + if (result == PURE_UNCERTAIN) app->uncertain = true; + else if (result != PURE_OK && payload_size) pure_log_retract(app->log); } if (result == PURE_OK) { for (size_t k = 0; k < count; ++k) diff --git a/core/test-module.c b/core/test-module.c index d03a27b..959e72e 100644 --- a/core/test-module.c +++ b/core/test-module.c @@ -1806,6 +1806,42 @@ static void stored_crashes(void) { remove_store(path); } +/* A save that changes numbers alone (no list, so no log entry) and whose + * commit may yet be stored leaves the app uncertain all the same: the next + * event, which changes a list alone, is refused, and starting again finds + * what the store has (review 004, F2). */ +static void uncertain_numbers(void) { + char *path = new_store(); + static const S nums[] = {{"n", 0}}; + static const T states[] = {{"rows", 3, 0}}; + static const RF fields[] = {{"value", 0, 0}}; + static const R records[] = {{"row", fields, 1}}; + static const I inputs[] = {KEY("inc", "KeyI"), KEY("add", "KeyA")}; + static const O code[] = { + {L, 0, 0}, {C, 0, 1}, {ADD, 0, 0}, {ST, 0, 0}, /* n = n + 1 */ + {L, 0, 0}, {LA, 1, 0}, /* rows.append(row { n }) */ + }; + static const H handlers[] = {{1, 0, 4}, {3, 4, 2}}; + Spec spec = {nums, 1, inputs, 2, NULL, 0, handlers, 2, code, LEN(code), NULL, 0, states, 1, NULL, 0, NULL, 0, + records, 1, NULL, 0}; + CHECK(load(spec, &module_a) == PURE_OK); + PureApp app; + CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK); + fail_stage = PURE_BEFORE_DIR_SYNC; + CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_UNCERTAIN && app.uncertain && app.values[0] == 0); + fail_stage = -1; + CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_CONFLICT && !pure_list_count(app.lists[1])); + pure_app_stop(&app); + CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0] == 1 && + !pure_list_count(app.lists[1])); + CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && pure_list_get(app.lists[1], 0, 0).num == 1); + pure_app_stop(&app); + CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0] == 1 && + pure_list_count(app.lists[1]) == 1 && pure_list_get(app.lists[1], 0, 0).num == 1); + pure_app_stop(&app); + remove_store(path); +} + /* Frame changes are stored by the next event or by a save; a stop alone * leaves them out. */ static void stored_frames(void) { @@ -1951,6 +1987,7 @@ static void stored_states(void) { CHECK(pure_save_test_checkpoints > checkpoints + 5); /* images written again as the log grew */ stored_equivalence(text_spec, text_words, LEN(text_words), 0x7f4a7c15u); stored_crashes(); + uncertain_numbers(); stored_frames(); stored_messages(); stored_migration(); -- cgit v1.2.3