1 /*
2 * Copyright © 2005-2020 Rich Felker
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #define _GNU_SOURCE
25 #include <stdio.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <math.h>
30
31 #pragma GCC diagnostic ignored "-Wpragmas"
32 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
33 #pragma GCC diagnostic ignored "-Wformat-extra-args"
34 #pragma GCC diagnostic ignored "-Wformat"
35 #pragma GCC diagnostic ignored "-Wformat-truncation"
36 #pragma GCC diagnostic ignored "-Woverflow"
37
38 #define DISABLE_SLOW_TESTS
39
40 #define TEST(r, f, x, m) ( \
41 ((r) = (f)) == (x) || \
42 (printf(__FILE__ ":%d: %s failed (" m ")\n", __LINE__, #f, r, x), err++, 0) )
43
44 #define TEST_S(s, x, m) ( \
45 !strcmp((s),(x)) || \
46 (printf(__FILE__ ":%d: [%s] != [%s] (%s)\n", __LINE__, s, x, m), err++, 0) )
47
48 static const struct {
49 const char *fmt;
50 int i;
51 const char *expect;
52 } int_tests[] = {
53 /* width, precision, alignment */
54 { "%04d", 12, "0012" },
55 { "%.3d", 12, "012" },
56 { "%3d", 12, " 12" },
57 { "%-3d", 12, "12 " },
58 { "%+3d", 12, "+12" },
59 { "%+-5d", 12, "+12 " },
60 { "%+- 5d", 12, "+12 " },
61 { "%- 5d", 12, " 12 " },
62 { "% d", 12, " 12" },
63 { "%0-5d", 12, "12 " },
64 { "%-05d", 12, "12 " },
65
66 /* ...explicit precision of 0 shall be no characters. */
67 { "%.0d", 0, "" },
68 { "%.0o", 0, "" },
69 { "%#.0d", 0, "" },
70 #ifdef TINY_STDIO
71 { "%#.0o", 0, "" },
72 #endif
73 { "%#.0x", 0, "" },
74
75 /* ...but it still has to honor width and flags. */
76 { "%2.0u", 0, " " },
77 { "%02.0u", 0, " " },
78 { "%2.0d", 0, " " },
79 { "%02.0d", 0, " " },
80 { "% .0d", 0, " " },
81 { "%+.0d", 0, "+" },
82
83 /* hex: test alt form and case */
84 { "%x", 63, "3f" },
85 { "%#x", 63, "0x3f" },
86 { "%X", 63, "3F" },
87
88 /* octal: test alt form */
89 { "%o", 15, "17" },
90 { "%#o", 15, "017" },
91
92 { NULL, 0.0, NULL }
93 };
94
95 static const struct {
96 const char *fmt;
97 double f;
98 const char *expect;
99 } fp_tests[] = {
100 /* basic form, handling of exponent/precision for 0 */
101 { "%e", 0.0, "0.000000e+00" },
102 { "%f", 0.0, "0.000000" },
103 { "%g", 0.0, "0" },
104 { "%#g", 0.0, "0.00000" },
105
106 /* rounding */
107 { "%f", 1.1, "1.100000" },
108 { "%f", 1.2, "1.200000" },
109 { "%f", 1.3, "1.300000" },
110 { "%f", 1.4, "1.400000" },
111 { "%f", 1.5, "1.500000" },
112 { "%.4f", 1.06125, "1.0613" },
113 { "%.2f", 1.375, "1.38" },
114 { "%.1f", 1.375, "1.4" },
115 { "%.15f", 1.1, "1.100000000000000" },
116 #ifdef TINY_STDIO
117 { "%.16f", 1.1, "1.1000000000000000" },
118 { "%.17f", 1.1, "1.10000000000000000" },
119 #else
120 /* legacy stdio adds non-zero digits beyond needed precision */
121 { "%.16f", 1.1, "1.1000000000000001" },
122 { "%.17f", 1.1, "1.10000000000000009" },
123 #endif
124 { "%.2e", 1500001.0, "1.50e+06" },
125 { "%.2e", 1505000.0, "1.50e+06" },
126 { "%.2e", 1505000.00000095367431640625, "1.51e+06" },
127 { "%.2e", 1505001.0, "1.51e+06" },
128 { "%.2e", 1506000.0, "1.51e+06" },
129
130 /* correctness in DBL_DIG places */
131 { "%.15g", 1.23456789012345, "1.23456789012345" },
132
133 /* correct choice of notation for %g */
134 { "%g", 0.0001, "0.0001" },
135 { "%g", 0.00001, "1e-05" },
136 { "%g", 123456, "123456" },
137 { "%g", 1234567, "1.23457e+06" },
138 { "%.7g", 1234567, "1234567" },
139 { "%.7g", 12345678, "1.234568e+07" },
140 { "%.8g", 0.1, "0.1" },
141 { "%.9g", 0.1, "0.1" },
142 { "%.10g", 0.1, "0.1" },
143 { "%.11g", 0.1, "0.1" },
144
145 /* pi in double precision, printed to a few extra places */
146 { "%.15f", M_PI, "3.141592653589793" },
147 #ifdef TINY_STDIO
148 { "%.18f", M_PI, "3.141592653589793000" },
149 #else
150 /* legacy stdio adds non-zero digits beyond needed precision */
151 { "%.18f", M_PI, "3.141592653589793116" },
152 #endif
153
154 /* exact conversion of large integers */
155 #ifdef TINY_STDIO
156 { "%.0f", 340282366920938463463374607431768211456.0,
157 "340282366920938500000000000000000000000" },
158 #else
159 /* legacy stdio adds non-zero digits beyond needed precision */
160 { "%.0f", 340282366920938463463374607431768211456.0,
161 "340282366920938463463374607431768211456" },
162 #endif
163
164 { NULL, 0.0, NULL }
165 };
166
test_snprintf(void)167 int test_snprintf(void)
168 {
169 int i, j, k;
170 int err=0;
171 char b[2000];
172
173 TEST(i, snprintf(0, 0, "%ld", 123456l), 6, "length returned %d != %d");
174 TEST(i, snprintf(0, 0, "%.4s", "hello"), 4, "length returned %d != %d");
175 TEST(i, snprintf(b, 0, "%.0s", "goodbye"), 0, "length returned %d != %d");
176
177 strcpy(b, "xxxxxxxx");
178 TEST(i, snprintf(b, 4, "%ld", 123456l), 6, "length returned %d != %d");
179 TEST_S(b, "123", "incorrect output");
180 TEST(i, b[5], 'x', "buffer overrun");
181
182 #if __SIZEOF_DOUBLE__ == 8
183 #if defined(TINY_STDIO) || (!defined(NO_FLOATING_POINT) && (!defined(_WANT_IO_LONG_DOUBLE) || defined(_LDBL_EQ_DBL)))
184 /* Perform ascii arithmetic to test printing tiny doubles */
185 TEST(i, snprintf(b, sizeof b, "%.1022f", 0x1p-1021), 1024, "%d != %d");
186 b[1] = '0';
187 for (i=0; i<1021; i++) {
188 for (k=0, j=1023; j>0; j--) {
189 if (b[j]<'5') b[j]+=b[j]-'0'+k, k=0;
190 else b[j]+=b[j]-'0'-10+k, k=1;
191 }
192 }
193 TEST(i, b[1], '1', "'%c' != '%c'");
194 // I have no idea what this is testing, but it's not sensible
195 // for (j=2; b[j]=='0'; j++);
196 // TEST(i, j, 1024, "%d != %d");
197 #endif
198 #endif
199
200
201 #ifndef DISABLE_SLOW_TESTS
202 errno = 0;
203 TEST(i, snprintf(NULL, 0, "%.*u", 2147483647, 0), 2147483647, "cannot print max length %d");
204 // picolibc doesn't check this condition
205 // TEST(i, snprintf(NULL, 0, "%.*u ", 2147483647, 0), -1, "integer overflow %d");
206 // TEST(i, errno, EOVERFLOW, "after overflow: %d != %d");
207 #endif
208 for (j=0; int_tests[j].fmt; j++) {
209 TEST(i, snprintf(b, sizeof b, int_tests[j].fmt, int_tests[j].i), (int) strlen(b), "%d != %d");
210 TEST_S(b, int_tests[j].expect, "bad integer conversion");
211 }
212
213 (void) fp_tests;
214 (void) k;
215 #if !defined(NO_FLOATING_POINT) && defined(_IO_FLOAT_EXACT) && __SIZEOF_DOUBLE__ == 8
216 for (j=0; fp_tests[j].fmt; j++) {
217 TEST(i, snprintf(b, sizeof b, fp_tests[j].fmt, fp_tests[j].f), (int) strlen(b), "%d != %d");
218 TEST_S(b, fp_tests[j].expect, "bad floating point conversion");
219 }
220
221 TEST(i, snprintf(0, 0, "%.4a", 1.0), 11, "%d != %d");
222 #endif
223
224 return err;
225 }
226
227 #define TEST_NAME snprintf
228 #include "testcase.h"
229