Backed out changeset 8f976ed899d7 (bug 1847231) for causing bc failures on browser_se...
[gecko.git] / mozglue / tests / TestPrintf.cpp
blob5f6f9e2005cb9dc2ff7a7520513e0f93fa283ac1
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 #include "mozilla/Printf.h"
8 #include <cfloat>
9 #include <cmath>
10 #include <inttypes.h>
11 #include <stdarg.h>
12 #include <stddef.h>
14 #if defined(__clang__)
15 # pragma clang diagnostic push
16 # pragma clang diagnostic ignored "-Wc++11-narrowing"
17 #elif defined(__GNUC__)
18 # pragma GCC diagnostic push
19 # pragma GCC diagnostic ignored "-Wnarrowing"
20 #endif
21 namespace tiformat {
22 #include "glibc_printf_tests/tiformat.c"
24 namespace tllformat {
25 #include "glibc_printf_tests/tllformat.c"
27 #if defined(__clang__)
28 # pragma clang diagnostic pop
29 #elif defined(__GNUC__)
30 # pragma GCC diagnostic pop
31 #endif
32 namespace tfformat {
33 #include "glibc_printf_tests/tfformat.c"
36 // A simple implementation of PrintfTarget, just for testing
37 // PrintfTarget::print.
38 class TestPrintfTarget : public mozilla::PrintfTarget {
39 public:
40 static const char* test_string;
42 TestPrintfTarget() : mOut(0) { memset(mBuffer, '\0', sizeof(mBuffer)); }
44 ~TestPrintfTarget() {
45 MOZ_RELEASE_ASSERT(mOut == strlen(test_string));
46 MOZ_RELEASE_ASSERT(strncmp(mBuffer, test_string, strlen(test_string)) == 0);
49 bool append(const char* sp, size_t len) override {
50 if (mOut + len < sizeof(mBuffer)) {
51 memcpy(&mBuffer[mOut], sp, len);
53 mOut += len;
54 return true;
57 private:
58 char mBuffer[100];
59 size_t mOut;
62 const char* TestPrintfTarget::test_string = "test string";
64 static void TestPrintfTargetPrint() {
65 TestPrintfTarget checker;
66 checker.print("test string");
69 // As of clang 14, __attribute__((printf)) doesn't allow %n on Android targets,
70 // which is used in this test.
71 static bool
72 #ifndef __ANDROID__
73 MOZ_FORMAT_PRINTF(5, 6)
74 #endif
75 check_print(const char* file, int line,
76 bool (*cmp)(const char* a, const char* b), const char* expect,
77 const char* fmt, ...) {
78 va_list ap;
80 va_start(ap, fmt);
81 mozilla::SmprintfPointer output = mozilla::Vsmprintf(fmt, ap);
82 va_end(ap);
84 bool ret = output && cmp(output.get(), expect);
85 if (!ret && strcmp(expect, "ignore") != 0) {
86 fprintf(stderr, "(actual) \"%s\" != (expected) \"%s\" (%s:%d)\n",
87 output.get() ? output.get() : "null", expect, file, line);
89 return ret;
92 bool str_match(const char* a, const char* b) { return !strcmp(a, b); }
94 bool approx_match(const char* a, const char* b) {
95 return tfformat::matches(const_cast<char*>(a), b);
98 #define print_one(...) check_print(__FILE__, __LINE__, str_match, __VA_ARGS__)
100 static const char* zero() { return nullptr; }
102 static void TestPrintfFormats() {
103 MOZ_RELEASE_ASSERT(print_one("0", "%d", 0));
104 MOZ_RELEASE_ASSERT(print_one("23", "%d", 23));
105 MOZ_RELEASE_ASSERT(print_one("+23", "%+d", 23));
106 MOZ_RELEASE_ASSERT(print_one("-23", "%+d", -23));
107 MOZ_RELEASE_ASSERT(print_one("0023", "%04d", 23));
108 MOZ_RELEASE_ASSERT(print_one("777777", "%04d", 777777));
109 MOZ_RELEASE_ASSERT(print_one(" 23", "% 4d", 23));
110 MOZ_RELEASE_ASSERT(print_one("23 ", "%-4d", 23));
111 MOZ_RELEASE_ASSERT(print_one(" 23", "%*d", 4, 23));
112 MOZ_RELEASE_ASSERT(print_one("-23 ", "%*d", -7, -23));
113 MOZ_RELEASE_ASSERT(print_one(" 077", "%5.3d", 77));
114 MOZ_RELEASE_ASSERT(print_one(" 077", "%5.*d", 3, 77));
115 MOZ_RELEASE_ASSERT(print_one(" 077", "%*.*d", 5, 3, 77));
116 MOZ_RELEASE_ASSERT(print_one("077 ", "%*.*d", -5, 3, 77));
117 MOZ_RELEASE_ASSERT(print_one("77 ", "%*.*d", -5, -3, 77));
118 MOZ_RELEASE_ASSERT(print_one("-1", "%d", -1));
119 MOZ_RELEASE_ASSERT(print_one("23", "%u", 23u));
120 MOZ_RELEASE_ASSERT(print_one("0x17", "0x%x", 23u));
121 MOZ_RELEASE_ASSERT(print_one("0xFF", "0x%X", 255u));
122 MOZ_RELEASE_ASSERT(print_one("027", "0%o", 23u));
123 MOZ_RELEASE_ASSERT(print_one("-1", "%hd", (short)-1));
124 // A funny special case.
125 MOZ_RELEASE_ASSERT(print_one("", "%.*d", 0, 0));
126 // This could be expanded if need be, it's just convenient to do
127 // it this way.
128 if (sizeof(short) == 2) {
129 MOZ_RELEASE_ASSERT(print_one("8000", "%hx", (unsigned short)0x8000));
131 MOZ_RELEASE_ASSERT(print_one("2305", "%ld", 2305l));
132 MOZ_RELEASE_ASSERT(print_one("-2305", "%ld", -2305l));
133 MOZ_RELEASE_ASSERT(print_one("0xf0f0", "0x%lx", 0xf0f0ul));
134 MOZ_RELEASE_ASSERT(print_one("0", "%lld", 0ll));
135 MOZ_RELEASE_ASSERT(print_one("2305", "%lld", 2305ll));
136 MOZ_RELEASE_ASSERT(print_one("-2305", "%lld", -2305ll));
137 // A funny special case.
138 MOZ_RELEASE_ASSERT(print_one("", "%.*lld", 0, 0ll));
139 MOZ_RELEASE_ASSERT(print_one("0xF0F0", "0x%llX", 0xf0f0ull));
140 MOZ_RELEASE_ASSERT(print_one("27270", "%zu", (size_t)27270));
141 MOZ_RELEASE_ASSERT(print_one("27270", "%tu", (ptrdiff_t)27270));
142 MOZ_RELEASE_ASSERT(print_one("27270", "%ju", (intmax_t)27270));
143 MOZ_RELEASE_ASSERT(print_one("hello", "he%so", "ll"));
144 MOZ_RELEASE_ASSERT(print_one("hello ", "%-8s", "hello"));
145 MOZ_RELEASE_ASSERT(print_one(" hello", "%8s", "hello"));
146 MOZ_RELEASE_ASSERT(print_one("hello ", "%*s", -8, "hello"));
147 MOZ_RELEASE_ASSERT(print_one("hello", "%.*s", 5, "hello there"));
148 MOZ_RELEASE_ASSERT(print_one("", "%.*s", 0, "hello there"));
149 MOZ_RELEASE_ASSERT(print_one("%%", "%%%%"));
150 MOZ_RELEASE_ASSERT(print_one("0", "%p", (char*)0));
151 MOZ_RELEASE_ASSERT(print_one("h", "%c", 'h'));
152 MOZ_RELEASE_ASSERT(print_one("1.500000", "%f", 1.5f));
153 MOZ_RELEASE_ASSERT(print_one("1.5", "%g", 1.5));
154 MOZ_RELEASE_ASSERT(print_one("1.50000", "%.5f", 1.5));
156 MOZ_RELEASE_ASSERT(print_one("z ", "%-7s", "z"));
157 MOZ_RELEASE_ASSERT(print_one("z ", "%*s", -7, "z"));
158 MOZ_RELEASE_ASSERT(print_one("hello", "%*s", -3, "hello"));
160 MOZ_RELEASE_ASSERT(print_one(" q", "%3c", 'q'));
161 MOZ_RELEASE_ASSERT(print_one("q ", "%-3c", 'q'));
162 MOZ_RELEASE_ASSERT(print_one(" q", "%*c", 3, 'q'));
163 MOZ_RELEASE_ASSERT(print_one("q ", "%*c", -3, 'q'));
165 // Regression test for bug#1350097. The bug was an assertion
166 // failure caused by printing a very long floating point value.
167 print_one("ignore", "%lf", DBL_MAX);
169 // Regression test for bug#1517433. The bug was an assertion
170 // failure caused by printing a floating point value with a large
171 // precision and/or width.
172 print_one("ignore", "%500.500lf", DBL_MAX);
174 MOZ_RELEASE_ASSERT(print_one("2727", "%" PRIu32, (uint32_t)2727));
175 MOZ_RELEASE_ASSERT(print_one("aa7", "%" PRIx32, (uint32_t)2727));
176 MOZ_RELEASE_ASSERT(print_one("2727", "%" PRIu64, (uint64_t)2727));
177 MOZ_RELEASE_ASSERT(print_one("aa7", "%" PRIx64, (uint64_t)2727));
179 int n1, n2;
180 MOZ_RELEASE_ASSERT(print_one(" hi ", "%n hi %n", &n1, &n2));
181 MOZ_RELEASE_ASSERT(n1 == 0);
182 MOZ_RELEASE_ASSERT(n2 == 4);
184 MOZ_RELEASE_ASSERT(print_one("23 % 24", "%2$ld %% %1$d", 24, 23l));
185 MOZ_RELEASE_ASSERT(
186 print_one("7 8 9 10", "%4$lld %3$ld %2$d %1$hd", (short)10, 9, 8l, 7ll));
188 MOZ_RELEASE_ASSERT(print_one("0 ", "%2$p %1$n", &n1, zero()));
189 MOZ_RELEASE_ASSERT(n1 == 2);
191 MOZ_RELEASE_ASSERT(print_one("23 % 024", "%2$-3ld%%%1$4.3d", 24, 23l));
192 MOZ_RELEASE_ASSERT(print_one("23 1.5", "%2$d %1$g", 1.5, 23));
193 MOZ_RELEASE_ASSERT(
194 print_one("ff number FF", "%3$llx %1$s %2$lX", "number", 255ul, 255ull));
195 MOZ_RELEASE_ASSERT(
196 print_one("7799 9977", "%2$zu %1$zu", (size_t)9977, (size_t)7799));
199 template <typename T, size_t N>
200 static void TestGlibcPrintf(T (&test_cases)[N], const char* file,
201 bool (*cmp)(const char* a, const char* b)) {
202 bool ok = true;
203 char fmt2[40];
204 for (auto& line : test_cases) {
205 // mozilla::PrintfTarget doesn't support the `#` flag character or the
206 // `a` conversion specifier.
207 if (!line.line || strchr(line.format_string, '#') ||
208 strchr(line.format_string, 'a')) {
209 continue;
212 // Derive the format string in the test case to add "2$" in the specifier
213 // (transforming e.g. "%f" into "%2$f"), and append "%1$.0d".
214 // The former will make the format string take the `line.value` as the
215 // second argument, and the latter will make the first argument formatted
216 // with no precision. We'll pass 0 as the first argument, such that the
217 // formatted value for it is "", which means the expected result string
218 // is still the same.
219 MOZ_RELEASE_ASSERT(sizeof(fmt2) > strlen(line.format_string) + 8);
220 const char* percent = strchr(line.format_string, '%');
221 MOZ_RELEASE_ASSERT(percent);
222 size_t percent_off = percent - line.format_string;
223 memcpy(fmt2, line.format_string, percent_off + 1);
224 memcpy(fmt2 + percent_off + 1, "2$", 2);
225 strcpy(fmt2 + percent_off + 3, percent + 1);
226 strcat(fmt2, "%1$.0d");
228 int l = line.line;
229 const char* res = line.result;
230 const char* fmt = line.format_string;
231 if (strchr(line.format_string, 'I')) {
232 ok = check_print(file, l, cmp, res, fmt, (size_t)line.value) && ok;
233 ok = check_print(file, l, cmp, res, fmt2, 0, (size_t)line.value) && ok;
234 } else {
235 ok = check_print(file, l, cmp, res, fmt, line.value) && ok;
236 ok = check_print(file, l, cmp, res, fmt2, 0, line.value) && ok;
239 MOZ_RELEASE_ASSERT(ok);
242 #if defined(XP_WIN)
243 int wmain()
244 #else
245 int main()
246 #endif // defined(XP_WIN)
248 TestPrintfFormats();
249 TestPrintfTargetPrint();
250 TestGlibcPrintf(tiformat::sprint_ints, "tiformat.c", str_match);
251 TestGlibcPrintf(tllformat::sprint_ints, "tllformat.c", str_match);
252 TestGlibcPrintf(tfformat::sprint_doubles, "tfformat.c", approx_match);
254 // %f is actually a not very useful formatting specifier, and if you give
255 // large numbers, it will print... large amounts of characters. Ensure
256 // that it does (which requires a patch to double-conversion).
257 mozilla::SmprintfPointer dbl_max = mozilla::Smprintf("%f", -DBL_MAX);
258 MOZ_RELEASE_ASSERT(dbl_max);
259 // Its length should be 309 digits before the dot, 6 after, plus the dot
260 // and the negative sign.
261 MOZ_RELEASE_ASSERT(strlen(dbl_max.get()) == 317);
263 return 0;