Re: [PATCH v5.2 12/12] linux/termios: regression test for termios speed functions
H. Peter Anvin
hpa@zytor.com
Fri May 30 20:26:13 GMT 2025
On May 30, 2025 6:32:22 AM PDT, Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> wrote:
>
>
>On 27/05/25 19:08, H. Peter Anvin wrote:
>> Test that runs through a fairly large combination of the various
>> termios speed functions, for the new speed_t interface, for the old
>> speed_t interface (if enabled), and for the new baud_t interface.
>>
>> [ v5.1: clean up the definition of the VERIFY and CHECK macros ]
>> [ v5.2: make sure Makefile is sorted to prevent lint error ]
>>
>> Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
>
>I am seeing some issues on alpha, both on qemu-user and with a VM with
>6.12.13-alpha-generic:
>
>error: ../sysdeps/unix/sysv/linux/tst-termios-linux.c:new_cfspeed_test:249: c_ispeed = 0: CIBAUD = __B1800 (000012), should be __B0 (000000)
>error: ../sysdeps/unix/sysv/linux/tst-termios-linux.c:new_cfspeed_test:251: c_ispeed = 0: CIBAUD = __B1800 (000012), should be __B0 (000000)
>error: ../sysdeps/unix/sysv/linux/tst-termios-linux.c:new_cfspeed_test:253: c_ispeed = 0: CIBAUD = __B1800 (000012), should be __B0 (000000)
>error: ../sysdeps/unix/sysv/linux/tst-termios-linux.c:new_cfspeed_test:255: c_ispeed = 0: CIBAUD = __B1800 (000012), should be __B0 (000000)
>error: ../sysdeps/unix/sysv/linux/tst-termios-linux.c:new_cfspeed_test:249: c_ispeed = 50: CIBAUD = __B2400 (000013), should be __B50 (000001)
>error: ../sysdeps/unix/sysv/linux/tst-termios-linux.c:new_cfspeed_test:251: c_ispeed = 50: CIBAUD = __B2400 (000013), should be __B50 (000001)
>[...]
>
>I think something might be really broken on alpha. I did not see any
>other issue in other different architectures (x86, aarch64, arm, riscv,
>s390x, powerpc, sparc, mips).
>
>> ---
>> sysdeps/unix/sysv/linux/Makefile | 1 +
>> sysdeps/unix/sysv/linux/tst-termios-linux.c | 610 ++++++++++++++++++
>> .../sysv/linux/tst-termios-speed-tables.c | 86 +++
>> 3 files changed, 697 insertions(+)
>> create mode 100644 sysdeps/unix/sysv/linux/tst-termios-linux.c
>> create mode 100644 sysdeps/unix/sysv/linux/tst-termios-speed-tables.c
>>
>> diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
>> index a14b8c1f6181..913fe736bc18 100644
>> --- a/sysdeps/unix/sysv/linux/Makefile
>> +++ b/sysdeps/unix/sysv/linux/Makefile
>> @@ -236,6 +236,7 @@ tests += \
>> tst-sysvmsg-linux \
>> tst-sysvsem-linux \
>> tst-sysvshm-linux \
>> + tst-termios-linux \
>> tst-tgkill \
>> tst-timerfd \
>> tst-ttyname-direct \
>> diff --git a/sysdeps/unix/sysv/linux/tst-termios-linux.c b/sysdeps/unix/sysv/linux/tst-termios-linux.c
>> new file mode 100644
>> index 000000000000..9b9b3f984ca9
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/tst-termios-linux.c
>> @@ -0,0 +1,610 @@
>> +/* Linux termios regression tests
>> +
>> + Copyright (C) 2025 Free Software Foundation, Inc.
>> + This file is part of the GNU C Library.
>> +
>> + The GNU C Library is free software; you can redistribute it and/or
>> + modify it under the terms of the GNU Lesser General Public License as
>> + published by the Free Software Foundation; either version 2.1 of the
>> + License, or (at your option) any later version.
>> +
>> + The GNU C Library is distributed in the hope that it will be useful,
>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> + Lesser General Public License for more details.
>> +
>> + You should have received a copy of the GNU Lesser General Public
>> + License along with the GNU C Library; see the file COPYING.LIB. If
>> + not, see <https://www.gnu.org/licenses/>. */
>> +
>> +#include <errno.h>
>> +#include <fcntl.h>
>> +#include <limits.h>
>> +#include <stdbool.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +#include <sys/ioctl.h>
>> +#include <termios.h>
>> +#include <unistd.h>
>> +
>> +#include <shlib-compat.h>
>> +
>> +#include <support/check.h>
>> +#include <support/namespace.h>
>> +#include <support/support.h>
>> +#include <support/temp_file.h>
>> +#include <support/test-driver.h>
>> +#include <support/xunistd.h>
>> +
>> +/* generic utilities */
>> +static unsigned int errors;
>> +
>> +struct where {
>> + const char *file;
>> + const char *func;
>> + int line;
>> +};
>> +typedef const struct where *where_t;
>> +
>> +#define HERE \
>> + ({ \
>> + static const struct where here = { __FILE__, __func__, __LINE__ }; \
>> + &here; \
>> + })
>> +#define ERROR(where, msg, ...) \
>> + do { \
>> + const where_t _w = where; \
>> + errors++; \
>> + printf ("error: %s:%s:%d: " msg "\n", \
>> + _w->file, _w->func, _w->line , ## __VA_ARGS__); \
>> + } while (0)
>> +
>> +static void verify_failed (where_t where, const char *expr,
>> + const char *test, bool bad)
>> +{
>> + ERROR(where, "%s%s%s %s%s%s",
>> + bad ? "assertion " : "",
>> + expr,
>> + bad ? test : "",
>> + bad ? (errno ? "failed and " : "failed") : "",
>> + errno ? "set errno: " : "",
>> + errno ? strerror(errno) : "");
>> +}
>> +
>> +static inline void verify_check (where_t where, const char *expr,
>> + const char *test, bool good)
>> +{
>> + bool bad = !good;
>> + if (__builtin_expect (bad || errno, 0))
>> + verify_failed (where, expr, test, bad);
>> +}
>> +
>> +/* Evaluate an expression and verify a specific predicate as well
>> + as errno not being set by the expression. */
>> +#define VERIFY(expr,op,res) \
>> + ({ \
>> + const __auto_type _r = (res); \
>> + errno = 0; \
>> + __auto_type _v = (expr); \
>> + verify_check (HERE, #expr, " " #op " " #res, _v op _r); \
>> + _v; \
>> + })
>
>We have support/check.h with macros to check the result, compare blobs,
>and register errors instead of adding code on each test. It also calls
>support_record_failure/support_test_compare_failure, which maintains
>an error coutner in a shared mapped and handle multithread environments.
>
>> +
>> +/* Check for zero (useful for return values) or just for errno being set */
>> +#define CHECKZERO(expr) VERIFY (expr,==,0)
>> +#define CHECKERR(expr) VERIFY (expr,||,1)
>> +
>> +/* Speed function tests */
>> +
>> +#define BOGUS ((speed_t)-1)
>> +#define ANY ((speed_t)-2)
>> +
>> +#include "tst-termios-speed-tables.c"
>> +
>> +/* These intentionally are a separate implementation from speed.c;
>> + these should be "trivially correct" and don't need to be optimized
>> + in any way */
>> +
>> +/* Returns __BOTHER if there is no legacy value for this speed */
>> +static speed_t speed_to_cbaud (speed_t speed)
>> +{
>> + const struct cbaud_table *ct;
>> + for (ct = cbaud_table; ct->speed != ANY; ct++)
>> + {
>> + if (ct->speed == speed)
>> + break;
>> + }
>> + return ct->cbaud;
>> +}
>> +
>> +/* Returns ANY if cbaud is __BOTHER, or BOGUS if invalid */
>> +static speed_t cbaud_to_speed (speed_t cbaud)
>> +{
>> + const struct cbaud_table *ct;
>> + for (ct = cbaud_table; ct->cbaud != BOGUS; ct++)
>> + {
>> + if (ct->cbaud == cbaud)
>> + break;
>> + }
>> + return ct->speed;
>> +}
>> +
>> +static const char *cbaud_name (speed_t cbaud)
>> +{
>> + const struct cbaud_table *ct;
>> + for (ct = cbaud_table; ct->cbaud != BOGUS; ct++)
>> + {
>> + if (ct->cbaud == cbaud)
>> + break;
>> + }
>> + return ct->name;
>> +}
>> +
>> +static void check_speed (where_t where, speed_t expected,
>> + speed_t speed, speed_t cbaud,
>> + speed_t cfspeed, baud_t cfbaud,
>> + char io)
>> +{
>> + speed_t want_cbaud;
>> + cbaud &= CBAUD;
>> +
>> + if (expected != ANY && speed != expected)
>> + {
>> + ERROR (where, "c_%cspeed = %u, expected %u",
>> + io, speed, expected);
>> + return;
>> + }
>
>Using the support/check.h macros, it could be replaced by:
>
> if (expected != ANY)
> TEST_COMPARE (speed, expected);
>
>Same for the rest of checks.
>
>> + if (cfspeed != speed)
>> + {
>> + ERROR (where, "cfget%cspeed = %u, expected %u",
>> + io, cfspeed, speed);
>> + return;
>> + }
>> + if (cfbaud != cfspeed)
>> + {
>> + ERROR (where, "cfget%cbaud = %u, but cfget%cspeed = %u",
>> + io, cfbaud, io, cfspeed);
>> + return;
>> + }
>> + want_cbaud = speed_to_cbaud (speed);
>> + if (cbaud != want_cbaud) {
>> + ERROR (where, "c_%cspeed = %u: %s = %s (%06o), should be %s (%06o)",
>> + io, speed,
>> + io == 'o' ? "CBAUD" : "CIBAUD", cbaud_name (cbaud), cbaud,
>> + cbaud_name (want_cbaud), want_cbaud);
>> + return;
>> + }
>> +}
>> +
>> +/* Validate that the speeds in the struct termios are properly normalized.
>> + The difference is the handling of ispeed == 0. */
>> +
>> +/* Use this after cfset* () */
>> +static void check_speeds_cf (where_t where, const struct termios *tio_p,
>> + speed_t ospeed, speed_t ispeed)
>> +{
>> + check_speed (where, ospeed,
>> + tio_p->c_ospeed, tio_p->c_cflag,
>> + CHECKERR (cfgetospeed (tio_p)),
>> + CHECKERR (cfgetobaud (tio_p)), 'o');
>> + check_speed (where, ispeed,
>> + tio_p->c_ispeed, tio_p->c_cflag >> IBSHIFT,
>> + CHECKERR (cfgetispeed (tio_p)),
>> + CHECKERR (cfgetibaud (tio_p)), 'i');
>> +}
>> +
>> +/* Use this after tc[gs]etattr () */
>> +static void check_speeds_tc (where_t where, int fd,
>> + speed_t ospeed, speed_t ispeed)
>> +{
>> + struct termios tio;
>> +
>> + CHECKZERO (tcgetattr (fd, &tio));
>> + check_speeds_cf (where, &tio, ospeed, ispeed ? ispeed : ospeed);
>> +}
>> +
>> +/* For search and replace convenience */
>> +#define check_bauds_cf check_speeds_cf
>> +#define check_bauds_tc check_speeds_tc
>> +
>> +/* Common routine for setting speeds, with checking */
>> +static void
>> +set_speeds (int fd, speed_t ospeed, speed_t ispeed)
>> +{
>> + struct termios tio;
>> +
>> + CHECKZERO (tcgetattr (fd, &tio));
>> + CHECKZERO (cfsetospeed (&tio, ospeed));
>> + CHECKZERO (cfsetispeed (&tio, ispeed));
>> + check_speeds_cf (HERE, &tio, ospeed, ispeed);
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_speeds_tc (HERE, fd, ospeed, ispeed ? ispeed : ospeed);
>> +}
>> +
>> +/* Actual tests */
>> +
>> +typedef void (*speed_test_t)(int ttyfd, speed_t speed);
>> +static void
>> +run_speed_test (int fd, speed_test_t test);
>> +
>> +/* New interface cfset*speed test */
>> +static void
>> +new_cfspeed_test (int fd, speed_t speed)
>> +{
>> + struct termios tio;
>> + speed_t old_ospeed, old_ispeed;
>> +
>> + CHECKZERO (tcgetattr (fd, &tio));
>> + old_ospeed = CHECKERR (cfgetospeed (&tio));
>> + old_ispeed = CHECKERR (cfgetispeed (&tio));
>> +
>> + /* Check initial normalization */
>> + check_speeds_cf (HERE, &tio, old_ospeed, old_ispeed);
>> +
>> + /* Check cfset*speed normalization */
>> + CHECKZERO (cfsetospeed (&tio, speed));
>> + check_speeds_cf (HERE, &tio, speed, old_ispeed);
>> + CHECKZERO (cfsetispeed (&tio, speed));
>> + check_speeds_cf (HERE, &tio, speed, speed);
>> + CHECKZERO (cfsetospeed (&tio, old_ospeed));
>> + check_speeds_cf (HERE, &tio, old_ospeed, speed);
>> + CHECKZERO (cfsetispeed (&tio, B0));
>> + check_speeds_cf (HERE, &tio, old_ospeed, B0);
>> + CHECKZERO (cfsetspeed (&tio, speed));
>> + check_speeds_cf (HERE, &tio, speed, speed);
>> + CHECKZERO (cfsetospeed (&tio, old_ospeed));
>> + CHECKZERO (cfsetispeed (&tio, old_ispeed));
>> + check_speeds_cf (HERE, &tio, old_ospeed, old_ispeed);
>> +}
>> +
>> +/* New interface cfset*speed test with tcsetattr */
>> +static void
>> +new_tcspeed_test (int fd, speed_t speed)
>> +{
>> + struct termios tio;
>> + speed_t old_ospeed, old_ispeed;
>> +
>> + CHECKZERO (tcgetattr (fd, &tio));
>> + old_ospeed = CHECKERR (cfgetospeed (&tio));
>> + old_ispeed = CHECKERR (cfgetispeed (&tio));
>> +
>> + /* Check initial normalization */
>> + check_speeds_cf (HERE, &tio, old_ospeed, old_ispeed);
>> +
>> + /* Check cfset*speed normalization */
>> + CHECKZERO (cfsetospeed (&tio, speed));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_speeds_tc (HERE, fd, speed, old_ispeed);
>> + CHECKZERO (cfsetispeed (&tio, speed));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_speeds_tc (HERE, fd, speed, speed);
>> + CHECKZERO (cfsetospeed (&tio, old_ospeed));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_speeds_tc (HERE, fd, old_ospeed, speed);
>> + CHECKZERO (cfsetispeed (&tio, B0));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_speeds_tc (HERE, fd, old_ospeed, B0);
>> + CHECKZERO (cfsetspeed (&tio, speed));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_speeds_tc (HERE, fd, speed, speed);
>> + CHECKZERO (cfsetospeed (&tio, old_ospeed));
>> + CHECKZERO (cfsetispeed (&tio, old_ispeed));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_speeds_tc (HERE, fd, old_ospeed, old_ispeed);
>> +}
>> +
>> +/* New interface cfset*baud test */
>> +static void
>> +new_cfbaud_test (int fd, baud_t baud)
>> +{
>> + struct termios tio;
>> + baud_t old_obaud, old_ibaud;
>> +
>> + CHECKZERO (tcgetattr (fd, &tio));
>> + old_obaud = CHECKERR (cfgetobaud (&tio));
>> + old_ibaud = CHECKERR (cfgetibaud (&tio));
>> +
>> + /* Check initial normalization */
>> + check_bauds_cf (HERE, &tio, old_obaud, old_ibaud);
>> +
>> + /* Check cfset*baud normalization */
>> + CHECKZERO (cfsetobaud (&tio, baud));
>> + check_bauds_cf (HERE, &tio, baud, old_ibaud);
>> + CHECKZERO (cfsetibaud (&tio, baud));
>> + check_bauds_cf (HERE, &tio, baud, baud);
>> + CHECKZERO (cfsetobaud (&tio, old_obaud));
>> + check_bauds_cf (HERE, &tio, old_obaud, baud);
>> + CHECKZERO (cfsetibaud (&tio, B0));
>> + check_bauds_cf (HERE, &tio, old_obaud, B0);
>> + CHECKZERO (cfsetbaud (&tio, baud));
>> + check_bauds_cf (HERE, &tio, baud, baud);
>> + CHECKZERO (cfsetobaud (&tio, old_obaud));
>> + CHECKZERO (cfsetibaud (&tio, old_ibaud));
>> + check_bauds_cf (HERE, &tio, old_obaud, old_ibaud);
>> +}
>> +
>> +/* New interface cfset*baud test with tcsetattr */
>> +static void
>> +new_tcbaud_test (int fd, baud_t baud)
>> +{
>> + struct termios tio;
>> + baud_t old_obaud, old_ibaud;
>> +
>> + CHECKZERO (tcgetattr (fd, &tio));
>> + old_obaud = CHECKERR (cfgetobaud (&tio));
>> + old_ibaud = CHECKERR (cfgetibaud (&tio));
>> +
>> + /* Check initial normalization */
>> + check_bauds_cf (HERE, &tio, old_obaud, old_ibaud);
>> +
>> + /* Check cfset*baud normalization */
>> + CHECKZERO (cfsetobaud (&tio, baud));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_bauds_tc (HERE, fd, baud, old_ibaud);
>> + CHECKZERO (cfsetibaud (&tio, baud));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_bauds_tc (HERE, fd, baud, baud);
>> + CHECKZERO (cfsetobaud (&tio, old_obaud));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_bauds_tc (HERE, fd, old_obaud, baud);
>> + CHECKZERO (cfsetibaud (&tio, B0));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_bauds_tc (HERE, fd, old_obaud, B0);
>> + CHECKZERO (cfsetbaud (&tio, baud));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_bauds_tc (HERE, fd, baud, baud);
>> + CHECKZERO (cfsetobaud (&tio, old_obaud));
>> + CHECKZERO (cfsetibaud (&tio, old_ibaud));
>> + CHECKZERO (tcsetattr (fd, TCSANOW, &tio));
>> + check_bauds_tc (HERE, fd, old_obaud, old_ibaud);
>> +}
>> +
>> +/*
>> + * Old interface tests. This depends critically on the new struct
>> + * termios being guaranteed to be a superset of the legacy struct
>> + * termios.
>> + */
>> +#if TEST_COMPAT (libc, GLIBC_2_0, GLIBC_2_42)
>> +extern int __old_cfsetospeed (struct termios *tio_p, speed_t speed);
>> +compat_symbol_reference (libc, __old_cfsetospeed, cfsetospeed, GLIBC_2_0);
>> +extern int __old_cfsetispeed (struct termios *tio_p, speed_t speed);
>> +compat_symbol_reference (libc, __old_cfsetispeed, cfsetispeed, GLIBC_2_0);
>> +extern speed_t __old_cfgetospeed (const struct termios *tio_p);
>> +compat_symbol_reference (libc, __old_cfgetospeed, cfgetospeed, GLIBC_2_0);
>> +extern speed_t __old_cfgetispeed (const struct termios *tio_p);
>> +compat_symbol_reference (libc, __old_cfgetispeed, cfgetispeed, GLIBC_2_0);
>> +extern int __old_tcsetattr (int fd, int act, const struct termios *tio_p);
>> +compat_symbol_reference (libc, __old_tcsetattr, tcsetattr, GLIBC_2_0);
>> +extern int __old_tcgetattr (int fd, struct termios *tio_p);
>> +compat_symbol_reference (libc, __old_tcgetattr, tcgetattr, GLIBC_2_0);
>> +
>> +static int old_tcsetattr (int fd, const struct termios *tio_p)
>> +{
>> + struct termios old_tio = *tio_p;
>> +
>> + /* Deliberately corrupt c_ispeed and c_ospeed */
>> + old_tio.c_ispeed = 0xdeadbeef;
>> + old_tio.c_ospeed = 0xfeedface;
>> + return __old_tcsetattr (fd, TCSANOW, &old_tio);
>> +}
>> +static int old_tcgetattr (int fd, struct termios *tio_p)
>> +{
>> + int rv;
>> + memset (tio_p, 0xde, sizeof *tio_p);
>> + rv = __old_tcgetattr (fd, tio_p);
>> + if (rv)
>> + return rv;
>> +
>> + /* Deliberately corrupt c_ispeed and c_ospeed */
>> + tio_p->c_ispeed = 0xdeadbeef;
>> + tio_p->c_ospeed = 0xfeedface;
>> + return 0;
>> +}
>> +
>> +/* Old interface test. This relies on the new struct termios always
>> + being a binary superset of the old one.
>> + This doesn't bother testing split speed, since that never worked
>> + on the old glibc. */
>> +static void
>> +old_tcspeed_test (int fd, speed_t speed)
>> +{
>> + struct termios tio;
>> + speed_t cbaud;
>> +
>> + if (!speed)
>> + return; /* Skip B0 for this test */
>> +
>> + cbaud = speed_to_cbaud (speed);
>> + if (cbaud == __BOTHER)
>> + return;
>> +
>> + CHECKZERO (old_tcgetattr (fd, &tio));
>> + CHECKZERO (__old_cfsetospeed (&tio, cbaud));
>> + VERIFY (__old_cfgetospeed (&tio),==,cbaud);
>> + CHECKZERO (__old_cfsetispeed (&tio, cbaud));
>> + VERIFY (__old_cfgetispeed (&tio),==,cbaud);
>> + CHECKZERO (old_tcsetattr (fd, &tio));
>> + check_speeds_tc (HERE, fd, speed, speed);
>> +}
>> +
>> +/* Verify that invalid CBAUD values return error for the old interfaces */
>> +static void
>> +old_invalid_speeds_test (int fd)
>> +{
>> + struct termios tio;
>> + speed_t cbaud;
>> +
>> + for (cbaud = 0 ; cbaud ; cbaud > 0xffff ? (cbaud <<= 1) : cbaud++) {
>> + speed_t realspeed;
>> + realspeed = (cbaud & ~CBAUD) ? BOGUS : cbaud_to_speed (cbaud);
>> + if (realspeed >= ANY)
>> + {
>> + int rv;
>> +
>> + errno = 0;
>> + rv = __old_cfsetospeed (&tio, cbaud);
>> + if (rv != -1 || errno != EINVAL)
>> + {
>> + ERROR(HERE, "__old_cfsetospeed() accepted invalid value %06o",
>> + cbaud);
>> + }
>> +
>> + errno = 0;
>> + rv = __old_cfsetispeed (&tio, cbaud);
>> + if (rv != -1 || errno != EINVAL)
>> + {
>> + ERROR(HERE, "__old_cfsetispeed() accepted invalid value %06o",
>> + cbaud);
>> + }
>> + }
>> + else
>> + {
>> + CHECKZERO (__old_cfsetospeed (&tio, cbaud));
>> + VERIFY (__old_cfgetospeed (&tio),==,cbaud);
>> + CHECKZERO (__old_cfsetispeed (&tio, cbaud));
>> + VERIFY (__old_cfgetispeed (&tio),==,cbaud);
>> + if (cbaud)
>> + {
>> + CHECKZERO (old_tcsetattr (fd, &tio));
>> + check_speeds_tc (HERE, fd, realspeed, realspeed);
>> + }
>> + }
>> + }
>> +}
>> +
>> +static void
>> +compat_tests (int fd)
>> +{
>> + run_speed_test (fd, old_tcspeed_test);
>> + old_invalid_speeds_test (fd);
>> +}
>> +#else /* No TEST_COMPAT */
>> +#define compat_tests(fd) ((void)(fd))
>> +#endif
>> +
>> +static void
>> +run_speed_test (int fd, speed_test_t test)
>> +{
>> + unsigned short seed [3] = { 0x1234, 0x5678, 0x9abc };
>> + struct speeds {
>> + speed_t ospeed, ispeed;
>> + };
>> + static const struct speeds initial_speeds [] = {
>> + { 2400, 2400 }, /* Standard speed, non-split */
>> + { 123456, 123456 }, /* Nonstandard speed, non-split */
>> + { 75, 1200 }, /* Standard split speeds */
>> + { 9600, 456789 }, /* One standard, one nonstandard */
>> + { 54321, 1234567890 }, /* Nonstandard, one very high */
>> + { 0, 0 }
>> + };
>> +
>> + for (const struct speeds *is = initial_speeds ; is->ospeed ; is++)
>
>Maybe use array_length() instead of the ending sentinel.
>
>> + {
>> + /* Set up initial conditions */
>> + set_speeds (fd, is->ospeed, is->ispeed);
>> +
>> + /* Test all common speeds */
>> + for (int i = 0 ; i < NUM_TEST_SPEEDS ; i++)
>> + {
>> + test (fd, test_speeds[i]);
>> + }
>> +
>> + /* Test pseudorandom speeds; NUM_TEST_SPEEDS here is arbitrary */
>> + for (int i = 0 ; i < NUM_TEST_SPEEDS ; i++)
>> + {
>> + test (fd, (speed_t) jrand48 (seed));
>> + }
>> +
>> + /* Test power-of-2 speeds */
>> + for (speed_t s = 1 ; s ; s <<= 1)
>> + {
>> + test (fd, s);
>> + }
>> +
>> + /* Test power of 2 multiples of 300;
>> + 300 << 23 is the maximum below 2^32 */
>> + for (int i = 0 ; i < 24 ; i++)
>> + {
>> + test (fd, B300 << i);
>> + }
>> + }
>> +}
>> +
>> +static void
>> +run_speed_tests (int fd)
>> +{
>> + /* Test proper canonicalization using the new interface */
>> + run_speed_test (fd, new_cfspeed_test);
>> + run_speed_test (fd, new_tcspeed_test);
>> +
>> + /* Try the new cfset*baud() functions */
>> + run_speed_test (fd, new_cfbaud_test);
>> + run_speed_test (fd, new_tcbaud_test);
>> +
>> + /* Tests of the legacy functions */
>> + compat_tests (fd);
>> +}
>> +
>> +/* chroot setup */
>> +
>> +static char *chrootdir;
>> +
>> +static void
>> +prepare (int argc, char **argv)
>> +{
>> + chrootdir = xasprintf ("%s/tst-termios-XXXXXX", test_dir);
>> + if (mkdtemp (chrootdir) == NULL)
>> + FAIL_EXIT1 ("mkdtemp (\"%s\"): %m", chrootdir);
>> + add_temp_file (chrootdir);
>
>Maybe support_create_temp_directory instead?
>
>> +}
>> +#define PREPARE prepare
>> +
>> +/* test dispatch */
>> +
>> +static void
>> +run_in_chroot (void)
>> +{
>> + /* Create a pty slave to use as a tty. Most of the termios settings,
>> + including the speeds, have no impact on a pty, but they are still
>> + settable like for any other tty, which makes them very convenient
>> + for testing. */
>> + int ptmfd, ttyfd;
>> +
>> + ptmfd = posix_openpt (O_RDWR|O_NOCTTY|O_NONBLOCK);
>> + if (ptmfd < 0 || grantpt (ptmfd) || unlockpt (ptmfd))
>> + {
>> + FAIL_UNSUPPORTED ("cannot create PTY in chroot: %m"
>> + " (consider increasing limits?)");
>> + }
>> +
>> + ttyfd = ioctl (ptmfd, TIOCGPTPEER, O_RDWR|O_NOCTTY|O_NONBLOCK);
>> + if (ttyfd < 0 || !isatty (ttyfd))
>> + {
>> + FAIL_UNSUPPORTED ("failed to open PTY slave in chroot: %m");
>> + }
>> +
>
>We have support_openpty handles the tty creation, with the only difference
>being it does not use O_NONBLOCK. If this is required, by add the extra
>flags required as an additional argument.
>
>> + run_speed_tests (ttyfd);
>> +
>> + close (ttyfd);
>> + close (ptmfd);
>> +}
>> +
>> +static int
>> +do_test (void)
>> +{
>> + errors = 0;
>> +
>> + support_become_root ();
>> + run_in_chroot ();
>> +
>> + if (errors)
>> + {
>> + printf ("%s: %u error%s\n", __FILE__, errors, errors != 1 ? "s" : "");
>> + return 1;
>> + }
>
>With support/check.h macro there is no need to handle it.
>
>> +
>> + return 0;
>> +}
>> +
>> +#include <support/test-driver.c>
>> diff --git a/sysdeps/unix/sysv/linux/tst-termios-speed-tables.c b/sysdeps/unix/sysv/linux/tst-termios-speed-tables.c
>> new file mode 100644
>> index 000000000000..885266d09a39
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/tst-termios-speed-tables.c
>
>Maybe just fold it on the test itself? It is only used there, or do you intend
>to add further tests that might use these definitions?
>
>> @@ -0,0 +1,86 @@
>> +/* Table of legacy speed constants */
>> +
>> +struct cbaud_table
>> +{
>> + speed_t speed;
>> + speed_t cbaud;
>> + const char *name;
>> +};
>> +
>> +static const struct cbaud_table cbaud_table [] =
>> +{
>> + { 0, __B0, "__B0" },
>> + { 50, __B50, "__B50" },
>> + { 75, __B75, "__B75" },
>> + { 110, __B110, "__B110" },
>> + { 134, __B134, "__B134" },
>> + { 150, __B150, "__B150" },
>> + { 200, __B200, "__B200" },
>> + { 300, __B300, "__B300" },
>> + { 600, __B600, "__B600" },
>> + { 1200, __B1200, "__B1200" },
>> + { 1800, __B1800, "__B1800" },
>> + { 2400, __B2400, "__B2400" },
>> + { 4800, __B4800, "__B4800" },
>> +#ifdef __B7200
>> + { 7200, __B7200, "__B7200" },
>> +#endif
>> + { 9600, __B9600, "__B9600" },
>> +#ifdef __B14400
>> + { 14400, __B14400, "__B14400" },
>> +#endif
>> + { 19200, __B19200, "__B19200" },
>> +#ifdef __B28800
>> + { 28800, __B28800, "__B28800" },
>> +#endif
>> + { 38400, __B38400, "__B38400" },
>> + { 57600, __B57600, "__B57600" },
>> +#ifdef __B76800
>> + { 76800, __B76800, "__B76800" },
>> +#endif
>> + { 115200, __B115200, "__B115200" },
>> +#ifdef __B153600
>> + { 153600, __B153600, "__B153600" },
>> +#endif
>> + { 230400, __B230400, "__B230400" },
>> +#ifdef __B307200
>> + { 307200, __B307200, "__B307200" },
>> +#endif
>> + { 460800, __B460800, "__B460800" },
>> + { 500000, __B500000, "__B500000" },
>> + { 576000, __B576000, "__B576000" },
>> +#ifdef __B614400
>> + { 614400, __B614400, "__B614400" },
>> +#endif
>> + { 921600, __B921600, "__B921600" },
>> + { 1000000, __B1000000, "__B1000000" },
>> + { 1152000, __B1152000, "__B1152000" },
>> + { 1500000, __B1500000, "__B1500000" },
>> + { 2000000, __B2000000, "__B2000000" },
>> +#ifdef __B2500000
>> + { 2500000, __B2500000, "__B2500000" },
>> +#endif
>> +#ifdef __B3000000
>> + { 3000000, __B3000000, "__B3000000" },
>> +#endif
>> +#ifdef __B3500000
>> + { 3500000, __B3500000, "__B3500000" },
>> +#endif
>> +#ifdef __B4000000
>> + { 4000000, __B4000000, "__B4000000" },
>> +#endif
>> + { ANY, __BOTHER, "__BOTHER" },
>> + { BOGUS, BOGUS, "invalid" }
>> +};
>> +
>> +/* List of common speeds to test */
>> +
>> +#define NUM_TEST_SPEEDS 41
>> +static const speed_t test_speeds [NUM_TEST_SPEEDS] =
>> +{
>> + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
>> + 4800, 7200, 9600, 14400, 19200, 28800, 33600, 38400, 57600,
>> + 76800, 115200, 153600, 230400, 307200, 460800, 500000,
>> + 576000, 614400, 921600, 1000000, 1152000, 1500000, 2000000,
>> + 2500000, 3000000, 3500000, 4000000, 5000000, 10000000
>> +};
>
Alpha, of course, is the trouble child that didn't get termios2 until 4.20...
More information about the Libc-alpha
mailing list