test(bench): add F17 forward-iteration coverage symmetric to F16

This commit is contained in:
2026-05-04 17:16:01 +03:00
parent 52f97f7195
commit a9b181beb7

View File

@@ -7,7 +7,7 @@
* bench_failure_modes.c
* vim: expandtab:ts=4:sts=4:sw=4
*
* F1F16 failure-injection tests. Each test crafts a deliberately corrupt
* F1F17 failure-injection tests. Each test crafts a deliberately corrupt
* or pathological flat-file and asserts how the backend handles it:
*
* F1 Truncated last line (crash mid-fwrite simulation)
@@ -22,6 +22,7 @@
* F14 mtime/inode flip — file replaced under us, ensure_fresh must rebuild
* F15 Empty file (0 bytes) — verify reports INFO and continues
* F16 Page-Up cursor must not stick on entries[0] (regression guard)
* F17 Forward iteration via start_time covers every message (no gaps)
*
* Each test prints PASS/FAIL with detail and writes a CSV row:
* F#, "failure", file_size, line_count, wall_ms, peak_rss_kb, note
@@ -692,6 +693,112 @@ test_F16(fail_ctx_t* ctx)
be->close();
}
// ---------------------------------------------------------------------------
// F17 — Forward iteration (start_time + from_start=TRUE) reaches every
// message without gaps.
//
// Symmetric counterpart to F16: instead of paging from newest backwards
// via end_time, page from oldest forwards via start_time. Each iteration
// passes start_time = timestamp of the newest message returned in the
// previous batch; the backend should return the next batch of strictly
// newer messages until the file is exhausted.
//
// This exercises a different branch of _flatfile_get_previous_chat:
// - read_from = ff_state_offset_for_time(start_time) (not bom_len)
// - read_to = EOF (no end_time bisect)
// - filter drops msgs with timestamp <= start_dt
// - pagination keeps FIRST MESSAGES_TO_RETRIEVE (not last)
// The path bypasses cursor entirely (start_time triggers cursor=-1 reset).
//
// Pass criterion: total messages received == total written.
static void
test_F17(fail_ctx_t* ctx)
{
if (!test_enabled(ctx->tests, "F17")) return;
auto_gchar gchar* path = setup_test_dir(ctx->tmp_dir, "F17");
const int total_msgs = 2000;
FILE* fp = fopen(path, "w");
fprintf(fp, "%s", FLATFILE_HEADER);
for (int i = 0; i < total_msgs; i++)
write_normal_line(fp, i, "forward-iter regression payload");
fclose(fp);
g_free(g_flatfile_account_jid);
g_flatfile_account_jid = g_strdup(ACCOUNT_JID);
setenv("BENCH_ACCOUNT_JID", ACCOUNT_JID, 1);
db_backend_t* be = db_backend_flatfile();
if (!be || !be->init || !be->get_previous_chat || !be->close) {
report(ctx, "F17", FALSE, 0, path, "flatfile backend not available");
return;
}
ProfAccount fake = { 0 };
fake.jid = (gchar*)ACCOUNT_JID;
if (!be->init(&fake)) {
report(ctx, "F17", FALSE, 0, path, "flatfile init failed");
return;
}
int total_received = 0;
int empty_responses = 0;
int iterations = 0;
// Initial start_time before the first written message so the first
// call returns msgs starting at index 0.
auto_gchar gchar* start_time = g_strdup("2025-06-15T11:00:00Z");
double t0 = bench_now_ms();
while (iterations < 200) {
iterations++;
GSList* batch = NULL;
db_history_result_t r = be->get_previous_chat(
CONTACT_JID, start_time, NULL, TRUE, FALSE, &batch);
if (r == DB_RESPONSE_EMPTY) {
empty_responses++;
break;
}
if (r != DB_RESPONSE_SUCCESS || !batch) {
empty_responses++;
break;
}
// pre_result is sorted oldest-first; with from_start=TRUE we keep
// the first MESSAGES_TO_RETRIEVE. The newest in the returned batch
// is therefore the LAST element of the list.
ProfMessage* newest = NULL;
int batch_n = 0;
for (GSList* l = batch; l; l = l->next) {
batch_n++;
newest = l->data;
}
if (!newest || !newest->timestamp) {
g_slist_free_full(batch, (GDestroyNotify)message_free);
break;
}
total_received += batch_n;
g_free(start_time);
start_time = g_date_time_format_iso8601(newest->timestamp);
g_slist_free_full(batch, (GDestroyNotify)message_free);
}
double dt = bench_now_ms() - t0;
gboolean ok = (total_received == total_msgs)
&& (empty_responses == 1)
&& (iterations < 200);
auto_gchar gchar* note = g_strdup_printf(
"wrote=%d received=%d iters=%d empty=%d (forward-iter regression)",
total_msgs, total_received, iterations, empty_responses);
report(ctx, "F17", ok, dt, path, note);
be->close();
}
// ---------------------------------------------------------------------------
// Driver
@@ -732,6 +839,7 @@ main(int argc, char** argv)
test_F14(&ctx);
test_F15(&ctx);
test_F16(&ctx);
test_F17(&ctx);
fprintf(stderr, "\nfailure-modes summary: %d passed, %d failed\n",
ctx.passed, ctx.failed);