TestPrintf.cpp (10209B)
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/. */ 5 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 9 10 #include "mozilla/Printf.h" 11 12 #include <cfloat> 13 #include <cmath> 14 #include <inttypes.h> 15 #include <stdarg.h> 16 #include <stddef.h> 17 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" 24 #endif 25 namespace tiformat { 26 #include "glibc_printf_tests/tiformat.c" 27 } 28 namespace tllformat { 29 #include "glibc_printf_tests/tllformat.c" 30 } 31 #if defined(__clang__) 32 # pragma clang diagnostic pop 33 #elif defined(__GNUC__) 34 # pragma GCC diagnostic pop 35 #endif 36 namespace tfformat { 37 #include "glibc_printf_tests/tfformat.c" 38 } 39 40 // A simple implementation of PrintfTarget, just for testing 41 // PrintfTarget::print. 42 class TestPrintfTarget : public mozilla::PrintfTarget { 43 public: 44 static const char* test_string; 45 46 TestPrintfTarget() : mOut(0) { memset(mBuffer, '\0', sizeof(mBuffer)); } 47 48 ~TestPrintfTarget() { 49 MOZ_RELEASE_ASSERT(mOut == strlen(test_string)); 50 MOZ_RELEASE_ASSERT(strncmp(mBuffer, test_string, strlen(test_string)) == 0); 51 } 52 53 bool append(const char* sp, size_t len) override { 54 if (mOut + len < sizeof(mBuffer)) { 55 memcpy(&mBuffer[mOut], sp, len); 56 } 57 mOut += len; 58 return true; 59 } 60 61 private: 62 char mBuffer[100]; 63 size_t mOut; 64 }; 65 66 const char* TestPrintfTarget::test_string = "test string"; 67 68 static void TestPrintfTargetPrint() { 69 TestPrintfTarget checker; 70 checker.print("test string"); 71 } 72 73 // As of clang 14, __attribute__((printf)) doesn't allow %n on Android targets, 74 // which is used in this test. 75 static bool 76 #ifndef __ANDROID__ 77 MOZ_FORMAT_PRINTF(5, 6) 78 #endif 79 check_print(const char* file, int line, 80 bool (*cmp)(const char* a, const char* b), const char* expect, 81 const char* fmt, ...) { 82 va_list ap; 83 84 va_start(ap, fmt); 85 mozilla::SmprintfPointer output = mozilla::Vsmprintf(fmt, ap); 86 va_end(ap); 87 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); 92 } 93 return ret; 94 } 95 96 bool str_match(const char* a, const char* b) { return !strcmp(a, b); } 97 98 bool approx_match(const char* a, const char* b) { 99 return tfformat::matches(const_cast<char*>(a), b); 100 } 101 102 #define print_one(...) check_print(__FILE__, __LINE__, str_match, __VA_ARGS__) 103 104 static const char* zero() { return nullptr; } 105 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 131 // it this way. 132 if (sizeof(short) == 2) { 133 MOZ_RELEASE_ASSERT(print_one("8000", "%hx", (unsigned short)0x8000)); 134 } 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)); 159 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")); 163 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')); 168 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); 172 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); 177 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)); 182 183 int n1, n2; 184 MOZ_RELEASE_ASSERT(print_one(" hi ", "%n hi %n", &n1, &n2)); 185 MOZ_RELEASE_ASSERT(n1 == 0); 186 MOZ_RELEASE_ASSERT(n2 == 4); 187 188 MOZ_RELEASE_ASSERT(print_one("23 % 24", "%2$ld %% %1$d", 24, 23l)); 189 MOZ_RELEASE_ASSERT( 190 print_one("7 8 9 10", "%4$lld %3$ld %2$d %1$hd", (short)10, 9, 8l, 7ll)); 191 192 MOZ_RELEASE_ASSERT(print_one("0 ", "%2$p %1$n", &n1, zero())); 193 MOZ_RELEASE_ASSERT(n1 == 2); 194 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)); 197 MOZ_RELEASE_ASSERT( 198 print_one("ff number FF", "%3$llx %1$s %2$lX", "number", 255ul, 255ull)); 199 MOZ_RELEASE_ASSERT( 200 print_one("7799 9977", "%2$zu %1$zu", (size_t)9977, (size_t)7799)); 201 } 202 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)) { 206 bool ok = true; 207 char fmt2[40]; 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')) { 213 continue; 214 } 215 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"); 231 232 int l = line.line; 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; 238 } else { 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; 241 } 242 } 243 MOZ_RELEASE_ASSERT(ok); 244 } 245 246 #if defined(XP_WIN) 247 int wmain() 248 #else 249 int main() 250 #endif // defined(XP_WIN) 251 { 252 TestPrintfFormats(); 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); 257 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); 266 267 return 0; 268 }