tor-browser

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

TestFmt.cpp (6518B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "gtest/gtest.h"
      8 #include "fmt/format.h"
      9 #include "fmt/xchar.h"
     10 #include "nsTArray.h"
     11 #include "nsFmtString.h"
     12 #include "nsString.h"
     13 #include "mozilla/Sprintf.h"
     14 
     15 #define snprintf(...)
     16 
     17 namespace tfformat {
     18 #include "../glibc_printf_tests/tfformat.c"
     19 }
     20 
     21 #undef snprintf
     22 
     23 nsCString PrintfToFmtFormat(const char* aPrintfFormatString) {
     24  nsCString fmtFormat;
     25  fmtFormat.AppendASCII("{");
     26  fmtFormat.AppendASCII(aPrintfFormatString);
     27  // {fmt} uses < to left align, while printf traditionally use -.
     28  // The order between the sign forcing ("+") or zero-padding ("0") and the
     29  // alignment ("-" or "<") is also reversed.
     30  fmtFormat.ReplaceSubstring("+-", "<+");
     31  fmtFormat.ReplaceSubstring("0-", "<0");
     32  // {fmt} doesn't support e.g. %4.f to denote 4 digits of integer part, and
     33  // zero digit of fractional part. Simply replace them by the explicit form
     34  // with a 0.
     35  fmtFormat.ReplaceSubstring(".f", ".0f");
     36  fmtFormat.ReplaceSubstring(".F", ".0F");
     37  fmtFormat.ReplaceSubstring(".e", ".0e");
     38  fmtFormat.ReplaceSubstring(".E", ".0E");
     39  fmtFormat.ReplaceSubstring(".G", ".0G");
     40  fmtFormat.ReplaceSubstring(".g", ".0g");
     41  fmtFormat.ReplaceChar('%', ':');
     42  fmtFormat.AppendASCII("}");
     43  return fmtFormat;
     44 }
     45 
     46 TEST(Fmt, CrossCheckPrintf)
     47 {
     48  char bufGeckoPrintf[1024] = {};
     49  // - 1 because the last item is just a zero
     50  for (uint32_t i = 2; i < std::size(tfformat::sprint_doubles) - 1; i++) {
     51    if (strstr(tfformat::sprint_doubles[i].format_string, "#") ||
     52        strstr(tfformat::sprint_doubles[i].format_string, "a")) {
     53      // Gecko's printf doesn't implement the '#' specifier nor 'a' conversion
     54      // specifier (hex notation), but {fmt} does.
     55      // Skip this test-case for the cross-check.
     56      continue;
     57    }
     58    nsCString fmtFormat =
     59        PrintfToFmtFormat(tfformat::sprint_doubles[i].format_string);
     60    nsCString withFmt;
     61    withFmt.AppendVfmt(fmtFormat.get(), fmt::make_format_args(
     62                                            tfformat::sprint_doubles[i].value));
     63    SprintfBuf(bufGeckoPrintf, 1024, tfformat::sprint_doubles[i].format_string,
     64               tfformat::sprint_doubles[i].value);
     65    if (strcmp(bufGeckoPrintf, withFmt.get()) != 0) {
     66      fprintf(stderr, "Conversion index %d: %lf, %s -> %s\n", i,
     67              tfformat::sprint_doubles[i].value,
     68              tfformat::sprint_doubles[i].format_string, fmtFormat.get());
     69      fprintf(stderr, "DIFF: |%s|\n vs.   |%s|\n", bufGeckoPrintf,
     70              withFmt.get());
     71    }
     72    ASSERT_STREQ(bufGeckoPrintf, withFmt.get());
     73  }
     74 }
     75 
     76 TEST(Fmt, Sequences)
     77 {
     78  char bufFmt[1024] = {};
     79  {
     80    nsTArray<int> array(4);
     81    for (uint32_t i = 0; i < 4; i++) {
     82      array.AppendElement(i);
     83    }
     84    auto [out, size] =
     85        fmt::format_to(bufFmt, FMT_STRING("{}"), fmt::join(array, ", "));
     86    *out = 0;
     87    ASSERT_STREQ("0, 1, 2, 3", bufFmt);
     88  }
     89  {
     90    nsTArray<uint8_t> array(4);
     91    for (uint32_t i = 0; i < 4; i++) {
     92      array.AppendElement((123 * 5 * (i + 1)) % 255);
     93    }
     94    auto [out, size] =
     95        fmt::format_to(bufFmt, FMT_STRING("{:#04x}"), fmt::join(array, ", "));
     96    *out = 0;
     97    ASSERT_STREQ("0x69, 0xd2, 0x3c, 0xa5", bufFmt);
     98  }
     99 }
    100 
    101 struct POD {
    102  double mA;
    103  uint64_t mB;
    104 };
    105 
    106 struct POD2 {
    107  double mA;
    108  uint64_t mB;
    109 };
    110 
    111 template <>
    112 class fmt::formatter<POD> : public formatter<string_view> {
    113 public:
    114  auto format(const POD& aPod, format_context& aCtx) const {
    115    std::string temp;
    116    format_to(std::back_inserter(temp), FMT_STRING("POD: mA: {}, mB: {}"),
    117              aPod.mA, aPod.mB);
    118    return formatter<string_view>::format(temp, aCtx);
    119  }
    120 };
    121 
    122 auto format_as(POD2 aInstance) -> std::string {
    123  return fmt::format(FMT_STRING("POD2: mA: {}, mB: {}"), aInstance.mA,
    124                     aInstance.mB);
    125 }
    126 
    127 TEST(Fmt, PodPrint)
    128 {
    129  char bufFmt[1024] = {};
    130 
    131  POD p{4.33, 8};
    132  POD2 p2{4.33, 8};
    133  {
    134    auto [out, size] = fmt::format_to(bufFmt, FMT_STRING("{}"), p);
    135    *out = 0;
    136    ASSERT_STREQ("POD: mA: 4.33, mB: 8", bufFmt);
    137  }
    138 
    139  {
    140    auto [out, size] = fmt::format_to(bufFmt, FMT_STRING("{:>30}"), p);
    141    *out = 0;
    142    ASSERT_STREQ("          POD: mA: 4.33, mB: 8", bufFmt);
    143  }
    144 
    145  {
    146    auto [out, size] = fmt::format_to(bufFmt, FMT_STRING("{:>30}"), p2);
    147    *out = 0;
    148    ASSERT_STREQ("         POD2: mA: 4.33, mB: 8", bufFmt);
    149  }
    150 }
    151 
    152 TEST(Fmt, nsString)
    153 {
    154  {
    155    nsFmtCString str(FMT_STRING("{} {} {}"), 4, 4.3, " end");
    156    ASSERT_STREQ("4 4.3  end", str.get());
    157  }
    158  {
    159    nsFmtString str(FMT_STRING(u"Étonnant {} {} {}"), u"Étienne", 4, 4.3);
    160    ASSERT_STREQ("Étonnant Étienne 4 4.3", NS_ConvertUTF16toUTF8(str).get());
    161  }
    162  {
    163    nsString str;
    164    str.AppendFmt(FMT_STRING(u"Étonnant {} {} {}"), u"Étienne", 4, 4.3);
    165    ASSERT_STREQ("Étonnant Étienne 4 4.3", NS_ConvertUTF16toUTF8(str).get());
    166  }
    167  {
    168    nsCString str;
    169    str.AppendFmt(FMT_STRING("{} {} {}"), 4, 4.3, " end");
    170    ASSERT_STREQ("4 4.3  end", str.get());
    171  }
    172 }
    173 
    174 TEST(Fmt, Truncation)
    175 {
    176  char too_short_buf[16];
    177  const char* too_long_buf = "asdasdlkasjdashdkajhsdkhaksdjhasd";
    178  {
    179    auto [out, truncated] =
    180        fmt::format_to(too_short_buf, FMT_STRING("{}"), too_long_buf);
    181    assert(truncated);
    182    // Overwrite the last char for printing
    183    too_short_buf[15] = 0;
    184    fmt::print(FMT_STRING("{} {} {}\n"), too_short_buf, fmt::ptr(out),
    185               truncated);
    186  }
    187  {
    188    auto [out, size] = fmt::format_to_n(too_short_buf, sizeof(too_short_buf),
    189                                        FMT_STRING("{}"), too_long_buf);
    190    assert(size > sizeof(too_short_buf));
    191    too_short_buf[15] = 0;
    192    fmt::print(FMT_STRING("{} {} {}\n"), too_short_buf, fmt::ptr(out), size);
    193  }
    194 }
    195 
    196 TEST(Fmt, NullString)
    197 {
    198  char str[16];
    199  auto [out, size] = fmt::format_to(str, FMT_STRING("{}"), (char*)nullptr);
    200  *out = 0;
    201  ASSERT_STREQ("(null)", str);
    202 }
    203 
    204 // ASAN intercepts the underlying fwrite and crashes.
    205 #if defined(__has_feature)
    206 #  if not(__has_feature(address_sanitizer))
    207 TEST(Fmt, IOError)
    208 {
    209  FILE* duped = fdopen(dup(fileno(stderr)), "w");
    210  fclose(duped);
    211  // glibc will crash here on x86 Linux debug
    212 #    if defined(DEBUG) && defined(XP_LINUX) && !defined(__i386__)
    213  fmt::println(duped, FMT_STRING("Hi {}"), 14);
    214 #    endif
    215 }
    216 #  endif
    217 #endif