diff options
author | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2020-07-08 01:18:05 +0000 |
---|---|---|
committer | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2020-07-08 01:18:05 +0000 |
commit | cf420c1356813b8f665f5bc53c266e5d39e2bb5f (patch) | |
tree | 4ac35ca2caff4314b91e9a07c9447fc4a13a9d5e /regress/lib | |
parent | 8a5f3c0b3e76e88fa546119d56f9cd5930d5dd93 (diff) |
New regression tests for character and string conversions
and for their modifiers, written from scratch.
Diffstat (limited to 'regress/lib')
-rw-r--r-- | regress/lib/libc/printf/Makefile | 12 | ||||
-rw-r--r-- | regress/lib/libc/printf/string.c | 435 |
2 files changed, 445 insertions, 2 deletions
diff --git a/regress/lib/libc/printf/Makefile b/regress/lib/libc/printf/Makefile index c2e2732d6e1..fba2bd105fc 100644 --- a/regress/lib/libc/printf/Makefile +++ b/regress/lib/libc/printf/Makefile @@ -1,5 +1,13 @@ -# $OpenBSD: Makefile,v 1.1 2008/09/07 20:36:10 martynas Exp $ +# $OpenBSD: Makefile,v 1.2 2020/07/08 01:18:04 schwarze Exp $ -PROG= fp +PROGS = fp string + +REGRESS_TARGETS = ${PROGS:S/^/run-regress-/} + +.for p in ${PROGS} +run-regress-${p}: ${p} + ./${p} +.PHONY: run-regress-${p} +.endfor .include <bsd.regress.mk> diff --git a/regress/lib/libc/printf/string.c b/regress/lib/libc/printf/string.c new file mode 100644 index 00000000000..afbc4fc79c2 --- /dev/null +++ b/regress/lib/libc/printf/string.c @@ -0,0 +1,435 @@ +/* $OpenBSD: string.c,v 1.1 2020/07/08 01:18:04 schwarze Exp $ */ +/* + * Copyright (c) 2020 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Test the %c, %lc, %s, and %ls conversion specifiers with all their + * modifiers, in particular with the minus flag, width, and maxbytes. + * Also verify that other flags do nothing useful. + */ +#include <err.h> +#include <errno.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> + +void tc(const char *, int, const char *); +void tlc(const char *, wint_t, const char *); +void tlc_expect_fail(const char *, wint_t); +void ts(const char *, const char *, const char *); +void tls(const char *, const wchar_t *, const char *); +void tls_expect_fail(const char *, const wchar_t *); + +static int badret, badlen, badout; /* Error counters. */ +static int verbose; /* For debugging. */ + + +/* + * Print the single-byte character c with the format fmt, + * check that the result matches what we want, + * and report and count the error on failure. + */ +void +tc(const char *fmt, int c, const char *want) +{ + char buf[32]; + size_t len; + int irc, happy; + + happy = 1; + irc = snprintf(buf, sizeof(buf), fmt, c); + len = strlen(want); + if (irc < 0) { + warn("printf(\"%s\", %d) returned %d", fmt, c, irc); + badret++; + return; + } + if ((unsigned long long)irc != len) { + warnx("printf(\"%s\", %d) returned %d (expected %zu)", + fmt, c, irc, len); + badlen++; + happy = 0; + } + if (strcmp(buf, want) != 0) { + warnx("printf(\"%s\", %d) wrote \"%s\" (expected \"%s\")", + fmt, c, buf, want); + badout++; + happy = 0; + } + if (verbose && happy) + warnx("printf(\"%s\", %d) wrote \"%s\" length %d (OK)", + fmt, c, buf, irc); +} + +/* + * Print the wide character wc with the format fmt, + * check that the result matches what we want, + * and report and count the error on failure. + */ +void +tlc(const char *fmt, wint_t wc, const char *want) +{ + char buf[32]; + const char *charset; + size_t len; + int irc, happy; + + happy = 1; + charset = MB_CUR_MAX > 1 ? "UTF-8" : "ASCII"; + irc = snprintf(buf, sizeof(buf), fmt, wc); + len = strlen(want); + if (irc < 0) { + warn("%s printf(\"%s\", U+%.4X) returned %d", + charset, fmt, (unsigned int)wc, irc); + badret++; + return; + } + if ((unsigned long long)irc != len) { + warnx("%s printf(\"%s\", U+%.4X) returned %d (expected %zu)", + charset, fmt, (unsigned int)wc, irc, len); + badlen++; + happy = 0; + } + if (strcmp(buf, want) != 0) { + warnx("%s printf(\"%s\", U+%.4X) " + "wrote \"%s\" (expected \"%s\")", + charset, fmt, (unsigned int)wc, buf, want); + badout++; + happy = 0; + } + if (verbose && happy) + warnx("%s printf(\"%s\", U+%.4X) wrote \"%s\" length %d (OK)", + charset, fmt, (unsigned int)wc, buf, irc); +} + +/* + * Try to print the invalid wide character wc with the format fmt, + * check that it fails as it should, and report and count if it doesn't. + */ +void +tlc_expect_fail(const char *fmt, wint_t wc) +{ + char buf[32]; + const char *charset; + int irc; + + errno = 0; + charset = MB_CUR_MAX > 1 ? "UTF-8" : "ASCII"; + irc = snprintf(buf, sizeof(buf), fmt, wc); + if (irc != -1) { + warn("%s printf(\"%s\", U+%.4X) returned %d", + charset, fmt, (unsigned int)wc, irc); + badret++; + } else if (errno != EILSEQ) { + warnx("%s printf(\"%s\", U+%.4X) errno %d (expected %d)", + charset, fmt, (unsigned int)wc, errno, EILSEQ); + badret++; + } else if (verbose) + warnx("%s printf(\"%s\", U+%.4X) returned %d errno %d (OK)", + charset, fmt, (unsigned int)wc, irc, errno); +} + +/* + * Print the string s with the format fmt, + * check that the result matches what we want, + * and report and count the error on failure. + */ +void +ts(const char *fmt, const char *s, const char *want) +{ + char buf[32]; + size_t len; + int irc, happy; + + happy = 1; + irc = snprintf(buf, sizeof(buf), fmt, s); + len = strlen(want); + if (irc < 0) { + warn("printf(\"%s\", \"%s\") returned %d", fmt, s, irc); + badret++; + return; + } + if ((unsigned long long)irc != len) { + warnx("printf(\"%s\", \"%s\") returned %d (expected %zu)", + fmt, s, irc, len); + badlen++; + happy = 0; + } + if (strcmp(buf, want) != 0) { + warnx("printf(\"%s\", \"%s\") wrote \"%s\" (expected \"%s\")", + fmt, s, buf, want); + badout++; + happy = 0; + } + if (verbose && happy) + warnx("printf(\"%s\", \"%s\") wrote \"%s\" length %d (OK)", + fmt, s, buf, irc); +} + +/* + * Print the wide character string ws with the format fmt, + * check that the result matches what we want, + * and report and count the error on failure. + */ +void +tls(const char *fmt, const wchar_t *ws, const char *want) +{ + char buf[32]; + const char *charset; + size_t len; + int irc, happy; + + happy = 1; + charset = MB_CUR_MAX > 1 ? "UTF-8" : "ASCII"; + irc = snprintf(buf, sizeof(buf), fmt, ws); + len = strlen(want); + if (irc < 0) { + warn("%s printf(\"%s\", \"%ls\") returned %d", + charset, fmt, ws, irc); + badret++; + return; + } + if ((unsigned long long)irc != len) { + warnx("%s printf(\"%s\", \"%ls\") returned %d (expected %zu)", + charset, fmt, ws, irc, len); + badlen++; + happy = 0; + } + if (strcmp(buf, want) != 0) { + warnx("%s printf(\"%s\", \"%ls\") " + "wrote \"%s\" (expected \"%s\")", + charset, fmt, ws, buf, want); + badout++; + happy = 0; + } + if (verbose && happy) + warnx("%s printf(\"%s\", \"%ls\") wrote \"%s\" length %d (OK)", + charset, fmt, ws, buf, irc); +} + +/* + * Try to print the invalid wide character string ws with the format fmt, + * check that it fails as it should, and report and count if it doesn't. + */ +void +tls_expect_fail(const char *fmt, const wchar_t *ws) +{ + char buf[32]; + const char *charset; + int irc; + + errno = 0; + charset = MB_CUR_MAX > 1 ? "UTF-8" : "ASCII"; + irc = snprintf(buf, sizeof(buf), fmt, ws); + if (irc != -1) { + warn("%s printf(\"%s\", U+%.4X, ...) returned %d", + charset, fmt, (unsigned int)*ws, irc); + badret++; + } else if (errno != EILSEQ) { + warnx("%s printf(\"%s\", U+%.4X, ...) errno %d (expected %d)", + charset, fmt, (unsigned int)*ws, errno, EILSEQ); + badret++; + } else if (verbose) + warnx("%s printf(\"%s\", U+%.4X, ...) " + "returned %d errno %d (OK)", + charset, fmt, (unsigned int)*ws, irc, errno); +} + +int +main(int argc, char *argv[]) +{ + const wchar_t ws[] = { 0x0421, 0x043e, 0x0444, 0x044f, 0 }; + const wchar_t wsbad[] = { 0x0391, 0xdeef, 0x3c9, 0 }; + int badarg, picky; + char ch; + + badarg = picky = 0; + while ((ch = getopt(argc, argv, "pv")) != -1) { + switch (ch) { + case 'p': + picky = 1; + break; + case 'v': + verbose = 1; + break; + default: + badarg = 1; + break; + } + } + argc -= optind; + argv += optind; + if (argc > 0) { + warnx("unexpected argument \"%s\"", *argv); + badarg = 1; + } + if (badarg) { + fputs("usage: string [-pv]\n", stderr); + return 1; + } + + /* + * Valid use cases of %c and %s. + */ + + tc("<%c>", '=', "<=>"); + tc("<%c>", '\t', "<\t>"); + tc("<%c>", 0xfe, "<\xfe>"); + tc("<%-c>", '=', "<=>"); + tc("<%2c>", '=', "< =>"); + tc("<%-2c>", '=', "<= >"); + + ts("<%s>", "text", "<text>"); + ts("<%-s>", "text", "<text>"); + ts("<%6s>", "text", "< text>"); + ts("<%-6s>", "text", "<text >"); + ts("<%.2s>", "text", "<te>"); + ts("<%4.2s>", "text", "< te>"); + ts("<%-4.2s>", "text", "<te >"); + + /* + * Undefined behaviour of %c and %s. + * Do not test by default to avoid noise. + * But provide the tests anyway to help track down + * unintended changes of behaviour when needed. + */ + + if (picky) { + tc("<%#c>", '=', "<=>"); + tc("<% -3c>", '=', "<= >"); + tc("<%+-3c>", '=', "<= >"); + tc("<%03c>", '=', "<00=>"); + tc("<%-03c>", '=', "<= >"); + tc("<%3.2c>", '=', "< =>"); + tc("<%hc>", '=', "<=>"); + + ts("<%#s>", "text", "<text>"); + ts("<% -6s>", "text", "<text >"); + ts("<%+-6s>", "text", "<text >"); + ts("<%06s>", "text", "<00text>"); + ts("<%-06s>", "text", "<text >"); + ts("<%hs>", "text", "<text>"); + } + + /* + * Valid use cases of %lc and %ls in the POSIX locale. + */ + + tlc("<%lc>", L'=', "<=>"); + tlc("<%lc>", L'\t', "<\t>"); + tlc_expect_fail("<%lc>", 0x03c0); + tlc("<%-lc>", L'=', "<=>"); + tlc("<%2lc>", L'=', "< =>"); + tlc("<%-2lc>", L'=', "<= >"); + + tls("<%ls>", L"text", "<text>"); + tls_expect_fail("<%ls>", ws); + tls_expect_fail("<%ls>", wsbad); + tls("<%-ls>", L"text", "<text>"); + tls("<%6ls>", L"text", "< text>"); + tls("<%-6ls>", L"text", "<text >"); + tls("<%.2ls>", L"text", "<te>"); + tls("<%4.2ls>", L"text", "< te>"); + tls("<%-4.2ls>", L"text", "<te >"); + + /* + * Undefined behaviour of %lc and %ls in the POSIX locale. + */ + + if (picky) { + tlc("<%lc>", 0x00fe, "<\xfe>"); + tlc("<%#lc>", L'=', "<=>"); + tlc("<% -3lc>", L'=', "<= >"); + tlc("<%+-3lc>", L'=', "<= >"); + tlc("<%03lc>", L'=', "<00=>"); + tlc("<%-03lc>", L'=', "<= >"); + tlc("<%3.2lc>", L'=', "< =>"); + tc("<%llc>", '=', "<=>"); + + tls("<%#ls>", L"text", "<text>"); + tls("<% -6ls>", L"text", "<text >"); + tls("<%+-6ls>", L"text", "<text >"); + tls("<%06ls>", L"text", "<00text>"); + tls("<%-06ls>", L"text", "<text >"); + ts("<%lls>", "text", "<text>"); + } + + /* + * Valid use cases of %lc and %ls in a UTF-8 locale. + */ + + if (setlocale(LC_CTYPE, "C.UTF-8") == NULL) + err(1, "setlocale"); + + tlc("<%lc>", L'=', "<=>"); + tlc("<%lc>", L'\t', "<\t>"); + tlc("<%lc>", 0x00fe, "<\xc3\xbe>"); + tlc("<%lc>", 0x03c0, "<\xcf\x80>"); + tlc_expect_fail("<%lc>", 0x123456); + tlc("<%-lc>", L'=', "<=>"); + tlc("<%-lc>", 0x03c0, "<\xcf\x80>"); + tlc("<%2lc>", L'=', "< =>"); + tlc("<%3lc>", 0x03c0, "< \xcf\x80>"); + tlc("<%-2lc>", L'=', "<= >"); + tlc("<%-3lc>", 0x03c0, "<\xcf\x80 >"); + + tls("<%ls>", ws, "<\xd0\xa1\xd0\xbe\xd1\x84\xd1\x8f>"); + tls_expect_fail("<%ls>", wsbad); + tls("<%-ls>", ws, "<\xd0\xa1\xd0\xbe\xd1\x84\xd1\x8f>"); + tls("<%9ls>", ws, "< \xd0\xa1\xd0\xbe\xd1\x84\xd1\x8f>"); + tls("<%-9ls>", ws, "<\xd0\xa1\xd0\xbe\xd1\x84\xd1\x8f >"); + tls("<%.4ls>", ws, "<\xd0\xa1\xd0\xbe>"); + tls("<%.3ls>", ws, "<\xd0\xa1>"); + tls("<%6.4ls>", ws, "< \xd0\xa1\xd0\xbe>"); + tls("<%3.3ls>", ws, "< \xd0\xa1>"); + tls("<%-6.4ls>", ws, "<\xd0\xa1\xd0\xbe >"); + tls("<%-3.3ls>", ws, "<\xd0\xa1 >"); + + /* + * Undefined behaviour of %lc and %ls in a UTF-8 locale. + */ + + if (picky) { + tlc("<%#lc>", 0x03c0, "<\xcf\x80>"); + tlc("<% -4lc>", 0x03c0, "<\xcf\x80 >"); + tlc("<%+-4lc>", 0x03c0, "<\xcf\x80 >"); + tlc("<%04lc>", 0x03c0, "<00\xcf\x80>"); + tlc("<%-04lc>", 0x03c0, "<\xcf\x80 >"); + tlc("<%4.5lc>", 0x03c0, "< \xcf\x80>"); + tlc("<%4.3lc>", 0x03c0, "< \xcf\x80>"); + tlc("<%4.1lc>", 0x03c0, "< \xcf\x80>"); + tc("<%llc>", 0xfe, "<\xfe>"); + + tls("<%#ls>", ws + 2, "<\xd1\x84\xd1\x8f>"); + tls("<% -6ls>", ws + 2, "<\xd1\x84\xd1\x8f >"); + tls("<%+-6ls>", ws + 2, "<\xd1\x84\xd1\x8f >"); + tls("<%06ls>", ws + 2, "<00\xd1\x84\xd1\x8f>"); + tls("<%-06ls>", ws + 2, "<\xd1\x84\xd1\x8f >"); + ts("<%lls>", "text", "<text>"); + } + + /* + * Summarize the results. + */ + + if (badret + badlen + badout) + errx(1, "ERRORS: %d fail + %d mismatch (incl. %d bad length)", + badret, badout, badlen); + else if (verbose) + warnx("SUCCESS"); + return 0; +} |