211 lines
5.6 KiB
C
211 lines
5.6 KiB
C
// inputHost.c - Host-cc harness for the typed-character queue
|
|
// (jlInputGetChar / jlInputCharPush in src/core/input.c). Exercises
|
|
// FIFO ordering, the character filter, drop-newest overflow, reset
|
|
// semantics, and the jlInputPoll refill path via a mocked
|
|
// jlpInputPoll.
|
|
//
|
|
// Built by scripts/check-input.sh with -DJOEYLIB_PLATFORM_BLANK so
|
|
// port.h routes jlpInputPoll/jlpJoystickReset to the mocks below.
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "joey/input.h"
|
|
#include "inputInternal.h"
|
|
#include "port.h"
|
|
|
|
// Characters the mocked jlpInputPoll delivers on its next call
|
|
// (one-shot; cleared after delivery).
|
|
static const uint8_t *gMockChars = NULL;
|
|
static int gMockCount = 0;
|
|
|
|
// Prototypes (harness-local, alphabetical).
|
|
static bool checkEmpty(const char *name);
|
|
static bool popExpect(const char *name, const uint8_t *expect, int count);
|
|
static void pushAll(const uint8_t *chars, int count);
|
|
static bool runFilter(void);
|
|
static bool runInterleave(void);
|
|
static bool runOrder(void);
|
|
static bool runOverflow(void);
|
|
static bool runPollRefill(void);
|
|
static bool runReset(void);
|
|
|
|
|
|
void jlpInputPoll(void) {
|
|
int i;
|
|
|
|
for (i = 0; i < gMockCount; i++) {
|
|
jlInputCharPush(gMockChars[i]);
|
|
}
|
|
gMockCount = 0;
|
|
}
|
|
|
|
|
|
void jlpJoystickReset(jlJoystickE js) {
|
|
(void)js;
|
|
}
|
|
|
|
|
|
static bool checkEmpty(const char *name) {
|
|
int got;
|
|
|
|
got = jlInputGetChar();
|
|
if (got != -1) {
|
|
printf(" %s: expected empty (-1), got %d\n", name, got);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool popExpect(const char *name, const uint8_t *expect, int count) {
|
|
int got;
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
got = jlInputGetChar();
|
|
if (got != (int)expect[i]) {
|
|
printf(" %s: index %d: expected 0x%02X, got %d\n",
|
|
name, i, expect[i], got);
|
|
return false;
|
|
}
|
|
}
|
|
return checkEmpty(name);
|
|
}
|
|
|
|
|
|
static void pushAll(const uint8_t *chars, int count) {
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
jlInputCharPush(chars[i]);
|
|
}
|
|
}
|
|
|
|
|
|
// Control characters other than BS/TAB/CR/ESC, plus DEL and 8-bit
|
|
// values, must never surface; the documented four and the printable
|
|
// range must pass through unchanged.
|
|
static bool runFilter(void) {
|
|
static const uint8_t rejected[] = { 0x00, 0x01, 0x07, 0x0A, 0x0C, 0x1F, 0x7F };
|
|
static const uint8_t accepted[] = {
|
|
JL_CHAR_BACKSPACE, JL_CHAR_TAB, JL_CHAR_RETURN, JL_CHAR_ESCAPE,
|
|
0x20, '0', 'Z', 'z', '~', 0x7E
|
|
};
|
|
bool ok;
|
|
|
|
jlInputCharReset();
|
|
pushAll(rejected, (int)sizeof(rejected));
|
|
ok = checkEmpty("filter-rejects");
|
|
pushAll(accepted, (int)sizeof(accepted));
|
|
ok &= popExpect("filter-accepts", accepted, (int)sizeof(accepted));
|
|
printf(" filter: %s\n", ok ? "PASS" : "FAIL");
|
|
return ok;
|
|
}
|
|
|
|
|
|
// Pops interleaved with pushes keep FIFO order across the wrap point.
|
|
static bool runInterleave(void) {
|
|
static const uint8_t first[] = { 'a', 'b', 'c' };
|
|
static const uint8_t second[] = { 'd', 'e' };
|
|
static const uint8_t rest[] = { 'b', 'c', 'd', 'e' };
|
|
bool ok;
|
|
int got;
|
|
|
|
jlInputCharReset();
|
|
pushAll(first, (int)sizeof(first));
|
|
got = jlInputGetChar();
|
|
ok = (got == 'a');
|
|
if (!ok) {
|
|
printf(" interleave: expected 'a', got %d\n", got);
|
|
}
|
|
pushAll(second, (int)sizeof(second));
|
|
ok &= popExpect("interleave", rest, (int)sizeof(rest));
|
|
printf(" interleave: %s\n", ok ? "PASS" : "FAIL");
|
|
return ok;
|
|
}
|
|
|
|
|
|
// The definition-of-done string from the RetroNet handoff must come
|
|
// back byte-for-byte.
|
|
static bool runOrder(void) {
|
|
static const char target[] = "192.168.1.10:6510";
|
|
bool ok;
|
|
|
|
jlInputCharReset();
|
|
pushAll((const uint8_t *)target, (int)(sizeof(target) - 1));
|
|
ok = popExpect("order", (const uint8_t *)target, (int)(sizeof(target) - 1));
|
|
printf(" order: %s\n", ok ? "PASS" : "FAIL");
|
|
return ok;
|
|
}
|
|
|
|
|
|
// Overflow drops the NEWEST characters: exactly JL_CHAR_QUEUE_SIZE - 1
|
|
// survive, and they are the first ones pushed.
|
|
static bool runOverflow(void) {
|
|
uint8_t burst[JL_CHAR_QUEUE_SIZE + 8];
|
|
bool ok;
|
|
int i;
|
|
|
|
jlInputCharReset();
|
|
for (i = 0; i < (int)sizeof(burst); i++) {
|
|
burst[i] = (uint8_t)('!' + (i % 0x5E));
|
|
}
|
|
pushAll(burst, (int)sizeof(burst));
|
|
ok = popExpect("overflow", burst, JL_CHAR_QUEUE_SIZE - 1);
|
|
printf(" overflow: %s\n", ok ? "PASS" : "FAIL");
|
|
return ok;
|
|
}
|
|
|
|
|
|
// The real app-facing path: jlInputPoll runs the port poll, which
|
|
// pushes translated characters; jlInputGetChar then pops them.
|
|
static bool runPollRefill(void) {
|
|
static const uint8_t typed[] = { 'H', 'i', '!', JL_CHAR_RETURN };
|
|
bool ok;
|
|
|
|
jlInputCharReset();
|
|
gMockChars = typed;
|
|
gMockCount = (int)sizeof(typed);
|
|
jlInputPoll();
|
|
ok = popExpect("poll-refill", typed, (int)sizeof(typed));
|
|
jlInputPoll();
|
|
ok &= checkEmpty("poll-refill-once");
|
|
printf(" poll-refill: %s\n", ok ? "PASS" : "FAIL");
|
|
return ok;
|
|
}
|
|
|
|
|
|
// jlInputCharReset empties the queue (the jlInit path).
|
|
static bool runReset(void) {
|
|
static const uint8_t some[] = { 'x', 'y', 'z' };
|
|
bool ok;
|
|
|
|
jlInputCharReset();
|
|
pushAll(some, (int)sizeof(some));
|
|
jlInputCharReset();
|
|
ok = checkEmpty("reset");
|
|
printf(" reset: %s\n", ok ? "PASS" : "FAIL");
|
|
return ok;
|
|
}
|
|
|
|
|
|
int main(void) {
|
|
bool ok = true;
|
|
|
|
printf("inputHost: typed-character queue scenarios\n");
|
|
ok &= runOrder();
|
|
ok &= runFilter();
|
|
ok &= runOverflow();
|
|
ok &= runInterleave();
|
|
ok &= runReset();
|
|
ok &= runPollRefill();
|
|
if (!ok) {
|
|
printf("inputHost: FAIL\n");
|
|
return 1;
|
|
}
|
|
printf("inputHost: PASS\n");
|
|
return 0;
|
|
}
|