test: cover keyboardTest
This commit is contained in:
1 parent
fdaec0bfab
commit
58cf2424e4
1 file changed
+216
@@ -0,0 +1,216 @@
|
||||
#include "ktest.h"
|
||||
#include "keyboard.h"
|
||||
#include "string.h"
|
||||
|
||||
/*
|
||||
* Coverage for keyboard.c's per-reader console input queues (keyboard.h) --
|
||||
* the fan-out/blocking-channel machinery added this wave to close the gap
|
||||
* called out at the end of the previous one: a single global keyboard
|
||||
* buffer shared by every consumer, with no per-process queueing and no
|
||||
* scheduler-visible blocking. What is pure state machine (open/close,
|
||||
* scancode decoding, fan-out to N independent queues, exhaustion/reuse)
|
||||
* lives here, driven directly through keyboardHandleScancode -- the same
|
||||
* seam the live PS/2 IRQ handler (keyboardHandler) calls into, just fed a
|
||||
* synthetic scancode instead of a real inb() -- exactly the way
|
||||
* pipeShareTest.c drives pipe.c's state machine without a live scheduler.
|
||||
* The genuine cross-process park/wake handshake (real blocking read() on
|
||||
* fd 0 from two independent ring-3 processes, woken by a real keystroke)
|
||||
* only runs for real at boot (kmain.c's consolereader1/consolereader2 demo,
|
||||
* needles "consolereader1: got byte" / "consolereader2: got byte" in
|
||||
* tools/ci-test.sh), matching how every other blocking-I/O subsystem in
|
||||
* this kernel splits its test coverage.
|
||||
*
|
||||
* Scancode reference (set 1, US QWERTY, keyboard.c's own tables): 0x25 is
|
||||
* 'k'/'K', 0x2A/0xAA are left-shift make/break.
|
||||
*/
|
||||
#define scancodeK 0x25
|
||||
#define scancodeLeftShiftMake 0x2A
|
||||
#define scancodeLeftShiftBreak 0xAA
|
||||
|
||||
KTEST(keyboardOpenReaderReturnsDistinctHandlesAndEmptyReadWouldBlock)
|
||||
{
|
||||
int a = keyboardOpenReader();
|
||||
int b = keyboardOpenReader();
|
||||
KT_NE(t, a, consoleReaderInvalid);
|
||||
KT_NE(t, b, consoleReaderInvalid);
|
||||
KT_NE(t, a, b);
|
||||
|
||||
char buffer[4];
|
||||
KT_EQ(t, keyboardReaderRead(a, buffer, sizeof(buffer)), -1);
|
||||
KT_EQ(t, keyboardReaderRead(b, buffer, sizeof(buffer)), -1);
|
||||
|
||||
keyboardCloseReader(a);
|
||||
keyboardCloseReader(b);
|
||||
}
|
||||
|
||||
KTEST(keyboardReaderReadRejectsInvalidHandlesAndZeroLength)
|
||||
{
|
||||
KT_EQ(t, keyboardReaderRead(consoleReaderInvalid, nullptr, 4), -1);
|
||||
KT_EQ(t, keyboardReaderRead(9999, nullptr, 4), -1);
|
||||
|
||||
int h = keyboardOpenReader();
|
||||
KT_NE(t, h, consoleReaderInvalid);
|
||||
KT_EQ(t, keyboardReaderRead(h, nullptr, 0), 0);
|
||||
keyboardCloseReader(h);
|
||||
|
||||
/* Once closed, the handle is dead -- not silently reusable by the
|
||||
caller who just gave it up. */
|
||||
KT_EQ(t, keyboardReaderRead(h, nullptr, 0), -1);
|
||||
}
|
||||
|
||||
KTEST(keyboardHandleScancodeBroadcastsToEveryOpenReaderIndependently)
|
||||
{
|
||||
int a = keyboardOpenReader();
|
||||
int b = keyboardOpenReader();
|
||||
KT_NE(t, a, consoleReaderInvalid);
|
||||
KT_NE(t, b, consoleReaderInvalid);
|
||||
|
||||
keyboardHandleScancode(scancodeK);
|
||||
|
||||
char ca = 0, cb = 0;
|
||||
KT_EQ(t, keyboardReaderRead(a, &ca, 1), 1);
|
||||
KT_EQ(t, keyboardReaderRead(b, &cb, 1), 1);
|
||||
KT_EQ(t, ca, 'k');
|
||||
KT_EQ(t, cb, 'k');
|
||||
|
||||
/* Each reader's copy is independent: draining a's queue must not touch
|
||||
b's, and vice versa -- proven above by both still having their own
|
||||
byte to read rather than one starving the other. Both are empty now. */
|
||||
KT_EQ(t, keyboardReaderRead(a, &ca, 1), -1);
|
||||
KT_EQ(t, keyboardReaderRead(b, &cb, 1), -1);
|
||||
|
||||
keyboardCloseReader(a);
|
||||
keyboardCloseReader(b);
|
||||
}
|
||||
|
||||
KTEST(keyboardHandleScancodeAppliesShiftMakeAndBreak)
|
||||
{
|
||||
int h = keyboardOpenReader();
|
||||
KT_NE(t, h, consoleReaderInvalid);
|
||||
|
||||
keyboardHandleScancode(scancodeK);
|
||||
char c = 0;
|
||||
KT_EQ(t, keyboardReaderRead(h, &c, 1), 1);
|
||||
KT_EQ(t, c, 'k');
|
||||
|
||||
keyboardHandleScancode(scancodeLeftShiftMake);
|
||||
keyboardHandleScancode(scancodeK);
|
||||
KT_EQ(t, keyboardReaderRead(h, &c, 1), 1);
|
||||
KT_EQ(t, c, 'K');
|
||||
|
||||
keyboardHandleScancode(scancodeLeftShiftBreak);
|
||||
keyboardHandleScancode(scancodeK);
|
||||
KT_EQ(t, keyboardReaderRead(h, &c, 1), 1);
|
||||
KT_EQ(t, c, 'k');
|
||||
|
||||
keyboardCloseReader(h);
|
||||
}
|
||||
|
||||
KTEST(keyboardHandleScancodeIgnoresKeyReleaseAndUnmappedCodes)
|
||||
{
|
||||
int h = keyboardOpenReader();
|
||||
KT_NE(t, h, consoleReaderInvalid);
|
||||
|
||||
/* Key-release (high bit set) for 'k' must queue nothing. */
|
||||
keyboardHandleScancode(scancodeK | 0x80);
|
||||
char c;
|
||||
KT_EQ(t, keyboardReaderRead(h, &c, 1), -1);
|
||||
|
||||
/* Scancode 0 maps to 0 (no character) in both tables. */
|
||||
keyboardHandleScancode(0);
|
||||
KT_EQ(t, keyboardReaderRead(h, &c, 1), -1);
|
||||
|
||||
keyboardCloseReader(h);
|
||||
}
|
||||
|
||||
KTEST(keyboardReaderReadDeliversQueuedBytesInFifoOrderAcrossPartialReads)
|
||||
{
|
||||
int h = keyboardOpenReader();
|
||||
KT_NE(t, h, consoleReaderInvalid);
|
||||
|
||||
/* "kkk" via three separate scancode events, like three real keystrokes. */
|
||||
keyboardHandleScancode(scancodeK);
|
||||
keyboardHandleScancode(scancodeK);
|
||||
keyboardHandleScancode(scancodeK);
|
||||
|
||||
char buffer[8];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
/* Ask for only 2 of the 3 queued bytes -- must not block or fail, and
|
||||
must leave the third for the next call, same partial-transfer
|
||||
contract ring.h's ringRead already documents. */
|
||||
int64 got = keyboardReaderRead(h, buffer, 2);
|
||||
KT_EQ(t, got, 2);
|
||||
KT_EQ(t, buffer[0], 'k');
|
||||
KT_EQ(t, buffer[1], 'k');
|
||||
|
||||
got = keyboardReaderRead(h, buffer, 8);
|
||||
KT_EQ(t, got, 1);
|
||||
KT_EQ(t, buffer[0], 'k');
|
||||
|
||||
KT_EQ(t, keyboardReaderRead(h, buffer, 1), -1);
|
||||
|
||||
keyboardCloseReader(h);
|
||||
}
|
||||
|
||||
KTEST(keyboardReaderChannelIsNullForInvalidAndDistinctPerReader)
|
||||
{
|
||||
KT_NULL(t, keyboardReaderChannel(consoleReaderInvalid));
|
||||
KT_NULL(t, keyboardReaderChannel(9999));
|
||||
|
||||
int a = keyboardOpenReader();
|
||||
int b = keyboardOpenReader();
|
||||
KT_NE(t, a, consoleReaderInvalid);
|
||||
KT_NE(t, b, consoleReaderInvalid);
|
||||
|
||||
const void* channelA = keyboardReaderChannel(a);
|
||||
const void* channelB = keyboardReaderChannel(b);
|
||||
KT_NOTNULL(t, channelA);
|
||||
KT_NOTNULL(t, channelB);
|
||||
KT_NE(t, (int64)(uint64)channelA, (int64)(uint64)channelB);
|
||||
|
||||
/* Stable across repeated calls -- schedBlockCurrent/schedWakeChannel
|
||||
depend on the same reader always producing the same identity. */
|
||||
KT_EQP(t, keyboardReaderChannel(a), channelA);
|
||||
|
||||
/* A closed reader's channel must go back to null immediately -- a
|
||||
syscall handler using this as its retry condition must never park a
|
||||
thread on a channel nobody can ever wake again. */
|
||||
keyboardCloseReader(a);
|
||||
KT_NULL(t, keyboardReaderChannel(a));
|
||||
|
||||
keyboardCloseReader(b);
|
||||
}
|
||||
|
||||
KTEST(keyboardOpenReaderExhaustionThenCloseFreesSlotForReuse)
|
||||
{
|
||||
/* Does not assume the table starts fully empty -- by the time ktests
|
||||
run, schedInit() has already given the idle thread (id 0) its own fd
|
||||
0 reader (vfs.c's vfsFdInit), same convention vfsFifoTest.c's own
|
||||
pipe-table exhaustion test uses for the shared pipes[] table. */
|
||||
int handles[maxConsoleReaders];
|
||||
int madeCount = 0;
|
||||
while (madeCount < maxConsoleReaders) {
|
||||
int h = keyboardOpenReader();
|
||||
if (h == consoleReaderInvalid)
|
||||
break;
|
||||
handles[madeCount++] = h;
|
||||
}
|
||||
KT_GT(t, madeCount, 0);
|
||||
|
||||
/* Table is now genuinely full: one more must fail cleanly. */
|
||||
KT_EQ(t, keyboardOpenReader(), consoleReaderInvalid);
|
||||
|
||||
keyboardCloseReader(handles[0]);
|
||||
int reused = keyboardOpenReader();
|
||||
KT_NE(t, reused, consoleReaderInvalid);
|
||||
KT_EQ(t, reused, handles[0]);
|
||||
|
||||
/* Leave the shared reader table exactly as free as it was found, so
|
||||
later boot-time console users (the shell, and the
|
||||
consolereader1/consolereader2 live demo) are never starved by this
|
||||
test having run. */
|
||||
keyboardCloseReader(reused);
|
||||
for (int i = 1; i < madeCount; i++)
|
||||
keyboardCloseReader(handles[i]);
|
||||
}
|
||||
Reference in new issue
Block a user