mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
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
This commit is contained in:
1 parent
9f107b6e60
commit
dcd3cd5a98
7 files changed
+376
-4
No files matched your search
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* support.c — mock hardware edges + test environment for the host harness.
|
||||
*
|
||||
* Everything the firmware expects the platform/other-TUs to provide, but which
|
||||
* has no business touching real hardware in a unit test, lives here: the serial
|
||||
* ring, the command/history buffers, the character-output sink, and stubs for
|
||||
* the print helpers. The buffers are sized EXACTLY as on target so that
|
||||
* AddressSanitizer redzones catch the same off-by-one overflows the 8051 hits.
|
||||
*/
|
||||
#include "sdcc_shim.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
/* Declared by hand instead of <unistd.h>, whose sleep() prototype clashes with
|
||||
* the firmware's own sleep() declaration in rtl837x_common.h. */
|
||||
extern unsigned int alarm(unsigned int);
|
||||
extern void _exit(int);
|
||||
#include "rtl837x_common.h" /* SBUF_SIZE, CMD_BUF_SIZE, CMD_HISTORY_SIZE, decls */
|
||||
#include "support.h"
|
||||
|
||||
/* --- symbols the editor/parser reference (extern in the firmware headers) --- */
|
||||
__xdata volatile uint8_t sbuf_ptr;
|
||||
__xdata uint8_t sbuf[SBUF_SIZE];
|
||||
uint8_t cmd_buffer[CMD_BUF_SIZE]; /* ASan-instrumented: OOB caught */
|
||||
uint8_t cmd_available;
|
||||
uint8_t err_status;
|
||||
uint8_t cmd_history[CMD_HISTORY_SIZE];
|
||||
uint16_t cmd_history_ptr;
|
||||
|
||||
/* editor-internal read pointer (defined in cmd_editor.c, external linkage) */
|
||||
extern __xdata uint8_t l;
|
||||
|
||||
/* the editor entry points under test */
|
||||
void cmd_editor_init(void);
|
||||
void cmd_edit(void);
|
||||
|
||||
/* ------------------------------- output sink ------------------------------- */
|
||||
char out_buf[8192];
|
||||
size_t out_len;
|
||||
|
||||
void out_reset(void) { out_len = 0; out_buf[0] = 0; }
|
||||
|
||||
static void out_putc(char c) {
|
||||
if (out_len < sizeof(out_buf) - 1) { out_buf[out_len++] = c; out_buf[out_len] = 0; }
|
||||
}
|
||||
|
||||
/* --- mocked print helpers (signatures match rtl837x_common.h post-shim) ---- */
|
||||
void write_char(char c) { out_putc(c); }
|
||||
void print_string(char *p) { while (*p) out_putc(*p++); }
|
||||
void print_string_x(char *p) { while (*p) out_putc(*p++); }
|
||||
void print_cmd_prompt(void) { print_string((char *)"> "); }
|
||||
void itoa(uint8_t v) { /* firmware prints a decimal number */
|
||||
char t[4]; int n = 0;
|
||||
if (!v) { out_putc('0'); return; }
|
||||
while (v) { t[n++] = '0' + v % 10; v /= 10; }
|
||||
while (n) out_putc(t[--n]);
|
||||
}
|
||||
|
||||
/* ------------------------------ environment -------------------------------- */
|
||||
void env_reset(void) {
|
||||
sbuf_ptr = 0;
|
||||
t_memset(sbuf, 0, sizeof(sbuf));
|
||||
t_memset(cmd_buffer, 0, sizeof(cmd_buffer));
|
||||
t_memset(cmd_history, 0, sizeof(cmd_history));
|
||||
cmd_history_ptr = 0;
|
||||
cmd_available = 0;
|
||||
err_status = 0;
|
||||
out_reset();
|
||||
cmd_editor_init(); /* sets l = sbuf_ptr, clears cursor/len */
|
||||
}
|
||||
|
||||
/* Push one byte into the ring, draining first if it is full (leave 1 slot). */
|
||||
static void ring_push(uint8_t b) {
|
||||
while (((sbuf_ptr + 1) & SBUF_MASK) == l)
|
||||
cmd_edit(); /* drain; under the C4 bug this spins */
|
||||
sbuf[sbuf_ptr] = b;
|
||||
sbuf_ptr = (sbuf_ptr + 1) & SBUF_MASK;
|
||||
}
|
||||
|
||||
void feed_n(const char *s, size_t n) {
|
||||
for (size_t i = 0; i < n; i++) ring_push((uint8_t)s[i]);
|
||||
cmd_edit();
|
||||
}
|
||||
void feed(const char *s) { feed_n(s, t_strlen(s)); }
|
||||
|
||||
/* -------------------------------- watchdog --------------------------------- */
|
||||
static const char *wd_what;
|
||||
static void wd_fire(int sig) {
|
||||
(void)sig;
|
||||
/* async-signal-unsafe write is acceptable: we abort immediately after. */
|
||||
fprintf(stderr, "\n HANG DETECTED (watchdog): %s\n", wd_what ? wd_what : "?");
|
||||
_exit(2);
|
||||
}
|
||||
void watchdog_arm(unsigned seconds, const char *what) {
|
||||
wd_what = what;
|
||||
signal(SIGALRM, wd_fire);
|
||||
alarm(seconds);
|
||||
}
|
||||
void watchdog_disarm(void) { alarm(0); }
|
||||
|
||||
/* -------------------------------- framework -------------------------------- */
|
||||
int tests_run, tests_failed;
|
||||
Reference in new issue
Block a user