Something went wrong. Try again.
Live STIB-MIVB departures on the Pebble Time 2. It covers the bus, tram, and metro network of Brussels
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632#include <assert.h>#include "model.h"
static void put_stop(uint8_t index, const char *id, const char *name, uint8_t row_count) { Stop *stop = model_staging_stop_at(index); assert(stop); strncpy(stop->id, id, ID_LEN - 1); strncpy(stop->name, name, NAME_LEN - 1); stop->row_count = row_count; model_staging_note(index);}
static void test_staging_commit_swap(void) { model_begin(PAGE_TRANSIT, 1); Stop *stage = model_staging_stop_at(0); strcpy(stage->id, "S1"); stage->row_count = 1; strcpy(stage->rows[0].line, "10"); stage->rows[0].min1 = 2; model_staging_note(0); assert(model_stop_count(PAGE_TRANSIT) == 0); assert(model_stop_at(PAGE_TRANSIT, 0) == NULL); assert(model_stop_by_id(PAGE_TRANSIT, "S1") == NULL); model_commit(PAGE_TRANSIT); assert(model_stop_count(PAGE_TRANSIT) == 1); assert(strcmp(model_stop_at(PAGE_TRANSIT, 0)->id, "S1") == 0); model_note_request(PAGE_VILLO); model_begin(PAGE_VILLO, 1); strcpy(model_staging_stop_at(0)->id, "V1"); model_staging_note(0); assert(model_stop_at(PAGE_TRANSIT, 0)->row_count == 1); assert(model_stop_count(PAGE_VILLO) == 0); model_commit(PAGE_VILLO); assert(model_stop_at(PAGE_TRANSIT, 0) == NULL); assert(model_stop_count(PAGE_TRANSIT) == 0); assert(model_stop_count(PAGE_VILLO) == 1); model_note_request(PAGE_TRANSIT);}
static void test_recommit_replaces_board(void) { model_begin(PAGE_TRANSIT, 1); put_stop(0, "OLD", "Old", 1); model_commit(PAGE_TRANSIT); Stop *active = model_stop_at(PAGE_TRANSIT, 0); assert(strcmp(active->id, "OLD") == 0); model_begin(PAGE_TRANSIT, 2); put_stop(0, "NEW", "New", 0); put_stop(1, "NEW2", "New2", 0); assert(model_stop_count(PAGE_TRANSIT) == 1); assert(strcmp(model_stop_at(PAGE_TRANSIT, 0)->id, "OLD") == 0); model_commit(PAGE_TRANSIT); assert(model_stop_count(PAGE_TRANSIT) == 2); assert(strcmp(model_stop_at(PAGE_TRANSIT, 0)->id, "NEW") == 0); model_commit(PAGE_TRANSIT); assert(model_stop_count(PAGE_TRANSIT) == 2); assert(strcmp(model_stop_at(PAGE_TRANSIT, 0)->id, "NEW") == 0);}
static void test_partial_staging_never_commits(void) { model_begin(PAGE_TRANSIT, 1); put_stop(0, "GOOD", "G", 0); model_commit(PAGE_TRANSIT); assert(strcmp(model_stop_at(PAGE_TRANSIT, 0)->id, "GOOD") == 0); model_begin(PAGE_TRANSIT, 3); put_stop(0, "P0", "P", 0); put_stop(2, "P2", "P", 0); model_commit(PAGE_TRANSIT); assert(model_stop_count(PAGE_TRANSIT) == 1); assert(strcmp(model_stop_at(PAGE_TRANSIT, 0)->id, "GOOD") == 0); model_commit(PAGE_TRANSIT); assert(strcmp(model_stop_at(PAGE_TRANSIT, 0)->id, "GOOD") == 0); model_begin(PAGE_TRANSIT, 2); put_stop(0, "N0", "N", 0); put_stop(1, "N1", "N", 0); model_commit(PAGE_TRANSIT); assert(model_stop_count(PAGE_TRANSIT) == 2); assert(strcmp(model_stop_at(PAGE_TRANSIT, 0)->id, "N0") == 0);}
static void test_commit_requires_requested_page(void) { model_note_request(PAGE_TRANSIT); model_begin(PAGE_TRANSIT, 1); put_stop(0, "MINE", "M", 0); model_commit(PAGE_TRANSIT); assert(strcmp(model_stop_at(PAGE_TRANSIT, 0)->id, "MINE") == 0); model_begin(PAGE_TRANSIT, 1); put_stop(0, "LATE", "L", 0); model_note_request(PAGE_TRAINS); model_commit(PAGE_TRANSIT); assert(model_stop_count(PAGE_TRANSIT) == 1); assert(strcmp(model_stop_at(PAGE_TRANSIT, 0)->id, "MINE") == 0); model_begin(PAGE_TRAINS, 1); put_stop(0, "TR", "T", 0); model_commit(PAGE_TRAINS); assert(model_stop_count(PAGE_TRAINS) == 1); assert(strcmp(model_stop_at(PAGE_TRAINS, 0)->id, "TR") == 0); model_note_request(PAGE_TRANSIT);}
static void test_glance_row_survives_page_switch(void) { model_begin(PAGE_TRANSIT, 1); Stop *stage = model_staging_stop_at(0); strcpy(stage->id, "S1"); stage->row_count = 1; strcpy(stage->rows[0].line, "92"); strcpy(stage->rows[0].destination, "FORT-JACO"); stage->rows[0].min1 = 4; model_staging_note(0); model_commit(PAGE_TRANSIT); model_note_request(PAGE_VILLO); model_begin(PAGE_VILLO, 0); model_commit(PAGE_VILLO); const Row *row = model_glance_row(); assert(row && strcmp(row->line, "92") == 0 && row->min1 == 4); model_note_request(PAGE_TRANSIT); model_begin(PAGE_TRANSIT, 0); model_commit(PAGE_TRANSIT); assert(model_glance_row() == NULL);}
static void test_begin_clamps_count(void) { model_begin(PAGE_TRANSIT, 200); for (uint8_t i = 0; i < MAX_STOPS; i++) { put_stop(i, "x", "X", 0); } model_commit(PAGE_TRANSIT); assert(model_stop_count(PAGE_TRANSIT) == MAX_STOPS); model_begin(PAGE_TRANSIT, 0); model_commit(PAGE_TRANSIT); assert(model_stop_count(PAGE_TRANSIT) == 0); model_begin(PAGE_TRANSIT, 3); put_stop(0, "a", "A", 0); put_stop(1, "b", "B", 0); put_stop(2, "c", "C", 0); model_commit(PAGE_TRANSIT); assert(model_stop_count(PAGE_TRANSIT) == 3);}
static void test_stop_at_bounds(void) { model_begin(PAGE_TRANSIT, 3); assert(model_staging_stop_at(MAX_STOPS) == NULL); assert(model_staging_stop_at(255) == NULL); assert(model_staging_stop_at(0) != NULL); put_stop(0, "a", "A", 0); put_stop(1, "b", "B", 0); put_stop(2, "c", "C", 0); model_commit(PAGE_TRANSIT); assert(model_staging_stop_at(0) == NULL); assert(model_stop_at(PAGE_TRANSIT, MAX_STOPS) == NULL); assert(model_stop_at(PAGE_TRANSIT, 255) == NULL); assert(model_stop_at(PAGE_TRANSIT, MAX_STOPS - 1) != NULL); assert(model_stop_at(PAGE_COUNT, 0) == NULL);}
static void test_line_complete(void) { model_line_begin(); assert(!model_line_complete()); LineDetail *detail = model_line_detail(); detail->strip_count = 3; detail->strip_received = 2; assert(!model_line_complete()); detail->strip_received = 3; assert(!model_line_complete()); detail->header_received = true; assert(model_line_complete()); detail->strip_count = 0; assert(model_line_complete());}
static void test_line_complete_needs_header(void) { model_line_begin(); assert(!model_line_complete()); LineDetail *detail = model_line_detail(); assert(detail->strip_count == 0 && detail->strip_received == 0); assert(!model_line_complete()); detail->header_received = true; assert(model_line_complete());}
static void put_row(uint8_t stop_index, uint8_t row_index, const char *line, uint8_t kind, uint8_t flags, int16_t min1) { Stop *stop = model_staging_stop_at(stop_index); assert(stop); Row *row = &stop->rows[row_index]; strncpy(row->line, line, LINE_LEN - 1); row->kind = kind; row->flags = flags; row->min1 = min1;}
static void test_classification_matrix(void) { static const struct { uint8_t kind; uint8_t flags; const char *line; DepartureClass class; bool nearby; bool info; RowSelect select; bool alarm; } cases[] = { { 0, 0, "1", DEPARTURE_TRANSIT, true, false, ROW_SELECT_LINE, true }, { 0, 0, "", DEPARTURE_TRANSIT, false, false, ROW_SELECT_INERT, false }, { 0, ROW_FLAG_END, "1", DEPARTURE_TRANSIT, true, true, ROW_SELECT_INERT, false }, { 0, ROW_FLAG_END, "", DEPARTURE_TRANSIT, false, false, ROW_SELECT_INERT, false }, { 1, 0, "M1", DEPARTURE_TRANSIT, true, false, ROW_SELECT_LINE, true }, { 1, 0, "", DEPARTURE_TRANSIT, false, false, ROW_SELECT_INERT, false }, { 1, ROW_FLAG_END, "M1", DEPARTURE_TRANSIT, true, true, ROW_SELECT_INERT, false }, { 1, ROW_FLAG_END, "", DEPARTURE_TRANSIT, false, false, ROW_SELECT_INERT, false }, { 2, 0, "71", DEPARTURE_TRANSIT, true, false, ROW_SELECT_LINE, true }, { 2, 0, "", DEPARTURE_TRANSIT, false, false, ROW_SELECT_INERT, false }, { 2, ROW_FLAG_END, "71", DEPARTURE_TRANSIT, true, true, ROW_SELECT_INERT, false }, { 2, ROW_FLAG_END, "", DEPARTURE_TRANSIT, false, false, ROW_SELECT_INERT, false }, { 3, 0, "IC", DEPARTURE_TRAIN, true, true, ROW_SELECT_STATION, true }, { 3, 0, "", DEPARTURE_TRAIN, true, true, ROW_SELECT_STATION, false }, { 3, ROW_FLAG_END, "IC", DEPARTURE_TRAIN, true, true, ROW_SELECT_INERT, false }, { 3, ROW_FLAG_END, "", DEPARTURE_TRAIN, true, true, ROW_SELECT_INERT, false }, { 4, 0, "V", DEPARTURE_VILLO, true, true, ROW_SELECT_STAND, false }, { 4, 0, "", DEPARTURE_VILLO, true, true, ROW_SELECT_STAND, false }, { 4, ROW_FLAG_END, "V", DEPARTURE_VILLO, true, true, ROW_SELECT_INERT, false }, { 4, ROW_FLAG_END, "", DEPARTURE_VILLO, true, true, ROW_SELECT_INERT, false }, }; for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { Row row = { .kind = cases[i].kind }; strcpy(row.line, cases[i].line); for (int disrupted = 0; disrupted < 2; disrupted++) { row.flags = cases[i].flags | (disrupted ? ROW_FLAG_DISRUPTED : 0); Departure got = model_classify(&row); assert(got.class == cases[i].class); assert(got.nearby == cases[i].nearby); assert(got.info == cases[i].info); assert(got.select == cases[i].select); assert(got.alarm == cases[i].alarm); } }}
static void test_departures_sorts_interleaved(void) { model_begin(PAGE_TRANSIT, 3); put_stop(0, "a", "A", 2); put_stop(1, "b", "B", 2); put_stop(2, "c", "C", 2); put_row(0, 0, "1", 0, 0, 7); put_row(0, 1, "2", 0, 0, 2); put_row(1, 0, "3", 0, 0, 5); put_row(1, 1, "4", 0, 0, 1); put_row(2, 0, "5", 0, 0, 9); put_row(2, 1, "6", 0, 0, 3); model_commit(PAGE_TRANSIT); DepartureRef out[MAX_DEPARTURES]; uint8_t n = model_departures(PAGE_TRANSIT, out, MAX_DEPARTURES); assert(n == 6); assert(out[0].stop == 1 && out[0].row == 1); assert(out[1].stop == 0 && out[1].row == 1); assert(out[2].stop == 2 && out[2].row == 1); assert(out[3].stop == 1 && out[3].row == 0); assert(out[4].stop == 0 && out[4].row == 0); assert(out[5].stop == 2 && out[5].row == 0);}
static void test_departures_absent_last(void) { model_begin(PAGE_TRANSIT, 2); put_stop(0, "a", "A", 2); put_stop(1, "b", "B", 2); put_row(0, 0, "1", 0, 0, -1); put_row(0, 1, "2", 0, 0, 4); put_row(1, 0, "3", 0, 0, -1); put_row(1, 1, "4", 0, 0, 0); model_commit(PAGE_TRANSIT); DepartureRef out[MAX_DEPARTURES]; uint8_t n = model_departures(PAGE_TRANSIT, out, MAX_DEPARTURES); assert(n == 4); assert(out[0].stop == 1 && out[0].row == 1); assert(out[1].stop == 0 && out[1].row == 1); assert(out[2].stop == 0 && out[2].row == 0); assert(out[3].stop == 1 && out[3].row == 0);}
static void test_departures_tie_keeps_board_order(void) { model_begin(PAGE_TRANSIT, 3); put_stop(0, "a", "A", 2); put_stop(1, "b", "B", 1); put_stop(2, "c", "C", 1); put_row(0, 0, "1", 0, 0, 5); put_row(0, 1, "2", 0, 0, 5); put_row(1, 0, "3", 0, 0, 5); put_row(2, 0, "4", 0, 0, 2); model_commit(PAGE_TRANSIT); DepartureRef out[MAX_DEPARTURES]; uint8_t n = model_departures(PAGE_TRANSIT, out, MAX_DEPARTURES); assert(n == 4); assert(out[0].stop == 2 && out[0].row == 0); assert(out[1].stop == 0 && out[1].row == 0); assert(out[2].stop == 0 && out[2].row == 1); assert(out[3].stop == 1 && out[3].row == 0);}
static void test_stop_by_id_resolves_after_resort(void) { model_begin(PAGE_TRANSIT, 2); put_stop(0, "aaa", "A", 1); put_stop(1, "bbb", "B", 1); model_commit(PAGE_TRANSIT); Stop *found = model_stop_by_id(PAGE_TRANSIT, "bbb"); assert(found == model_stop_at(PAGE_TRANSIT, 1)); assert(strcmp(found->name, "B") == 0); model_begin(PAGE_TRANSIT, 2); put_stop(0, "bbb", "B", 1); put_stop(1, "aaa", "A", 1); model_commit(PAGE_TRANSIT); assert(model_stop_by_id(PAGE_TRANSIT, "bbb") == model_stop_at(PAGE_TRANSIT, 0)); assert(model_stop_by_id(PAGE_TRANSIT, "zzz") == NULL); assert(model_stop_by_id(PAGE_TRANSIT, "") == NULL);}
static void test_departures_nearby_appends_info_rows(void) { model_begin(PAGE_TRANSIT, 1); put_stop(0, "a", "A", 5); put_row(0, 0, "1", 0, 0, 50); put_row(0, 1, "51", 0, ROW_FLAG_END, -1); put_row(0, 2, "", 0, 0, 1); put_row(0, 3, "IC", 3, 0, 2); put_row(0, 4, "V", 4, 0, -1); model_commit(PAGE_TRANSIT); DepartureRef out[MAX_DEPARTURES]; uint8_t n = model_departures(PAGE_TRANSIT, out, MAX_DEPARTURES); assert(n == 4); assert(out[0].row == 0); assert(out[1].row == 3); assert(out[2].row == 1); assert(out[3].row == 4);}
static void test_departures_info_rows_respect_cap(void) { model_begin(PAGE_TRANSIT, 1); put_stop(0, "a", "A", 3); put_row(0, 0, "1", 0, 0, 50); put_row(0, 1, "IC", 3, 0, 2); put_row(0, 2, "V", 4, 0, -1); model_commit(PAGE_TRANSIT); DepartureRef out[2]; uint8_t n = model_departures(PAGE_TRANSIT, out, 2); assert(n == 2); assert(out[0].row == 0); assert(out[1].row == 1);}
static void test_departures_single_slot_favors_timed_over_info(void) { model_begin(PAGE_TRANSIT, 1); put_stop(0, "a", "A", 3); put_row(0, 0, "1", 0, 0, 50); put_row(0, 1, "IC", 3, 0, 2); put_row(0, 2, "V", 4, 0, -1); model_commit(PAGE_TRANSIT); DepartureRef out[1]; uint8_t n = model_departures(PAGE_TRANSIT, out, 1); assert(n == 1); assert(out[0].row == 0);}
static void test_departures_single_slot_falls_back_to_info(void) { model_begin(PAGE_TRANSIT, 1); put_stop(0, "a", "A", 2); put_row(0, 0, "IC", 3, 0, 2); put_row(0, 1, "V", 4, 0, -1); model_commit(PAGE_TRANSIT); DepartureRef out[1]; uint8_t n = model_departures(PAGE_TRANSIT, out, 1); assert(n == 1); assert(out[0].row == 0);}
static void test_departures_dense_board_keeps_info_rows(void) { model_begin(PAGE_TRANSIT, 6); for (uint8_t i = 0; i < 5; i++) { char id[ID_LEN]; snprintf(id, sizeof(id), "s%u", i); put_stop(i, id, "STOP", MAX_ROWS); for (uint8_t j = 0; j < MAX_ROWS; j++) { put_row(i, j, "9", 0, 0, (int16_t)((i * 7 + j * 3) % 90)); } } put_stop(5, "s5", "STOP", MAX_ROWS); for (uint8_t j = 0; j < 10; j++) { put_row(5, j, "9", 0, 0, (int16_t)(100 + j)); } put_row(5, 10, "IC", 3, 0, 2); put_row(5, 11, "V", 4, 0, 1); model_commit(PAGE_TRANSIT); DepartureRef out[MAX_DEPARTURES]; uint8_t n = model_departures(PAGE_TRANSIT, out, MAX_DEPARTURES); assert(n == MAX_DEPARTURES); bool has_train = false; bool has_villo = false; for (uint8_t k = 0; k < n; k++) { Row *row = &model_stop_at(PAGE_TRANSIT, out[k].stop)->rows[out[k].row]; if (row->kind == 3) { has_train = true; } if (row->kind == 4) { has_villo = true; } } assert(has_train); assert(has_villo); Row *last = &model_stop_at(PAGE_TRANSIT, out[n - 1].stop)->rows[out[n - 1].row]; Row *second_last = &model_stop_at(PAGE_TRANSIT, out[n - 2].stop)->rows[out[n - 2].row]; assert(model_classify(last).info); assert(model_classify(second_last).info);}
static void test_departures_no_info_rows_fills_all_timed(void) { model_begin(PAGE_TRANSIT, MAX_STOPS); for (uint8_t i = 0; i < MAX_STOPS; i++) { char id[ID_LEN]; snprintf(id, sizeof(id), "s%u", i); put_stop(i, id, "STOP", MAX_ROWS); for (uint8_t j = 0; j < MAX_ROWS; j++) { put_row(i, j, "9", 0, 0, (int16_t)((i * 11 + j * 5) % 90)); } } model_commit(PAGE_TRANSIT); DepartureRef out[MAX_DEPARTURES]; uint8_t n = model_departures(PAGE_TRANSIT, out, MAX_DEPARTURES); assert(n == MAX_DEPARTURES); for (uint8_t k = 0; k < n; k++) { Row *row = &model_stop_at(PAGE_TRANSIT, out[k].stop)->rows[out[k].row]; assert(!model_classify(row).info); }}
static void test_departures_info_bound_caps_reserve(void) { model_begin(PAGE_TRANSIT, MAX_STOPS); put_stop(0, "a", "A", 12); for (uint8_t j = 0; j < 10; j++) { char line[LINE_LEN]; snprintf(line, sizeof(line), "I%u", j); put_row(0, j, line, 3, 0, (int16_t)j); } put_row(0, 10, "1", 0, 0, 200); put_row(0, 11, "2", 0, 0, 201); for (uint8_t i = 1; i < MAX_STOPS; i++) { char id[ID_LEN]; snprintf(id, sizeof(id), "s%u", i); put_stop(i, id, "STOP", MAX_ROWS); for (uint8_t j = 0; j < MAX_ROWS; j++) { put_row(i, j, "9", 0, 0, (int16_t)(10 + i + j)); } } model_commit(PAGE_TRANSIT); DepartureRef out[MAX_DEPARTURES]; uint8_t n = model_departures(PAGE_TRANSIT, out, MAX_DEPARTURES); assert(n == MAX_DEPARTURES); uint8_t info_seen = 0; uint8_t timed_seen = 0; for (uint8_t k = 0; k < n; k++) { Row *row = &model_stop_at(PAGE_TRANSIT, out[k].stop)->rows[out[k].row]; if (model_classify(row).info) { info_seen++; } else { timed_seen++; } } assert(info_seen == MAX_INFO_ROWS); assert(timed_seen == MAX_DEPARTURES - MAX_INFO_ROWS);}
static void test_departures_cap_keeps_earliest(void) { model_begin(PAGE_TRANSIT, 1); put_stop(0, "a", "A", 6); put_row(0, 0, "1", 0, 0, 5); put_row(0, 1, "2", 0, 0, 1); put_row(0, 2, "3", 0, 0, 4); put_row(0, 3, "4", 0, 0, 2); put_row(0, 4, "5", 0, 0, 6); put_row(0, 5, "6", 0, 0, 3); model_commit(PAGE_TRANSIT); DepartureRef out[3]; uint8_t n = model_departures(PAGE_TRANSIT, out, 3); assert(n == 3); assert(out[0].row == 1); assert(out[1].row == 3); assert(out[2].row == 5); assert(model_departures(PAGE_TRANSIT, out, 0) == 0);}
static void test_departures_full_board_bounds(void) { model_begin(PAGE_TRANSIT, MAX_STOPS); for (uint8_t i = 0; i < MAX_STOPS; i++) { char id[ID_LEN]; snprintf(id, sizeof(id), "s%u", i); put_stop(i, id, "STOP", MAX_ROWS); for (uint8_t j = 0; j < MAX_ROWS; j++) { put_row(i, j, "9", 0, 0, (int16_t)((i * 7 + j * 3) % 60)); } } model_commit(PAGE_TRANSIT); DepartureRef out[MAX_DEPARTURES]; uint8_t n = model_departures(PAGE_TRANSIT, out, MAX_DEPARTURES); assert(n == MAX_DEPARTURES); for (uint8_t k = 1; k < n; k++) { Row *prev = &model_stop_at(PAGE_TRANSIT, out[k - 1].stop)->rows[out[k - 1].row]; Row *cur = &model_stop_at(PAGE_TRANSIT, out[k].stop)->rows[out[k].row]; assert(prev->min1 <= cur->min1); }}
static void test_departures_cached_until_dirty(void) { model_begin(PAGE_TRANSIT, 1); Stop *stage = model_staging_stop_at(0); strcpy(stage->id, "S1"); stage->row_count = 2; strcpy(stage->rows[0].line, "10"); stage->rows[0].min1 = 9; strcpy(stage->rows[1].line, "20"); stage->rows[1].min1 = 3; model_staging_note(0); model_commit(PAGE_TRANSIT); Stop *stop = model_stop_at(PAGE_TRANSIT, 0); DepartureRef refs[4]; assert(model_departures(PAGE_TRANSIT, refs, 4) == 2); assert(refs[0].row == 1); stop->rows[0].min1 = 1; assert(model_departures(PAGE_TRANSIT, refs, 4) == 2); assert(refs[0].row == 1); model_mark_dirty(PAGE_TRANSIT); assert(model_departures(PAGE_TRANSIT, refs, 4) == 2); assert(refs[0].row == 0);}
static void test_departures_prefix_matches_full(void) { model_begin(PAGE_TRANSIT, 1); Stop *stage = model_staging_stop_at(0); strcpy(stage->id, "S1"); stage->row_count = 3; strcpy(stage->rows[0].line, "10"); stage->rows[0].min1 = 9; strcpy(stage->rows[1].line, "20"); stage->rows[1].min1 = 3; strcpy(stage->rows[2].line, "30"); stage->rows[2].min1 = 5; model_staging_note(0); model_commit(PAGE_TRANSIT); DepartureRef all[8]; DepartureRef one; uint8_t n = model_departures(PAGE_TRANSIT, all, 8); assert(n == 3); assert(model_departures(PAGE_TRANSIT, &one, 1) == 1); assert(one.stop == all[0].stop && one.row == all[0].row);}
static void test_distinct_stops_counts_cached_refs(void) { DepartureRef refs[6] = { { .stop = 0, .row = 0 }, { .stop = 0, .row = 1 }, { .stop = 7, .row = 0 }, { .stop = 8, .row = 0 }, { .stop = 11, .row = 0 }, { .stop = 11, .row = 1 }, }; assert(model_distinct_stops(refs, 6) == 4); assert(model_distinct_stops(refs, 0) == 0);}
static void test_stale_flag(void) { model_note_data_arrival(PAGE_TRANSIT, 1000); assert(!model_stale(PAGE_TRANSIT, 1000)); assert(!model_stale(PAGE_TRANSIT, 1045)); assert(model_stale(PAGE_TRANSIT, 1046)); assert(model_stale(PAGE_TRANSIT, 9999)); model_note_data_arrival(PAGE_TRANSIT, 9999); assert(!model_stale(PAGE_TRANSIT, 10000));}
static void test_staleness_pure_decisions(void) { assert(model_staleness_action(1000, 1000, true, false) == STALENESS_NONE); assert(model_staleness_action(1044, 1000, true, false) == STALENESS_NONE); assert(model_staleness_action(1045, 1000, true, false) == STALENESS_NONE); assert(model_staleness_action(1046, 1000, true, false) == STALENESS_NUDGE); assert(model_staleness_action(1046, 1000, true, true) == STALENESS_NONE); assert(model_staleness_action(1046, 1000, false, false) == STALENESS_NONE); assert(model_staleness_action(2000, 1000, false, true) == STALENESS_NONE);}
static void test_check_staleness_stateful_cycle(void) { model_set_state(STATE_OK); model_note_data_arrival(PAGE_TRANSIT, 1000); assert(model_check_staleness(PAGE_TRANSIT, 1010) == STALENESS_NONE); assert(model_check_staleness(PAGE_TRANSIT, 1050) == STALENESS_NUDGE); assert(model_check_staleness(PAGE_TRANSIT, 1051) == STALENESS_NONE); assert(model_check_staleness(PAGE_TRANSIT, 2000) == STALENESS_NONE); model_note_data_arrival(PAGE_TRANSIT, 2000); assert(model_check_staleness(PAGE_TRANSIT, 2010) == STALENESS_NONE); assert(model_check_staleness(PAGE_TRANSIT, 2050) == STALENESS_NUDGE); model_set_state(STATE_NO_PHONE); assert(model_check_staleness(PAGE_TRANSIT, 3000) == STALENESS_NONE);}
int main(void) { model_init(); assert(model_wakeup_id() == -1); test_staging_commit_swap(); test_recommit_replaces_board(); test_partial_staging_never_commits(); test_commit_requires_requested_page(); test_glance_row_survives_page_switch(); test_begin_clamps_count(); test_stop_at_bounds(); test_line_complete(); test_line_complete_needs_header(); test_classification_matrix(); test_stop_by_id_resolves_after_resort(); test_departures_sorts_interleaved(); test_departures_absent_last(); test_departures_tie_keeps_board_order(); test_departures_nearby_appends_info_rows(); test_departures_info_rows_respect_cap(); test_departures_single_slot_favors_timed_over_info(); test_departures_single_slot_falls_back_to_info(); test_departures_dense_board_keeps_info_rows(); test_departures_no_info_rows_fills_all_timed(); test_departures_info_bound_caps_reserve(); test_departures_cap_keeps_earliest(); test_departures_full_board_bounds(); test_departures_cached_until_dirty(); test_departures_prefix_matches_full(); test_distinct_stops_counts_cached_refs(); test_stale_flag(); test_staleness_pure_decisions(); test_check_staleness_stateful_cycle(); return 0;}