// 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 #include #include #include #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; }