Module Name:    src
Committed By:   rillig
Date:           Thu Jan 25 18:13:14 UTC 2024

Modified Files:
        src/tests/lib/libutil: t_snprintb.c

Log Message:
tests/libutil/snprintb: clean up and extend tests

In case of a failure, print the details of the test case, including file
and line number of the actual test data. Do not write the format strings
directly to the output, as they contain non-printable bytes and embedded
null bytes.

After a failed test case, continue with the others.

Lay out the format strings according to their structure, to make them
more readable. Remove redundant "\0" at the end of the new-style format
strings.

Fix an off-by-one error in the test data: 0xf is FIFTEEN, not SIXTEEN.

Add a test for performing a restricted subset of rot13 in the format
string, to explore the limits of snprintb formatting.

What's still missing are tests for edge cases and error cases.


To generate a diff of this commit:
cvs rdiff -u -r1.8 -r1.9 src/tests/lib/libutil/t_snprintb.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/tests/lib/libutil/t_snprintb.c
diff -u src/tests/lib/libutil/t_snprintb.c:1.8 src/tests/lib/libutil/t_snprintb.c:1.9
--- src/tests/lib/libutil/t_snprintb.c:1.8	Sun Dec  8 17:37:16 2019
+++ src/tests/lib/libutil/t_snprintb.c	Thu Jan 25 18:13:14 2024
@@ -1,10 +1,11 @@
-/* $NetBSD: t_snprintb.c,v 1.8 2019/12/08 17:37:16 christos Exp $ */
+/* $NetBSD: t_snprintb.c,v 1.9 2024/01/25 18:13:14 rillig Exp $ */
 
 /*
- * Copyright (c) 2002, 2004, 2008, 2010 The NetBSD Foundation, Inc.
+ * Copyright (c) 2002, 2004, 2008, 2010, 2024 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
- * This code was contributed to The NetBSD Foundation by Christos Zoulas.
+ * This code was contributed to The NetBSD Foundation by Christos Zoulas and
+ * Roland Illig.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -31,27 +32,58 @@
 #include <sys/cdefs.h>
 __COPYRIGHT("@(#) Copyright (c) 2008, 2010\
  The NetBSD Foundation, inc. All rights reserved.");
-__RCSID("$NetBSD: t_snprintb.c,v 1.8 2019/12/08 17:37:16 christos Exp $");
+__RCSID("$NetBSD: t_snprintb.c,v 1.9 2024/01/25 18:13:14 rillig Exp $");
 
+#include <stdio.h>
 #include <string.h>
 #include <util.h>
+#include <vis.h>
 
 #include <atf-c.h>
 
+static const char *
+vis_fmt(const char *fmt, size_t fmtlen)
+{
+	static char buf[4][1024];
+	static size_t i;
+
+	i = (i + 1) % 4;
+	ATF_REQUIRE_MSG(
+	    strnvisx(buf[i], sizeof(buf[i]), fmt, fmtlen, VIS_WHITE | VIS_OCTAL) >= 0,
+	    "strnvisx failed for length %zu", fmtlen);
+	return buf[i];
+}
+
 static void
-h_snprintb(const char *fmt, uint64_t val, const char *expected)
+h_snprintb(const char *file, size_t line, const char *fmt, size_t fmtlen, uint64_t val,
+    const char *res, size_t reslen)
 {
-	char actual[1024];
-	int len, rlen;
+	char buf[1024];
+
+	int rv = snprintb(buf, sizeof(buf), fmt, val);
 
-	len = snprintb(actual, sizeof(actual), fmt, val);
-	rlen = (int)strlen(actual);
+	ATF_REQUIRE_MSG(rv > 0, "formatting %jx with '%s' returns error %d",
+	    (uintmax_t)val, vis_fmt(fmt, fmtlen), rv);
 
-	ATF_REQUIRE_STREQ_MSG(expected, actual, "format=%s val=%" PRIu64,
-	    fmt, val);
-	ATF_REQUIRE_EQ_MSG(rlen, len, "expected=%d actual=%d", rlen, len);
+	size_t buflen = rv;
+	ATF_CHECK_MSG(
+	    buflen == reslen && memcmp(buf, res, reslen) == 0,
+	    "failed:\n"
+	    "\ttest case: %s:%zu\n"
+	    "\tformat: %s\n"
+	    "\tvalue: %#jx\n"
+	    "\twant: %3zu bytes %s\n"
+	    "\thave: %3zu bytes %s\n",
+	    file, line,
+	    vis_fmt(fmt, fmtlen), (uintmax_t)val,
+	    reslen, vis_fmt(res, reslen),
+	    buflen, vis_fmt(buf, buflen));
 }
 
+#define	h_snprintb(fmt, val, res)					\
+	h_snprintb(__FILE__, __LINE__, fmt, sizeof(fmt) - 1,		\
+	    val, res, sizeof(res) - 1)
+
 ATF_TC(snprintb);
 ATF_TC_HEAD(snprintb, tc)
 {
@@ -59,72 +91,190 @@ ATF_TC_HEAD(snprintb, tc)
 }
 ATF_TC_BODY(snprintb, tc)
 {
-	h_snprintb("\10\2BITTWO\1BITONE", 3, "03<BITTWO,BITONE>");
-	h_snprintb("\177\20b\0A\0\0", 0, "0");
-
-	h_snprintb("\177\20b\05NOTBOOT\0b\06FPP\0b\013SDVMA\0b\015VIDEO\0"
-		"b\020LORES\0b\021FPA\0b\022DIAG\0b\016CACHE\0"
-		"b\017IOCACHE\0b\022LOOPBACK\0b\04DBGCACHE\0",
-		0xe860, "0xe860<NOTBOOT,FPP,SDVMA,VIDEO,CACHE,IOCACHE>");
-
-	h_snprintb("\177\20f\0\4FOO\0=\1ONE\0=\2TWO\0\0", 1,
+	h_snprintb(
+	    "\010"
+	    "\002BITTWO"
+	    "\001BITONE",
+	    3,
+	    "03<BITTWO,BITONE>");
+
+	h_snprintb(
+	    "\177\20"
+	    "b\0A\0",
+	    0,
+	    "0");
+
+	h_snprintb(
+	    "\177\020"
+	    "b\05NOTBOOT\0"
+	    "b\06FPP\0"
+	    "b\13SDVMA\0"
+	    "b\15VIDEO\0"
+	    "b\20LORES\0"
+	    "b\21FPA\0"
+	    "b\22DIAG\0"
+	    "b\16CACHE\0"
+	    "b\17IOCACHE\0"
+	    "b\22LOOPBACK\0"
+	    "b\04DBGCACHE\0",
+	    0xe860,
+	    "0xe860<NOTBOOT,FPP,SDVMA,VIDEO,CACHE,IOCACHE>");
+
+	h_snprintb(
+	    "\177\20"
+	    "f\0\4FOO\0"
+		"=\1ONE\0"
+		"=\2TWO\0",
+	    1,
 	    "0x1<FOO=0x1=ONE>");
-	h_snprintb("\177\20f\0\4X\0=\1ONE\0=\2TWO\0\0", 1,
+	h_snprintb(
+	    "\177\20"
+	    "f\0\4X\0"
+		"=\1ONE\0"
+		"=\2TWO\0",
+	    1,
 	    "0x1<X=0x1=ONE>");
-	h_snprintb("\177\020F\0\4\0:\1ONE\0:\2TWO\0\0", 1,
+	h_snprintb(
+	    "\177\20"
+	    "F\0\4\0"
+		":\1ONE\0"
+		":\2TWO\0",
+	    1,
 	    "0x1<ONE>");
 
-	h_snprintb("\177\20f\0\4FOO\0=\1ONE\0=\2TWO\0\0", 2,
+	h_snprintb(
+	    "\177\20"
+	    "f\0\4FOO\0"
+		"=\1ONE\0"
+		"=\2TWO\0",
+	    2,
 	    "0x2<FOO=0x2=TWO>");
-	h_snprintb("\177\20f\0\4X\0=\1ONE\0=\2TWO\0\0", 2,
+	h_snprintb(
+	    "\177\20"
+	    "f\0\4X\0"
+		"=\1ONE\0"
+		"=\2TWO\0",
+	    2,
 	    "0x2<X=0x2=TWO>");
-	h_snprintb("\177\020F\0\4\0:\1ONE\0:\2TWO\0\0", 2,
+	h_snprintb(
+	    "\177\020"
+	    "F\0\4\0"
+		":\1ONE\0"
+		":\2TWO\0",
+	    2,
 	    "0x2<TWO>");
 
-	h_snprintb("\177\20f\0\4FOO\0=\1ONE\0=\2TWO\0*=OTHER\0\0", 3,
+	h_snprintb(
+	    "\177\20"
+	    "f\0\4FOO\0"
+		"=\1ONE\0"
+		"=\2TWO\0"
+		"*=OTHER\0",
+	    3,
 	    "0x3<FOO=0x3=OTHER>");
-	h_snprintb("\177\20f\0\4X\0=\1ONE\0=\2TWO\0*=Other(%jd)\0\0", 3,
+	h_snprintb(
+	    "\177\20"
+	    "f\0\4X\0"
+		"=\1ONE\0"
+		"=\2TWO\0"
+		"*=Other(%jd)\0",
+	    3,
 	    "0x3<X=0x3=Other(3)>");
-	h_snprintb("\177\20f\0\x8X\0=\1ONE\0=\2TWO\0*=other(%jo)\0\0", 0x20,
+	h_snprintb(
+	    "\177\20"
+	    "f\0\x8X\0"
+		"=\1ONE\0"
+		"=\2TWO\0"
+		"*=other(%jo)\0",
+	    0x20,
 	    "0x20<X=0x20=other(40)>");
-	h_snprintb("\177\020F\0\4\0:\1ONE\0:\2TWO\0\0", 3,
+	h_snprintb(
+	    "\177\020"
+	    "F\0\4\0"
+		":\1ONE\0"
+		":\2TWO\0",
+	    3,
 	    "0x3<>");
 
-	h_snprintb("\177\20f\0\4Field_1\0=\1ONE\0=\2TWO\0"
-		   "f\4\4Field_2\0=\1ONE\0=\2TWO\0\0", 0x12,
+	h_snprintb(
+	    "\177\20"
+	    "f\0\4Field_1\0"
+		"=\1ONE\0"
+		"=\2TWO\0"
+	    "f\4\4Field_2\0"
+		"=\1ONE\0"
+		"=\2TWO\0",
+	    0x12,
 	    "0x12<Field_1=0x2=TWO,Field_2=0x1=ONE>");
 
-	h_snprintb("\177\20f\0\4Field_1\0=\1ONE\0=\2TWO\0"
-		   "F\x8\4\0*Field_3=%jd\0"
-		   "f\4\4Field_2\0:\1:ONE\0:\2:TWO\0\0", 0xD12,
+	h_snprintb(
+	    "\177\20"
+	    "f\0\4Field_1\0"
+		"=\1ONE\0"
+		"=\2TWO\0"
+	    "F\x8\4\0"
+		"*Field_3=%jd\0"
+	    "f\4\4Field_2\0"
+		":\1:ONE\0"
+		":\2:TWO\0",
+	    0xD12,
 	    "0xd12<Field_1=0x2=TWO,Field_3=13,Field_2=0x1:ONE>");
+
+	// It is possible but cumbersome to implement a reduced variant of
+	// rot13 using snprintb, shown here for lowercase letters only.
+	for (char ch = 'A'; ch <= '~'; ch++) {
+		char rot13 = ch >= 'a' && ch <= 'm' ? ch + 13
+		    : ch >= 'n' && ch <= 'z' ? ch - 13
+		    : '?';
+		char expected[8];
+		ATF_REQUIRE_EQ(7,
+		    snprintf(expected, sizeof(expected), "%#x<%c>", ch, rot13));
+		h_snprintb(
+		    "\177\020"
+		    "F\000\010\0"
+		    ":an\0:bo\0:cp\0:dq\0:er\0:fs\0:gt\0:hu\0"
+		    ":iv\0:jw\0:kx\0:ly\0:mz\0"
+		    ":na\0:ob\0:pc\0:qd\0:re\0:sf\0:tg\0:uh\0"
+		    ":vi\0:wj\0:xk\0:yl\0:zm\0"
+		    // If snprintf accepted "%jc", it would be possible to
+		    // echo the non-alphabetic characters instead of a
+		    // catchall question mark.
+		    "*?\0",
+		    ch,
+		    expected);
+	}
 }
 
 static void
-h_snprintb_m(const char *fmt, uint64_t val, int line_max, const char *res,
-     int rlen)
+h_snprintb_m(const char *file, size_t line, const char *fmt, size_t fmtlen, uint64_t val, int line_max,
+    const char *res, size_t reslen)
 {
 	char buf[1024];
-	int len;
 
-	len = snprintb_m(buf, sizeof(buf), fmt, val, line_max);
+	int rv = snprintb_m(buf, sizeof(buf), fmt, val, line_max);
 
-	const char *expected = res;
-	char *actual = buf;
-	int l = 0;
-	for (size_t i = 0; l < rlen; i++) {
-		l = (int)strlen(actual);
-		if (l == 0)
-			break;
-		ATF_REQUIRE_STREQ_MSG(expected, actual,
-		    "%zu: fmt=%s val=%" PRIu64, i, fmt, val);
-		actual += l;
-		expected += l;
-	}
-		
-	ATF_REQUIRE_EQ_MSG(rlen, len, "expected=%d actual=%d", rlen, len);
+	ATF_REQUIRE_MSG(rv >= 0, "formatting %jx with '%s' returns error %d",
+	    (uintmax_t)val, vis_fmt(fmt, fmtlen), rv);
+
+	size_t buflen = rv;
+	ATF_CHECK_MSG(
+	    buflen == reslen && memcmp(buf, res, reslen) == 0,
+	    "failed:\n"
+	    "\ttest case: %s:%zu\n"
+	    "\tformat: %s\n"
+	    "\tvalue: %#jx\n"
+	    "\twant: %zu bytes %s\n"
+	    "\thave: %zu bytes %s\n",
+	    file, line,
+	    vis_fmt(fmt, fmtlen), (uintmax_t)val,
+	    reslen, vis_fmt(res, reslen),
+	    buflen, vis_fmt(buf, buflen));
 }
 
+#define	h_snprintb_m(fmt, val, line_max, res)				\
+	h_snprintb_m(__FILE__, __LINE__, fmt, sizeof(fmt) - 1,		\
+	    val, line_max, res, sizeof(res) - 1)
+
 ATF_TC(snprintb_m);
 ATF_TC_HEAD(snprintb_m, tc)
 {
@@ -132,24 +282,34 @@ ATF_TC_HEAD(snprintb_m, tc)
 }
 ATF_TC_BODY(snprintb_m, tc)
 {
-	h_snprintb_m("\177\020b\0LSB\0b\1_BITONE\0f\4\4NIBBLE2\0"
-			"f\x10\4BURST\0=\4FOUR\0=\xfSIXTEEN\0"
-			"b\x1fMSB\0\0",
-                     0x800f0701,
-		     33,
-		     "0x800f0701<LSB,NIBBLE2=0>\0"
-			"0x800f0701<BURST=0xf=SIXTEEN,MSB>\0\0",
-		     60);
-
-	h_snprintb_m("\177\020b\0LSB\0b\1_BITONE\0f\4\4NIBBLE2\0"
-			"f\x10\4BURST\0=\4FOUR\0=\xfSIXTEEN\0"
-			"b\x1fMSB\0\0",
-                     0x800f0701,
-		     32,
-		     "0x800f0701<LSB,NIBBLE2=0>\0"
-			"0x800f0701<BURST=0xf=SIXTEEN>\0"
-			"0x800f0701<MSB>\0\0",
-		     72);
+	h_snprintb_m(
+	    "\177\020"
+	    "b\0LSB\0"
+	    "b\1_BITONE\0"
+	    "f\4\4NIBBLE2\0"
+	    "f\x10\4BURST\0"
+		"=\04FOUR\0"
+		"=\17FIFTEEN\0"
+	    "b\x1fMSB\0",
+	    0x800f0701,
+	    33,
+	    "0x800f0701<LSB,NIBBLE2=0>\0"
+	    "0x800f0701<BURST=0xf=FIFTEEN,MSB>\0");
+
+	h_snprintb_m(
+	    "\177\020"
+	    "b\0LSB\0"
+	    "b\1_BITONE\0"
+	    "f\4\4NIBBLE2\0"
+	    "f\x10\4BURST\0"
+		"=\04FOUR\0"
+		"=\17FIFTEEN\0"
+	    "b\x1fMSB\0",
+	    0x800f0701,
+	    32,
+	    "0x800f0701<LSB,NIBBLE2=0>\0"
+	    "0x800f0701<BURST=0xf=FIFTEEN>\0"
+	    "0x800f0701<MSB>\0");
 }
 
 ATF_TP_ADD_TCS(tp)

Reply via email to