From nobody Mon Oct 21 01:32:43 2024 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4XWyVN05pMz5b22g; Mon, 21 Oct 2024 01:32:44 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R11" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4XWyVM5t2Hz58mX; Mon, 21 Oct 2024 01:32:43 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1729474363; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=rAzYI0c98aEDIBAod/3Tnd630R+7wnu0+TwRUzjhOcc=; b=HkESGCJyAg9xbsaT38blncSYdSS1T3hxe+Xlr5EZQi8OrEBTo8n4pDDgAvxRazL4n8g5jx 9srOwoU4mY1kwYdffeEzeZbJpMmsEcz1efVJNIl19ctfrtYYgzi2u4yc7o9vcrrEcGmXYg INFYzo150sOOqnEQk1TXO2NG86J0kPPMufAuKjyrArCQK8PDlpAAkNzsUe5YehzbqAqZA0 eZd55xH6dcygDAYpP8qK+7KfN9z3e+Y3l/vmbl+wda2pgSQaFi7aUWpzYM9a8A8Fztk6J0 nMjyjAMgi3SBo/r3ef9ll97lzSW2CNVSWSRF5/JDM7axRl7LwLKlt0APDO4NWg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1729474363; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=rAzYI0c98aEDIBAod/3Tnd630R+7wnu0+TwRUzjhOcc=; b=l9ere6cru9ZD/0w+S5TmcTWkz7l6KWpri2LXPfMjl4GCYg3U8qbEA6/Lln2tcd3tYJDL1b 4mQOT00yV+xyxNUH3dZcH+MTj7V9wckZ3ST9eaNDEUT2AFpiG1AYghsdGJEUPYHYGAOr38 fJ5o6etR53bKNHpyKONN+JOLVP4fvsqeEs7epTYm7YrWwKcuhEcuUFQ5FfbMUL6gjtRndQ pKdD2MgF/59gFAMJ8biInmmRFKq25/IIDMUR5872HhqRagj/xpBA7IIja+g/B8ktn6kNlO 8cGkcoz26zUnk+PbNhTOM0G3YPzFabi+LEDZDGDCFLgMhSkisQUsV3FQa/tqSA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1729474363; a=rsa-sha256; cv=none; b=LwwMFszw4/iC1ZHkfUUCInblpUTXTmx/g0Xh3rgkhW2gwAJl0lWJ9K5I5zHc7dnHOO/Htr xrcbO5zHLB3/wr+bx4PH4Z34RAKlqjk3XInDlfKdlCC7CYm9gRmz/19zYL5wAUxQNnp4Es EP1w8bR/PJIetO16vyhS6BhZOJcZYO8Qe+DNVChLT+ruFhl6G42jNRr5mGSaOwFMGswqXP uHpa0kVYcsKk1SZQVZjfes6/p7+JfINBIK6QCNfWRrfGHIEBicvBEHc7LsmFrN59uWdb5l oQtR1iTBCE6NS55gF2SU1LE5G5wKmgT7yrmyKOIkpP5VJOfbs6UpTgGDXZYzHw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4XWyVM5Tckz1PkD; Mon, 21 Oct 2024 01:32:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 49L1Wh2Z036916; Mon, 21 Oct 2024 01:32:43 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 49L1WhXk036913; Mon, 21 Oct 2024 01:32:43 GMT (envelope-from git) Date: Mon, 21 Oct 2024 01:32:43 GMT Message-Id: <202410210132.49L1WhXk036913@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kyle Evans Subject: git: 096c39fae4ad - main - tests: kern: add some porch(1)-based tty tests List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kevans X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 096c39fae4ad5135a317925d8749b7d83f65ebf8 Auto-Submitted: auto-generated The branch main has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=096c39fae4ad5135a317925d8749b7d83f65ebf8 commit 096c39fae4ad5135a317925d8749b7d83f65ebf8 Author: Kyle Evans AuthorDate: 2024-10-21 01:31:59 +0000 Commit: Kyle Evans CommitDate: 2024-10-21 01:32:31 +0000 tests: kern: add some porch(1)-based tty tests If sysutils/porch is installed, we'll do some basic testing of tty behavior. The existing tests primarily cover ICANON-related processing and corner cases that have been fixed somewhat recently, but I anticipate growing this out a bit in due time. Reviewed by: ngie Differential Revision: https://reviews.freebsd.org/D46806 --- etc/mtree/BSD.tests.dist | 2 + tests/sys/kern/Makefile | 1 + tests/sys/kern/tty/Makefile | 12 +++ tests/sys/kern/tty/fionread.c | 21 +++++ tests/sys/kern/tty/readsz.c | 130 +++++++++++++++++++++++++++++ tests/sys/kern/tty/test_canon.orch | 102 ++++++++++++++++++++++ tests/sys/kern/tty/test_canon_fullbuf.orch | 23 +++++ tests/sys/kern/tty/test_ncanon.orch | 39 +++++++++ tests/sys/kern/tty/test_recanon.orch | 90 ++++++++++++++++++++ 9 files changed, 420 insertions(+) diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist index 8cac5e8d55e2..221e4b32a89b 100644 --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -854,6 +854,8 @@ .. pipe .. + tty + .. .. kqueue libkqueue diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile index e334ff64da41..933c1c9aa10e 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -127,6 +127,7 @@ WARNS?= 3 TESTS_SUBDIRS+= acct TESTS_SUBDIRS+= execve TESTS_SUBDIRS+= pipe +TESTS_SUBDIRS+= tty .include diff --git a/tests/sys/kern/tty/Makefile b/tests/sys/kern/tty/Makefile new file mode 100644 index 000000000000..c362793a8b64 --- /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 diff --git a/tests/sys/kern/tty/fionread.c b/tests/sys/kern/tty/fionread.c new file mode 100644 index 000000000000..929d613f883b --- /dev/null +++ b/tests/sys/kern/tty/fionread.c @@ -0,0 +1,21 @@ +/*- + * Copyright (c) 2024 Kyle Evans + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include +#include +#include + +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 index 000000000000..95dafa02472f --- /dev/null +++ b/tests/sys/kern/tty/readsz.c @@ -0,0 +1,130 @@ +/*- + * Copyright (c) 2024 Kyle Evans + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 000000000000..28018edfdcd6 --- /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 +-- +-- 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 index 000000000000..1833703e4f45 --- /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 +-- +-- 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 index 000000000000..14a34d82fa9a --- /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 +-- +-- 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 index 000000000000..e3943495ca5d --- /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 +-- +-- 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$")