1 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // Newer versions of the Windows SDK define NAN differently than before,
7 // which causes our tests to fail. Force the use of the old definition.
8 #define _UCRT_NEGATIVE_NAN 1
10 #include "mozilla/Printf.h"
18 #if defined(__clang__)
19 # pragma clang diagnostic push
20 # pragma clang diagnostic ignored "-Wc++11-narrowing"
21 #elif defined(__GNUC__)
22 # pragma GCC diagnostic push
23 # pragma GCC diagnostic ignored "-Wnarrowing"
26 #include "glibc_printf_tests/tiformat.c"
29 #include "glibc_printf_tests/tllformat.c"
31 #if defined(__clang__)
32 # pragma clang diagnostic pop
33 #elif defined(__GNUC__)
34 # pragma GCC diagnostic pop
37 #include "glibc_printf_tests/tfformat.c"
40 // A simple implementation of PrintfTarget, just for testing
41 // PrintfTarget::print.
42 class TestPrintfTarget
: public mozilla::PrintfTarget
{
44 static const char* test_string
;
46 TestPrintfTarget() : mOut(0) { memset(mBuffer
, '\0', sizeof(mBuffer
)); }
49 MOZ_RELEASE_ASSERT(mOut
== strlen(test_string
));
50 MOZ_RELEASE_ASSERT(strncmp(mBuffer
, test_string
, strlen(test_string
)) == 0);
53 bool append(const char* sp
, size_t len
) override
{
54 if (mOut
+ len
< sizeof(mBuffer
)) {
55 memcpy(&mBuffer
[mOut
], sp
, len
);
66 const char* TestPrintfTarget::test_string
= "test string";
68 static void TestPrintfTargetPrint() {
69 TestPrintfTarget checker
;
70 checker
.print("test string");
73 // As of clang 14, __attribute__((printf)) doesn't allow %n on Android targets,
74 // which is used in this test.
77 MOZ_FORMAT_PRINTF(5, 6)
79 check_print(const char* file
, int line
,
80 bool (*cmp
)(const char* a
, const char* b
), const char* expect
,
81 const char* fmt
, ...) {
85 mozilla::SmprintfPointer output
= mozilla::Vsmprintf(fmt
, ap
);
88 bool ret
= output
&& cmp(output
.get(), expect
);
89 if (!ret
&& strcmp(expect
, "ignore") != 0) {
90 fprintf(stderr
, "(actual) \"%s\" != (expected) \"%s\" (%s:%d)\n",
91 output
.get() ? output
.get() : "null", expect
, file
, line
);
96 bool str_match(const char* a
, const char* b
) { return !strcmp(a
, b
); }
98 bool approx_match(const char* a
, const char* b
) {
99 return tfformat::matches(const_cast<char*>(a
), b
);
102 #define print_one(...) check_print(__FILE__, __LINE__, str_match, __VA_ARGS__)
104 static const char* zero() { return nullptr; }
106 static void TestPrintfFormats() {
107 MOZ_RELEASE_ASSERT(print_one("0", "%d", 0));
108 MOZ_RELEASE_ASSERT(print_one("23", "%d", 23));
109 MOZ_RELEASE_ASSERT(print_one("+23", "%+d", 23));
110 MOZ_RELEASE_ASSERT(print_one("-23", "%+d", -23));
111 MOZ_RELEASE_ASSERT(print_one("0023", "%04d", 23));
112 MOZ_RELEASE_ASSERT(print_one("777777", "%04d", 777777));
113 MOZ_RELEASE_ASSERT(print_one(" 23", "% 4d", 23));
114 MOZ_RELEASE_ASSERT(print_one("23 ", "%-4d", 23));
115 MOZ_RELEASE_ASSERT(print_one(" 23", "%*d", 4, 23));
116 MOZ_RELEASE_ASSERT(print_one("-23 ", "%*d", -7, -23));
117 MOZ_RELEASE_ASSERT(print_one(" 077", "%5.3d", 77));
118 MOZ_RELEASE_ASSERT(print_one(" 077", "%5.*d", 3, 77));
119 MOZ_RELEASE_ASSERT(print_one(" 077", "%*.*d", 5, 3, 77));
120 MOZ_RELEASE_ASSERT(print_one("077 ", "%*.*d", -5, 3, 77));
121 MOZ_RELEASE_ASSERT(print_one("77 ", "%*.*d", -5, -3, 77));
122 MOZ_RELEASE_ASSERT(print_one("-1", "%d", -1));
123 MOZ_RELEASE_ASSERT(print_one("23", "%u", 23u));
124 MOZ_RELEASE_ASSERT(print_one("0x17", "0x%x", 23u));
125 MOZ_RELEASE_ASSERT(print_one("0xFF", "0x%X", 255u));
126 MOZ_RELEASE_ASSERT(print_one("027", "0%o", 23u));
127 MOZ_RELEASE_ASSERT(print_one("-1", "%hd", (short)-1));
128 // A funny special case.
129 MOZ_RELEASE_ASSERT(print_one("", "%.*d", 0, 0));
130 // This could be expanded if need be, it's just convenient to do
132 if (sizeof(short) == 2) {
133 MOZ_RELEASE_ASSERT(print_one("8000", "%hx", (unsigned short)0x8000));
135 MOZ_RELEASE_ASSERT(print_one("2305", "%ld", 2305l));
136 MOZ_RELEASE_ASSERT(print_one("-2305", "%ld", -2305l));
137 MOZ_RELEASE_ASSERT(print_one("0xf0f0", "0x%lx", 0xf0f0ul
));
138 MOZ_RELEASE_ASSERT(print_one("0", "%lld", 0ll));
139 MOZ_RELEASE_ASSERT(print_one("2305", "%lld", 2305ll));
140 MOZ_RELEASE_ASSERT(print_one("-2305", "%lld", -2305ll));
141 // A funny special case.
142 MOZ_RELEASE_ASSERT(print_one("", "%.*lld", 0, 0ll));
143 MOZ_RELEASE_ASSERT(print_one("0xF0F0", "0x%llX", 0xf0f0ull
));
144 MOZ_RELEASE_ASSERT(print_one("27270", "%zu", (size_t)27270));
145 MOZ_RELEASE_ASSERT(print_one("27270", "%tu", (ptrdiff_t)27270));
146 MOZ_RELEASE_ASSERT(print_one("27270", "%ju", (intmax_t)27270));
147 MOZ_RELEASE_ASSERT(print_one("hello", "he%so", "ll"));
148 MOZ_RELEASE_ASSERT(print_one("hello ", "%-8s", "hello"));
149 MOZ_RELEASE_ASSERT(print_one(" hello", "%8s", "hello"));
150 MOZ_RELEASE_ASSERT(print_one("hello ", "%*s", -8, "hello"));
151 MOZ_RELEASE_ASSERT(print_one("hello", "%.*s", 5, "hello there"));
152 MOZ_RELEASE_ASSERT(print_one("", "%.*s", 0, "hello there"));
153 MOZ_RELEASE_ASSERT(print_one("%%", "%%%%"));
154 MOZ_RELEASE_ASSERT(print_one("0", "%p", (char*)0));
155 MOZ_RELEASE_ASSERT(print_one("h", "%c", 'h'));
156 MOZ_RELEASE_ASSERT(print_one("1.500000", "%f", 1.5f
));
157 MOZ_RELEASE_ASSERT(print_one("1.5", "%g", 1.5));
158 MOZ_RELEASE_ASSERT(print_one("1.50000", "%.5f", 1.5));
160 MOZ_RELEASE_ASSERT(print_one("z ", "%-7s", "z"));
161 MOZ_RELEASE_ASSERT(print_one("z ", "%*s", -7, "z"));
162 MOZ_RELEASE_ASSERT(print_one("hello", "%*s", -3, "hello"));
164 MOZ_RELEASE_ASSERT(print_one(" q", "%3c", 'q'));
165 MOZ_RELEASE_ASSERT(print_one("q ", "%-3c", 'q'));
166 MOZ_RELEASE_ASSERT(print_one(" q", "%*c", 3, 'q'));
167 MOZ_RELEASE_ASSERT(print_one("q ", "%*c", -3, 'q'));
169 // Regression test for bug#1350097. The bug was an assertion
170 // failure caused by printing a very long floating point value.
171 print_one("ignore", "%lf", DBL_MAX
);
173 // Regression test for bug#1517433. The bug was an assertion
174 // failure caused by printing a floating point value with a large
175 // precision and/or width.
176 print_one("ignore", "%500.500lf", DBL_MAX
);
178 MOZ_RELEASE_ASSERT(print_one("2727", "%" PRIu32
, (uint32_t)2727));
179 MOZ_RELEASE_ASSERT(print_one("aa7", "%" PRIx32
, (uint32_t)2727));
180 MOZ_RELEASE_ASSERT(print_one("2727", "%" PRIu64
, (uint64_t)2727));
181 MOZ_RELEASE_ASSERT(print_one("aa7", "%" PRIx64
, (uint64_t)2727));
184 MOZ_RELEASE_ASSERT(print_one(" hi ", "%n hi %n", &n1
, &n2
));
185 MOZ_RELEASE_ASSERT(n1
== 0);
186 MOZ_RELEASE_ASSERT(n2
== 4);
188 MOZ_RELEASE_ASSERT(print_one("23 % 24", "%2$ld %% %1$d", 24, 23l));
190 print_one("7 8 9 10", "%4$lld %3$ld %2$d %1$hd", (short)10, 9, 8l, 7ll));
192 MOZ_RELEASE_ASSERT(print_one("0 ", "%2$p %1$n", &n1
, zero()));
193 MOZ_RELEASE_ASSERT(n1
== 2);
195 MOZ_RELEASE_ASSERT(print_one("23 % 024", "%2$-3ld%%%1$4.3d", 24, 23l));
196 MOZ_RELEASE_ASSERT(print_one("23 1.5", "%2$d %1$g", 1.5, 23));
198 print_one("ff number FF", "%3$llx %1$s %2$lX", "number", 255ul, 255ull));
200 print_one("7799 9977", "%2$zu %1$zu", (size_t)9977, (size_t)7799));
203 template <typename T
, size_t N
>
204 static void TestGlibcPrintf(T (&test_cases
)[N
], const char* file
,
205 bool (*cmp
)(const char* a
, const char* b
)) {
208 for (auto& line
: test_cases
) {
209 // mozilla::PrintfTarget doesn't support the `#` flag character or the
210 // `a` conversion specifier.
211 if (!line
.line
|| strchr(line
.format_string
, '#') ||
212 strchr(line
.format_string
, 'a')) {
216 // Derive the format string in the test case to add "2$" in the specifier
217 // (transforming e.g. "%f" into "%2$f"), and append "%1$.0d".
218 // The former will make the format string take the `line.value` as the
219 // second argument, and the latter will make the first argument formatted
220 // with no precision. We'll pass 0 as the first argument, such that the
221 // formatted value for it is "", which means the expected result string
222 // is still the same.
223 MOZ_RELEASE_ASSERT(sizeof(fmt2
) > strlen(line
.format_string
) + 8);
224 const char* percent
= strchr(line
.format_string
, '%');
225 MOZ_RELEASE_ASSERT(percent
);
226 size_t percent_off
= percent
- line
.format_string
;
227 memcpy(fmt2
, line
.format_string
, percent_off
+ 1);
228 memcpy(fmt2
+ percent_off
+ 1, "2$", 2);
229 strcpy(fmt2
+ percent_off
+ 3, percent
+ 1);
230 strcat(fmt2
, "%1$.0d");
233 const char* res
= line
.result
;
234 const char* fmt
= line
.format_string
;
235 if (strchr(line
.format_string
, 'I')) {
236 ok
= check_print(file
, l
, cmp
, res
, fmt
, (size_t)line
.value
) && ok
;
237 ok
= check_print(file
, l
, cmp
, res
, fmt2
, 0, (size_t)line
.value
) && ok
;
239 ok
= check_print(file
, l
, cmp
, res
, fmt
, line
.value
) && ok
;
240 ok
= check_print(file
, l
, cmp
, res
, fmt2
, 0, line
.value
) && ok
;
243 MOZ_RELEASE_ASSERT(ok
);
250 #endif // defined(XP_WIN)
253 TestPrintfTargetPrint();
254 TestGlibcPrintf(tiformat::sprint_ints
, "tiformat.c", str_match
);
255 TestGlibcPrintf(tllformat::sprint_ints
, "tllformat.c", str_match
);
256 TestGlibcPrintf(tfformat::sprint_doubles
, "tfformat.c", approx_match
);
258 // %f is actually a not very useful formatting specifier, and if you give
259 // large numbers, it will print... large amounts of characters. Ensure
260 // that it does (which requires a patch to double-conversion).
261 mozilla::SmprintfPointer dbl_max
= mozilla::Smprintf("%f", -DBL_MAX
);
262 MOZ_RELEASE_ASSERT(dbl_max
);
263 // Its length should be 309 digits before the dot, 6 after, plus the dot
264 // and the negative sign.
265 MOZ_RELEASE_ASSERT(strlen(dbl_max
.get()) == 317);