tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }