Files
RTLPlayground/test/test_cmd_editor.c
T
bloqaudio dcd3cd5a98 test: add host-side unit-test harness, covering cmd_editor
Compile selected firmware translation units unmodified under host gcc with
AddressSanitizer and UBSan, force-including a shim header that erases the
SDCC 8051 keywords, and link them against a mocked serial ring and console.
A first suite covers the CLI line editor: line entry, backspace editing,
and both long-input overflow cases (length cap and ring-pointer stall).

The only firmware-source change is one #ifndef RTLP_HOST_TEST block in
rtl837x_common.h grouping the libc-named prototypes (sleep/memcpy/memset/
strlen/strcpy/strcmp) so host builds take them from the C library instead;
on-target builds are unaffected.

Run with: make -C test
2026-09-14 14:02:35 -05:00

77 lines
2.9 KiB
C

/*
* test_cmd_editor.c — host unit tests for cmd_editor.c (the serial line editor).
*
* Focus: finding C4 (08-findings.md) — a full input line both HANGS the editor
* (continue without advancing the ring read pointer) and OVERFLOWS cmd_buffer by
* one byte (guard off-by-one lets cmd_line_len reach CMD_BUF_SIZE).
*
* These tests are RED against the current source and GREEN after the fix, with
* no hardware and no flash cycle. The hang is caught by a SIGALRM watchdog; the
* overflow is caught by AddressSanitizer redzones on cmd_buffer[].
*/
#include "sdcc_shim.h"
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "rtl837x_common.h" /* CMD_BUF_SIZE */
#include "support.h"
/* editor state (globals in cmd_editor.c, external linkage) */
extern __xdata uint8_t cmd_line_len;
extern uint8_t cmd_buffer[];
extern uint8_t cmd_available;
static void test_basic_line(void) {
printf("[test] basic line entry + Enter\n");
env_reset();
feed("vlan 199\n");
CHECK(cmd_available == 1, "Enter marks a command available");
CHECK(t_strcmp((char *)cmd_buffer, "vlan 199") == 0, "cmd_buffer holds the typed line");
}
static void test_backspace(void) {
printf("[test] backspace edits the line\n");
env_reset();
feed("helo");
feed("\b"); /* delete the 'o' -> wait, cursor at end: removes last char */
feed("lo\n");
CHECK(t_strcmp((char *)cmd_buffer, "hello") == 0, "backspace then retype yields 'hello'");
}
/* C4 part 1: a line that fills the buffer must not spin forever. */
static void test_full_line_no_hang(void) {
char big[200];
printf("[test] C4: overlong line does not hang the editor\n");
env_reset();
t_memset(big, 'a', sizeof(big));
watchdog_arm(5, "C4 full-line hang (editor spun without advancing ring ptr)");
feed_n(big, sizeof(big)); /* 200 printable chars, no newline */
watchdog_disarm();
/* Reaching here means no hang. With the fix the line caps one short of full. */
CHECK(cmd_line_len == CMD_BUF_SIZE - 1,
"line length caps at CMD_BUF_SIZE-1 (room reserved for NUL)");
}
/* C4 part 2: filling the buffer then Enter must not write cmd_buffer[CMD_BUF_SIZE]. */
static void test_full_line_no_overflow(void) {
char full[CMD_BUF_SIZE]; /* 128 printable chars, exactly buffer size */
printf("[test] C4: full line + Enter does not overflow cmd_buffer\n");
env_reset();
t_memset(full, 'b', sizeof(full));
watchdog_arm(5, "C4 overflow-path hang");
feed_n(full, sizeof(full));
feed("\n"); /* under the bug, writes cmd_buffer[128] -> ASan trap */
watchdog_disarm();
CHECK(1, "128-char line + Enter completes without ASan overflow");
}
int main(void) {
printf("== cmd_editor host tests ==\n");
test_basic_line();
test_backspace();
test_full_line_no_hang();
test_full_line_no_overflow();
printf("\n%d checks, %d failed\n", tests_run, tests_failed);
return tests_failed ? 1 : 0;
}