| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- /*
- * Malformed/edge-case NMEA regression test.
- *
- * Feeds soft/nmea.c's real gps_parse() (through the same PC_BUILD shim as
- * the rest of this test tool) a fixture of real and synthetic sentences
- * that are known to be awkward: invalid epochs from an actual hike log,
- * truncated/bad-checksum/short-dialect sentences, and a run long enough to
- * exercise the HDOP-based speed-sample gating added after that hike showed
- * a single poor-quality fix (HDOP 16, 44.8 km/h) skewing the whole-session
- * average.
- *
- * Nothing in malformed_test.nmea should crash the parser (run under a tool
- * like valgrind or ASan for a stronger guarantee than "the process exited
- * zero"), and the checks below assert the speed average is actually left
- * alone by the invalid/poor-quality samples, and is capped rather than
- * excluded for a genuinely fast, good-quality one.
- *
- * Usage: ./test-malformed [malformed_test.nmea]
- */
- #define PC_BUILD
- #include "main.h"
- volatile struct system_s System;
- struct location_s location;
- time_t utc;
- unsigned char FLAGS = 0;
- volatile unsigned char reset_cause = 0;
- FILE *debug_output = NULL;
- __flash const char gnss_name_stub[] = "n/a";
- __flash const char *gnss_names[] = { gnss_name_stub, gnss_name_stub, gnss_name_stub, gnss_name_stub, gnss_name_stub, gnss_name_stub };
- __flash const char uart_log_mode_stub[] = "n/a";
- __flash const char *uart_log_mode_names[] = { uart_log_mode_stub, uart_log_mode_stub, uart_log_mode_stub };
- time_t gps_parse(const char *nmea);
- char *get_iso_time(time_t time, unsigned char local) {
- (void)local;
- static char output[32];
- struct tm *ct = gmtime(&time);
- sprintf(output, "%04d-%02d-%02dT%02d:%02d:%02d.000Z",
- ct->tm_year + 1900, ct->tm_mon + 1, ct->tm_mday,
- ct->tm_hour, ct->tm_min, ct->tm_sec);
- return output;
- }
- void iso_time_to_filename(char *time) {
- (void)time;
- }
- static int failures;
- static void check(int cond, const char *what) {
- if (!cond) {
- fprintf(stderr, "FAIL: %s\n", what);
- failures++;
- } else {
- printf("ok: %s\n", what);
- }
- }
- /* Recognize the known-invalid lines from malformed_test.nmea's section 1,
- * to check precisely that they leave the speed average alone - not just
- * that nothing crashes. */
- static int is_invalid_epoch_line(const char *line) {
- return strstr(line, ",V,,,,,") != NULL /* RMC: empty lat/lon, status V */
- || (strstr(line, "VTG") && strstr(line, ",N*")); /* VTG: mode indicator N (not valid) */
- }
- int main(int argc, char *argv[]) {
- const char *path = argc > 1 ? argv[1] : "malformed_test.nmea";
- FILE *f = fopen(path, "r");
- char raw[512];
- char line[512];
- int total_lines = 0;
- if (!f) {
- fprintf(stderr, "Cannot open %s\n", path);
- return 1;
- }
- memset((void *)&System, 0, sizeof(System));
- memset(&location, 0, sizeof(location));
- System.conf.min_sats = 4;
- utc = 0;
- while (fgets(raw, sizeof raw, f)) {
- unsigned long int count_before, accum_before;
- size_t len = strlen(raw);
- while (len && (raw[len - 1] == '\n' || raw[len - 1] == '\r'))
- raw[--len] = '\0';
- if (len == 0 || raw[0] == '#')
- continue; /* blank line or fixture comment, not fed to the parser */
- snprintf(line, sizeof line, "%s\r\n", raw);
- total_lines++;
- count_before = System.speed_sample_count;
- accum_before = System.speed_accum_x100;
- gps_parse(line); /* must not crash, regardless of how malformed the line is */
- if (is_invalid_epoch_line(raw)) {
- char what[560];
- snprintf(what, sizeof what, "no speed sample added by: %s", raw);
- check(System.speed_sample_count == count_before && System.speed_accum_x100 == accum_before, what);
- }
- }
- fclose(f);
- printf("\nParsed %d lines without crashing.\n", total_lines);
- check(total_lines > 250, "fixture file actually has content");
- /* Section 3: the HDOP-16.2, 44.8 km/h sample must be excluded outright -
- * the average right after it must still equal the ~4.3 km/h warm-up
- * average, not be pulled up towards 44.8. */
- check(System.speed_sample_count >= 64, "warm-up samples were accepted");
- {
- double avg = (double)System.speed_accum_x100 / System.speed_sample_count / 100.0;
- char what[128];
- snprintf(what, sizeof what, "average (%.2f km/h) unaffected by the HDOP-16.2 44.8 km/h sample", avg);
- check(avg > 2.0 && avg < 6.0, what);
- }
- /* The 200 km/h, good-HDOP spike must be capped (added to the sum at
- * something well below 200, per SPEED_SAMPLE_CAP_MULT/FLOOR), not
- * excluded outright: a real fast segment (car/train) must still count. */
- {
- double avg_after = (double)System.speed_accum_x100 / System.speed_sample_count / 100.0;
- char what[160];
- snprintf(what, sizeof what, "200 km/h spike was capped, not excluded nor let through raw (average now %.2f km/h)", avg_after);
- check(avg_after > 2.5 && avg_after < 15.0, what);
- }
- if (failures) {
- printf("\n%d check(s) FAILED\n", failures);
- return 1;
- }
- printf("\nAll checks passed.\n");
- return 0;
- }
|