Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102720399
D46806.id143781.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D46806.id143781.diff
View Options
diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -852,6 +852,8 @@
..
pipe
..
+ tty
+ ..
..
kqueue
libkqueue
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -127,6 +127,7 @@
TESTS_SUBDIRS+= acct
TESTS_SUBDIRS+= execve
TESTS_SUBDIRS+= pipe
+TESTS_SUBDIRS+= tty
.include <netbsd-tests.test.mk>
diff --git a/tests/sys/kern/tty/Makefile b/tests/sys/kern/tty/Makefile
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/Makefile
@@ -0,0 +1,12 @@
+TESTSDIR= ${TESTSBASE}/sys/kern/tty
+BINDIR= ${TESTSDIR}
+
+PLAIN_TESTS_PORCH+= test_canon
+PLAIN_TESTS_PORCH+= test_canon_fullbuf
+PLAIN_TESTS_PORCH+= test_ncanon
+PLAIN_TESTS_PORCH+= test_recanon
+
+PROGS+= fionread
+PROGS+= readsz
+
+.include <bsd.test.mk>
diff --git a/tests/sys/kern/tty/fionread.c b/tests/sys/kern/tty/fionread.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/fionread.c
@@ -0,0 +1,21 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/ioctl.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ int nb;
+
+ assert(ioctl(STDIN_FILENO, FIONREAD, &nb) == 0);
+ printf("%d", nb);
+ return (0);
+}
diff --git a/tests/sys/kern/tty/readsz.c b/tests/sys/kern/tty/readsz.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/readsz.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s [-b bytes | -c lines | -e] [-s buffer-size]\n",
+ getprogname());
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *buf;
+ const char *errstr;
+ size_t bufsz = 0, reps;
+ ssize_t ret;
+ enum { MODE_BYTES, MODE_COUNT, MODE_EOF } mode;
+ int ch;
+
+ /*
+ * -b specifies number of bytes.
+ * -c specifies number of read() calls.
+ * -e specifies eof (default)
+ * -s to pass a buffer size
+ *
+ * Reading N lines is the same as -c with a high buffer size.
+ */
+ mode = MODE_EOF;
+ while ((ch = getopt(argc, argv, "b:c:es:")) != -1) {
+ switch (ch) {
+ case 'b':
+ mode = MODE_BYTES;
+ reps = strtonum(optarg, 0, SSIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "strtonum: %s", errstr);
+ break;
+ case 'c':
+ mode = MODE_COUNT;
+ reps = strtonum(optarg, 1, SSIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "strtonum: %s", errstr);
+ break;
+ case 'e':
+ mode = MODE_EOF;
+ break;
+ case 's':
+ bufsz = strtonum(optarg, 1, SSIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "strtonum: %s", errstr);
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (bufsz == 0) {
+ if (mode == MODE_BYTES)
+ bufsz = reps;
+ else
+ bufsz = LINE_MAX;
+ }
+
+ buf = malloc(bufsz);
+ if (buf == NULL)
+ err(1, "malloc");
+
+ for (;;) {
+ size_t readsz;
+
+ /*
+ * Be careful not to over-read if we're in byte-mode. In every other
+ * mode, we'll read as much as we can.
+ */
+ if (mode == MODE_BYTES)
+ readsz = MIN(bufsz, reps);
+ else
+ readsz = bufsz;
+
+ ret = read(STDIN_FILENO, buf, readsz);
+ if (ret == -1 && errno == EINTR)
+ continue;
+ if (ret == -1)
+ err(1, "read");
+ if (ret == 0) {
+ if (mode == MODE_EOF)
+ return (0);
+ errx(1, "premature EOF");
+ }
+
+ /* Write out what we've got */
+ write(STDOUT_FILENO, buf, ret);
+
+ /*
+ * Bail out if we've hit our metric (byte mode / count mode).
+ */
+ switch (mode) {
+ case MODE_BYTES:
+ reps -= ret;
+ if (reps == 0)
+ return (0);
+ break;
+ case MODE_COUNT:
+ reps--;
+ if (reps == 0)
+ return (0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (0);
+}
diff --git a/tests/sys/kern/tty/test_canon.orch b/tests/sys/kern/tty/test_canon.orch
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/test_canon.orch
@@ -0,0 +1,102 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+spawn("cat")
+
+write "Complete\r"
+match "Complete\r"
+
+write "Basic\rIncomplete"
+match "Basic\r"
+
+-- We shouldn't see any of the "Incomplete" line
+fail(function()
+end)
+
+match "Incomp" {
+ callback = function()
+ exit(1)
+ end
+}
+
+fail(nil)
+
+-- Pushing a ^D along should force a flush of the tty, cat(1) will write the
+-- result without a trailing newline.
+write " line^D"
+match "Incomplete line$"
+
+-- Erase!
+write "Dog^H^D"
+match "Do$"
+
+-- More erase!
+write "Cat Dog^W^D"
+match "Cat $"
+
+write "^D"
+eof()
+
+local function fionread_test(str, expected)
+ spawn("fionread")
+
+ write(str)
+ match(expected)
+end
+
+-- Incomplete line
+fionread_test("Hello", "0")
+-- VEOF does not count
+fionread_test("Hello^D", "5")
+-- VEOF still doesn't count, even if the next line is an extra VEOF later
+fionread_test("Hello^D^D", "5")
+-- read(2) definitely won't return the second incomplete line
+fionread_test("Hello^Dther", "5")
+-- read(2) also won't return a second complete line at once
+fionread_test("Hello^Dthere^D", "5")
+-- Finally, send a VEOF to terminate a blank line and signal EOF in read(2)
+fionread_test("^D", "0")
+
+-- \r will instead show up in the input stream to the application, so we must
+-- make sure those are counted where VEOF generally wouldn't be.
+fionread_test("Hello\r", "6")
+fionread_test("Hello\rther", "6")
+fionread_test("Hello\rthere\r", "6")
+fionread_test("\r", "1")
+
+local function readsz_test(str, arg, expected)
+ spawn("readsz", table.unpack(arg))
+
+ if type(str) == "table" then
+ assert(#str == 2)
+ write(str[1])
+ release()
+
+ -- Give readsz a chance to consume the partial input before we send more
+ -- along.
+ sleep(1)
+ write(str[2])
+ else
+ write(str)
+ end
+ match(expected)
+end
+
+readsz_test("partial", {"-b", 3}, "^$")
+readsz_test("partial^D", {"-b", 3}, "^par$")
+readsz_test("partial^D", {"-c", 1}, "^partial$")
+for s = 1, #"partial" do
+ readsz_test("partial^D", {"-s", s}, "^partial$")
+end
+-- Send part of the line, release and pause, then finish it.
+readsz_test({"par", "tial^D"}, {"-c", 1}, "^partial$")
+-- line is incomplete, so we'll just see the "partial" even if we want two
+readsz_test("partial^Dline", {"-c", 2}, "^partial$")
+readsz_test("partial^Dline^D", {"-c", 1}, "^partial$")
+readsz_test("partial^Dline^D", {"-c", 2}, "^partialline$")
diff --git a/tests/sys/kern/tty/test_canon_fullbuf.orch b/tests/sys/kern/tty/test_canon_fullbuf.orch
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/test_canon_fullbuf.orch
@@ -0,0 +1,23 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+local TTYINQ_DATASIZE = 128
+local scream = string.rep("A", TTYINQ_DATASIZE - 1)
+
+spawn("cat")
+
+-- Fill up a whole block with screaming + VEOF
+write(scream .. "^D")
+match(scream .. "$")
+
+scream = scream .. "A"
+
+-- Now fill up the next block, but spill the VEOF over to a third block.
+write(scream .. "^D")
+match(scream .. "$")
diff --git a/tests/sys/kern/tty/test_ncanon.orch b/tests/sys/kern/tty/test_ncanon.orch
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/test_ncanon.orch
@@ -0,0 +1,39 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+local function spawn_one(...)
+ spawn(...)
+
+ stty("lflag", 0, tty.lflag.ICANON)
+end
+
+-- We can send one byte...
+spawn_one("readsz", "-c", 1)
+write "H"
+match "^H$"
+
+-- or many.
+spawn_one("readsz", "-c", 1)
+write "Hello"
+match "^Hello$"
+
+-- VEOF is a normal character here, passed through as-is.
+spawn_one("readsz", "-c", 1)
+write "Hello^D"
+match "^Hello\x04$"
+spawn_one("readsz", "-c", 1)
+write "^D"
+match "^\x04$"
+
+-- Confirm that FIONREAD agrees that VEOF will be returned, even if it was sent
+-- while the tty was still in canonical mode.
+spawn("fionread")
+write "^D"
+stty("lflag", 0, tty.lflag.ICANON)
+match "^1$"
diff --git a/tests/sys/kern/tty/test_recanon.orch b/tests/sys/kern/tty/test_recanon.orch
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/test_recanon.orch
@@ -0,0 +1,90 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+local TTYINQ_DATASIZE = 128
+local scream = string.rep("A", TTYINQ_DATASIZE - 1)
+
+local function ncanon()
+ stty("lflag", nil, tty.lflag.ICANON)
+end
+
+local function canon()
+ stty("lflag", tty.lflag.ICANON)
+end
+
+spawn("readsz", "-e")
+ncanon()
+
+-- Fill up a whole block with screaming + VEOF; when it gets recanonicalized,
+-- the next line should be pointing to the beginning of the next block.
+write(scream .. "^D")
+
+canon()
+match(scream .. "$")
+
+-- The same as above, but spilling VEOF over to the next block.
+spawn("readsz", "-e")
+ncanon()
+
+write(scream .. "A^D")
+
+canon()
+match(scream .. "A$")
+
+-- We'll do it again, except with one character spilled over to the next block
+-- before we recanonicalize. We should then have the scream, followed by a
+-- partial line containing the spill over.
+spawn("cat")
+ncanon()
+
+write(scream .. "^DZ")
+
+canon()
+match(scream .. "$")
+
+-- Sending "B^D" should give us "ZB" to make sure that we didn't lose anything
+-- at the beginning of the next block.
+
+write("B^D")
+match("^ZB$")
+
+-- Next we'll VEOF at the beginning.
+spawn("readsz", "-e")
+ncanon()
+
+write("^D")
+match("^$")
+
+-- Finally, we'll trigger recanonicalization with an empty buffer. This one is
+-- just about avoiding a panic.
+spawn("true")
+
+ncanon()
+canon()
+release()
+eof()
+
+spawn("readsz", "-c", "1")
+
+write("Test^Dfoo")
+ncanon()
+
+match("^Test\x04foo$")
+
+-- Finally, swap VEOF out with ^F; before recent changes, we would remain
+-- canonicalized at Test^D and the kernel would block on it unless a short
+-- buffer was used since VEOF would not appear within the canonicalized bit.
+spawn("readsz", "-c", 1)
+
+write("Test^DLine^F")
+stty("cc", {
+ VEOF = "^F"
+})
+
+match("^Test\x04Line$")
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Nov 17, 8:16 AM (20 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14673108
Default Alt Text
D46806.id143781.diff (10 KB)
Attached To
Mode
D46806: tests: kern: add some porch(1)-based tty tests
Attached
Detach File
Event Timeline
Log In to Comment