about summary refs log blame commit diff
path: root/absl/strings/internal/str_format/convert_test.cc
blob: 0e8535c27b7a5b298039df6b90a5d11096cc3020 (plain) (tree)
1
2
3
4
5
6
7
8
9


                   
 
                 
                
                 
                 
                            
 
                        
                        
                                           
                                                  
                                

                
                    























                                                                        


                                                    

















                                                           
                                                                   







































                                                                 






                                                           



                                               
                                  


















































                                                               
                                  




                                                            












                                                                              
                                    









                                   


                                      



                                                                    
















































                                                                          



















                                                                       
                                           

































































































































                                                                               










                                                                           



































                                                                               

























                                                                          
               





                                                                      

                                                                            















                                                                            

















                                                                               









































                                                                               





                                                 






                                                                       



                                                                     
















                                                                                




       








































































































                                                                                                                                            
























                                                                                
                                       





                                                                         

                                                                 


                                      






                                                     















                                                                               





                                                                                





                                                       






                                                                       





                                                                               
                                                                              
                                                             
                                                                    










































































                                                                          
                  
                    
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>

#include <cctype>
#include <cmath>
#include <limits>
#include <string>
#include <thread>  // NOLINT

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/strings/internal/str_format/bind.h"
#include "absl/types/optional.h"

namespace absl {
ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
namespace {

template <typename T, size_t N>
size_t ArraySize(T (&)[N]) {
  return N;
}

std::string LengthModFor(float) { return ""; }
std::string LengthModFor(double) { return ""; }
std::string LengthModFor(long double) { return "L"; }
std::string LengthModFor(char) { return "hh"; }
std::string LengthModFor(signed char) { return "hh"; }
std::string LengthModFor(unsigned char) { return "hh"; }
std::string LengthModFor(short) { return "h"; }           // NOLINT
std::string LengthModFor(unsigned short) { return "h"; }  // NOLINT
std::string LengthModFor(int) { return ""; }
std::string LengthModFor(unsigned) { return ""; }
std::string LengthModFor(long) { return "l"; }                 // NOLINT
std::string LengthModFor(unsigned long) { return "l"; }        // NOLINT
std::string LengthModFor(long long) { return "ll"; }           // NOLINT
std::string LengthModFor(unsigned long long) { return "ll"; }  // NOLINT

std::string EscCharImpl(int v) {
  if (std::isprint(static_cast<unsigned char>(v))) {
    return std::string(1, static_cast<char>(v));
  }
  char buf[64];
  int n = snprintf(buf, sizeof(buf), "\\%#.2x",
                   static_cast<unsigned>(v & 0xff));
  assert(n > 0 && n < sizeof(buf));
  return std::string(buf, n);
}

std::string Esc(char v) { return EscCharImpl(v); }
std::string Esc(signed char v) { return EscCharImpl(v); }
std::string Esc(unsigned char v) { return EscCharImpl(v); }

template <typename T>
std::string Esc(const T &v) {
  std::ostringstream oss;
  oss << v;
  return oss.str();
}

void StrAppendV(std::string *dst, const char *format, va_list ap) {
  // First try with a small fixed size buffer
  static const int kSpaceLength = 1024;
  char space[kSpaceLength];

  // It's possible for methods that use a va_list to invalidate
  // the data in it upon use.  The fix is to make a copy
  // of the structure before using it and use that copy instead.
  va_list backup_ap;
  va_copy(backup_ap, ap);
  int result = vsnprintf(space, kSpaceLength, format, backup_ap);
  va_end(backup_ap);
  if (result < kSpaceLength) {
    if (result >= 0) {
      // Normal case -- everything fit.
      dst->append(space, result);
      return;
    }
    if (result < 0) {
      // Just an error.
      return;
    }
  }

  // Increase the buffer size to the size requested by vsnprintf,
  // plus one for the closing \0.
  int length = result + 1;
  char *buf = new char[length];

  // Restore the va_list before we use it again
  va_copy(backup_ap, ap);
  result = vsnprintf(buf, length, format, backup_ap);
  va_end(backup_ap);

  if (result >= 0 && result < length) {
    // It fit
    dst->append(buf, result);
  }
  delete[] buf;
}

void StrAppend(std::string *out, const char *format, ...) {
  va_list ap;
  va_start(ap, format);
  StrAppendV(out, format, ap);
  va_end(ap);
}

std::string StrPrint(const char *format, ...) {
  va_list ap;
  va_start(ap, format);
  std::string result;
  StrAppendV(&result, format, ap);
  va_end(ap);
  return result;
}

class FormatConvertTest : public ::testing::Test { };

template <typename T>
void TestStringConvert(const T& str) {
  const FormatArgImpl args[] = {FormatArgImpl(str)};
  struct Expectation {
    const char *out;
    const char *fmt;
  };
  const Expectation kExpect[] = {
    {"hello",  "%1$s"      },
    {"",       "%1$.s"     },
    {"",       "%1$.0s"    },
    {"h",      "%1$.1s"    },
    {"he",     "%1$.2s"    },
    {"hello",  "%1$.10s"   },
    {" hello", "%1$6s"     },
    {"   he",  "%1$5.2s"   },
    {"he   ",  "%1$-5.2s"  },
    {"hello ", "%1$-6.10s" },
  };
  for (const Expectation &e : kExpect) {
    UntypedFormatSpecImpl format(e.fmt);
    EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args)));
  }
}

TEST_F(FormatConvertTest, BasicString) {
  TestStringConvert("hello");  // As char array.
  TestStringConvert(static_cast<const char*>("hello"));
  TestStringConvert(std::string("hello"));
  TestStringConvert(string_view("hello"));
}

TEST_F(FormatConvertTest, NullString) {
  const char* p = nullptr;
  UntypedFormatSpecImpl format("%s");
  EXPECT_EQ("", FormatPack(format, {FormatArgImpl(p)}));
}

TEST_F(FormatConvertTest, StringPrecision) {
  // We cap at the precision.
  char c = 'a';
  const char* p = &c;
  UntypedFormatSpecImpl format("%.1s");
  EXPECT_EQ("a", FormatPack(format, {FormatArgImpl(p)}));

  // We cap at the NUL-terminator.
  p = "ABC";
  UntypedFormatSpecImpl format2("%.10s");
  EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)}));
}

// Pointer formatting is implementation defined. This checks that the argument
// can be matched to `ptr`.
MATCHER_P(MatchesPointerString, ptr, "") {
  if (ptr == nullptr && arg == "(nil)") {
    return true;
  }
  void* parsed = nullptr;
  if (sscanf(arg.c_str(), "%p", &parsed) != 1) {
    ABSL_RAW_LOG(FATAL, "Could not parse %s", arg.c_str());
  }
  return ptr == parsed;
}

TEST_F(FormatConvertTest, Pointer) {
  static int x = 0;
  const int *xp = &x;
  char c = 'h';
  char *mcp = &c;
  const char *cp = "hi";
  const char *cnil = nullptr;
  const int *inil = nullptr;
  using VoidF = void (*)();
  VoidF fp = [] {}, fnil = nullptr;
  volatile char vc;
  volatile char *vcp = &vc;
  volatile char *vcnil = nullptr;
  const FormatArgImpl args_array[] = {
      FormatArgImpl(xp),   FormatArgImpl(cp),  FormatArgImpl(inil),
      FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp),
      FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil),
  };
  auto args = absl::MakeConstSpan(args_array);

  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%p"), args),
              MatchesPointerString(&x));
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%20p"), args),
              MatchesPointerString(&x));
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.1p"), args),
              MatchesPointerString(&x));
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args),
              MatchesPointerString(&x));
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%30.20p"), args),
              MatchesPointerString(&x));

  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-p"), args),
              MatchesPointerString(&x));
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-20p"), args),
              MatchesPointerString(&x));
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-.1p"), args),
              MatchesPointerString(&x));
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args),
              MatchesPointerString(&x));
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args),
              MatchesPointerString(&x));

  // const char*
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args),
              MatchesPointerString(cp));
  // null const int*
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args),
              MatchesPointerString(nullptr));
  // null const char*
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args),
              MatchesPointerString(nullptr));
  // nonconst char*
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args),
              MatchesPointerString(mcp));

  // function pointers
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args),
              MatchesPointerString(reinterpret_cast<const void*>(fp)));
  EXPECT_THAT(
      FormatPack(UntypedFormatSpecImpl("%8$p"), args),
      MatchesPointerString(reinterpret_cast<volatile const void *>(vcp)));

  // null function pointers
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args),
              MatchesPointerString(nullptr));
  EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args),
              MatchesPointerString(nullptr));
}

struct Cardinal {
  enum Pos { k1 = 1, k2 = 2, k3 = 3 };
  enum Neg { kM1 = -1, kM2 = -2, kM3 = -3 };
};

TEST_F(FormatConvertTest, Enum) {
  const Cardinal::Pos k3 = Cardinal::k3;
  const Cardinal::Neg km3 = Cardinal::kM3;
  const FormatArgImpl args[] = {FormatArgImpl(k3), FormatArgImpl(km3)};
  UntypedFormatSpecImpl format("%1$d");
  UntypedFormatSpecImpl format2("%2$d");
  EXPECT_EQ("3", FormatPack(format, absl::MakeSpan(args)));
  EXPECT_EQ("-3", FormatPack(format2, absl::MakeSpan(args)));
}

template <typename T>
class TypedFormatConvertTest : public FormatConvertTest { };

TYPED_TEST_SUITE_P(TypedFormatConvertTest);

std::vector<std::string> AllFlagCombinations() {
  const char kFlags[] = {'-', '#', '0', '+', ' '};
  std::vector<std::string> result;
  for (size_t fsi = 0; fsi < (1ull << ArraySize(kFlags)); ++fsi) {
    std::string flag_set;
    for (size_t fi = 0; fi < ArraySize(kFlags); ++fi)
      if (fsi & (1ull << fi))
        flag_set += kFlags[fi];
    result.push_back(flag_set);
  }
  return result;
}

TYPED_TEST_P(TypedFormatConvertTest, AllIntsWithFlags) {
  typedef TypeParam T;
  typedef typename std::make_unsigned<T>::type UnsignedT;
  using remove_volatile_t = typename std::remove_volatile<T>::type;
  const T kMin = std::numeric_limits<remove_volatile_t>::min();
  const T kMax = std::numeric_limits<remove_volatile_t>::max();
  const T kVals[] = {
      remove_volatile_t(1),
      remove_volatile_t(2),
      remove_volatile_t(3),
      remove_volatile_t(123),
      remove_volatile_t(-1),
      remove_volatile_t(-2),
      remove_volatile_t(-3),
      remove_volatile_t(-123),
      remove_volatile_t(0),
      kMax - remove_volatile_t(1),
      kMax,
      kMin + remove_volatile_t(1),
      kMin,
  };
  const char kConvChars[] = {'d', 'i', 'u', 'o', 'x', 'X'};
  const std::string kWid[] = {"", "4", "10"};
  const std::string kPrec[] = {"", ".", ".0", ".4", ".10"};

  const std::vector<std::string> flag_sets = AllFlagCombinations();

  for (size_t vi = 0; vi < ArraySize(kVals); ++vi) {
    const T val = kVals[vi];
    SCOPED_TRACE(Esc(val));
    const FormatArgImpl args[] = {FormatArgImpl(val)};
    for (size_t ci = 0; ci < ArraySize(kConvChars); ++ci) {
      const char conv_char = kConvChars[ci];
      for (size_t fsi = 0; fsi < flag_sets.size(); ++fsi) {
        const std::string &flag_set = flag_sets[fsi];
        for (size_t wi = 0; wi < ArraySize(kWid); ++wi) {
          const std::string &wid = kWid[wi];
          for (size_t pi = 0; pi < ArraySize(kPrec); ++pi) {
            const std::string &prec = kPrec[pi];

            const bool is_signed_conv = (conv_char == 'd' || conv_char == 'i');
            const bool is_unsigned_to_signed =
                !std::is_signed<T>::value && is_signed_conv;
            // Don't consider sign-related flags '+' and ' ' when doing
            // unsigned to signed conversions.
            if (is_unsigned_to_signed &&
                flag_set.find_first_of("+ ") != std::string::npos) {
              continue;
            }

            std::string new_fmt("%");
            new_fmt += flag_set;
            new_fmt += wid;
            new_fmt += prec;
            // old and new always agree up to here.
            std::string old_fmt = new_fmt;
            new_fmt += conv_char;
            std::string old_result;
            if (is_unsigned_to_signed) {
              // don't expect agreement on unsigned formatted as signed,
              // as printf can't do that conversion properly. For those
              // cases, we do expect agreement with printf with a "%u"
              // and the unsigned equivalent of 'val'.
              UnsignedT uval = val;
              old_fmt += LengthModFor(uval);
              old_fmt += "u";
              old_result = StrPrint(old_fmt.c_str(), uval);
            } else {
              old_fmt += LengthModFor(val);
              old_fmt += conv_char;
              old_result = StrPrint(old_fmt.c_str(), val);
            }

            SCOPED_TRACE(std::string() + " old_fmt: \"" + old_fmt +
                         "\"'"
                         " new_fmt: \"" +
                         new_fmt + "\"");
            UntypedFormatSpecImpl format(new_fmt);
            EXPECT_EQ(old_result, FormatPack(format, absl::MakeSpan(args)));
          }
        }
      }
    }
  }
}

TYPED_TEST_P(TypedFormatConvertTest, Char) {
  typedef TypeParam T;
  using remove_volatile_t = typename std::remove_volatile<T>::type;
  static const T kMin = std::numeric_limits<remove_volatile_t>::min();
  static const T kMax = std::numeric_limits<remove_volatile_t>::max();
  T kVals[] = {
    remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10),
    remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10),
    remove_volatile_t(0),
    kMin + remove_volatile_t(1), kMin,
    kMax - remove_volatile_t(1), kMax
  };
  for (const T &c : kVals) {
    const FormatArgImpl args[] = {FormatArgImpl(c)};
    UntypedFormatSpecImpl format("%c");
    EXPECT_EQ(StrPrint("%c", c), FormatPack(format, absl::MakeSpan(args)));
  }
}

REGISTER_TYPED_TEST_CASE_P(TypedFormatConvertTest, AllIntsWithFlags, Char);

typedef ::testing::Types<
    int, unsigned, volatile int,
    short, unsigned short,
    long, unsigned long,
    long long, unsigned long long,
    signed char, unsigned char, char>
    AllIntTypes;
INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes,
                              TypedFormatConvertTest, AllIntTypes);
TEST_F(FormatConvertTest, VectorBool) {
  // Make sure vector<bool>'s values behave as bools.
  std::vector<bool> v = {true, false};
  const std::vector<bool> cv = {true, false};
  EXPECT_EQ("1,0,1,0",
            FormatPack(UntypedFormatSpecImpl("%d,%d,%d,%d"),
                       absl::Span<const FormatArgImpl>(
                           {FormatArgImpl(v[0]), FormatArgImpl(v[1]),
                            FormatArgImpl(cv[0]), FormatArgImpl(cv[1])})));
}


TEST_F(FormatConvertTest, Int128) {
  absl::int128 positive = static_cast<absl::int128>(0x1234567890abcdef) * 1979;
  absl::int128 negative = -positive;
  absl::int128 max = absl::Int128Max(), min = absl::Int128Min();
  const FormatArgImpl args[] = {FormatArgImpl(positive),
                                FormatArgImpl(negative), FormatArgImpl(max),
                                FormatArgImpl(min)};

  struct Case {
    const char* format;
    const char* expected;
  } cases[] = {
      {"%1$d", "2595989796776606496405"},
      {"%1$30d", "        2595989796776606496405"},
      {"%1$-30d", "2595989796776606496405        "},
      {"%1$u", "2595989796776606496405"},
      {"%1$x", "8cba9876066020f695"},
      {"%2$d", "-2595989796776606496405"},
      {"%2$30d", "       -2595989796776606496405"},
      {"%2$-30d", "-2595989796776606496405       "},
      {"%2$u", "340282366920938460867384810655161715051"},
      {"%2$x", "ffffffffffffff73456789f99fdf096b"},
      {"%3$d", "170141183460469231731687303715884105727"},
      {"%3$u", "170141183460469231731687303715884105727"},
      {"%3$x", "7fffffffffffffffffffffffffffffff"},
      {"%4$d", "-170141183460469231731687303715884105728"},
      {"%4$x", "80000000000000000000000000000000"},
  };

  for (auto c : cases) {
    UntypedFormatSpecImpl format(c.format);
    EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args)));
  }
}

TEST_F(FormatConvertTest, Uint128) {
  absl::uint128 v = static_cast<absl::uint128>(0x1234567890abcdef) * 1979;
  absl::uint128 max = absl::Uint128Max();
  const FormatArgImpl args[] = {FormatArgImpl(v), FormatArgImpl(max)};

  struct Case {
    const char* format;
    const char* expected;
  } cases[] = {
      {"%1$d", "2595989796776606496405"},
      {"%1$30d", "        2595989796776606496405"},
      {"%1$-30d", "2595989796776606496405        "},
      {"%1$u", "2595989796776606496405"},
      {"%1$x", "8cba9876066020f695"},
      {"%2$d", "340282366920938463463374607431768211455"},
      {"%2$u", "340282366920938463463374607431768211455"},
      {"%2$x", "ffffffffffffffffffffffffffffffff"},
  };

  for (auto c : cases) {
    UntypedFormatSpecImpl format(c.format);
    EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args)));
  }
}

TEST_F(FormatConvertTest, Float) {
#ifdef _MSC_VER
  // MSVC has a different rounding policy than us so we can't test our
  // implementation against the native one there.
  return;
#endif  // _MSC_VER

  const char *const kFormats[] = {
      "%",  "%.3", "%8.5", "%500",   "%.5000", "%.60", "%.30",   "%03",
      "%+", "% ",  "%-10", "%#15.3", "%#.0",   "%.0",  "%1$*2$", "%1$.*2$"};

  std::vector<double> doubles = {0.0,
                                 -0.0,
                                 .99999999999999,
                                 99999999999999.,
                                 std::numeric_limits<double>::max(),
                                 -std::numeric_limits<double>::max(),
                                 std::numeric_limits<double>::min(),
                                 -std::numeric_limits<double>::min(),
                                 std::numeric_limits<double>::lowest(),
                                 -std::numeric_limits<double>::lowest(),
                                 std::numeric_limits<double>::epsilon(),
                                 std::numeric_limits<double>::epsilon() + 1,
                                 std::numeric_limits<double>::infinity(),
                                 -std::numeric_limits<double>::infinity()};

  // Some regression tests.
  doubles.push_back(0.99999999999999989);

  if (std::numeric_limits<double>::has_denorm != std::denorm_absent) {
    doubles.push_back(std::numeric_limits<double>::denorm_min());
    doubles.push_back(-std::numeric_limits<double>::denorm_min());
  }

  for (double base :
       {1., 12., 123., 1234., 12345., 123456., 1234567., 12345678., 123456789.,
        1234567890., 12345678901., 123456789012., 1234567890123.}) {
    for (int exp = -123; exp <= 123; ++exp) {
      for (int sign : {1, -1}) {
        doubles.push_back(sign * std::ldexp(base, exp));
      }
    }
  }

  // Workaround libc bug.
  // https://sourceware.org/bugzilla/show_bug.cgi?id=22142
  const bool gcc_bug_22142 =
      StrPrint("%f", std::numeric_limits<double>::max()) !=
      "1797693134862315708145274237317043567980705675258449965989174768031"
      "5726078002853876058955863276687817154045895351438246423432132688946"
      "4182768467546703537516986049910576551282076245490090389328944075868"
      "5084551339423045832369032229481658085593321233482747978262041447231"
      "68738177180919299881250404026184124858368.000000";

  if (!gcc_bug_22142) {
    for (int exp = -300; exp <= 300; ++exp) {
      const double all_ones_mantissa = 0x1fffffffffffff;
      doubles.push_back(std::ldexp(all_ones_mantissa, exp));
    }
  }

  if (gcc_bug_22142) {
    for (auto &d : doubles) {
      using L = std::numeric_limits<double>;
      double d2 = std::abs(d);
      if (d2 == L::max() || d2 == L::min() || d2 == L::denorm_min()) {
        d = 0;
      }
    }
  }

  // Remove duplicates to speed up the logic below.
  std::sort(doubles.begin(), doubles.end());
  doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end());

#ifndef __APPLE__
  // Apple formats NaN differently (+nan) vs. (nan)
  doubles.push_back(std::nan(""));
#endif

  // Reserve the space to ensure we don't allocate memory in the output itself.
  std::string str_format_result;
  str_format_result.reserve(1 << 20);
  std::string string_printf_result;
  string_printf_result.reserve(1 << 20);

  for (const char *fmt : kFormats) {
    for (char f : {'f', 'F',  //
                   'g', 'G',  //
                   'a', 'A',  //
                   'e', 'E'}) {
      std::string fmt_str = std::string(fmt) + f;

      if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F') {
        // This particular test takes way too long with snprintf.
        // Disable for the case we are not implementing natively.
        continue;
      }

      for (double d : doubles) {
        int i = -10;
        FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)};
        UntypedFormatSpecImpl format(fmt_str);

        string_printf_result.clear();
        StrAppend(&string_printf_result, fmt_str.c_str(), d, i);
        str_format_result.clear();

        {
          AppendPack(&str_format_result, format, absl::MakeSpan(args));
        }

        if (string_printf_result != str_format_result) {
          // We use ASSERT_EQ here because failures are usually correlated and a
          // bug would print way too many failed expectations causing the test
          // to time out.
          ASSERT_EQ(string_printf_result, str_format_result)
              << fmt_str << " " << StrPrint("%.18g", d) << " "
              << StrPrint("%a", d) << " " << StrPrint("%.1080f", d);
        }
      }
    }
  }
}

TEST_F(FormatConvertTest, FloatRound) {
  std::string s;
  const auto format = [&](const char *fmt, double d) -> std::string & {
    s.clear();
    FormatArgImpl args[1] = {FormatArgImpl(d)};
    AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args));
#if !defined(_MSC_VER)
    // MSVC has a different rounding policy than us so we can't test our
    // implementation against the native one there.
    EXPECT_EQ(StrPrint(fmt, d), s);
#endif  // _MSC_VER

    return s;
  };
  // All of these values have to be exactly represented.
  // Otherwise we might not be testing what we think we are testing.

  // These values can fit in a 64bit "fast" representation.
  const double exact_value = 0.00000000000005684341886080801486968994140625;
  assert(exact_value == std::pow(2, -44));
  // Round up at a 5xx.
  EXPECT_EQ(format("%.13f", exact_value), "0.0000000000001");
  // Round up at a >5
  EXPECT_EQ(format("%.14f", exact_value), "0.00000000000006");
  // Round down at a <5
  EXPECT_EQ(format("%.16f", exact_value), "0.0000000000000568");
  // Nine handling
  EXPECT_EQ(format("%.35f", exact_value),
            "0.00000000000005684341886080801486969");
  EXPECT_EQ(format("%.36f", exact_value),
            "0.000000000000056843418860808014869690");
  // Round down the last nine.
  EXPECT_EQ(format("%.37f", exact_value),
            "0.0000000000000568434188608080148696899");
  EXPECT_EQ(format("%.10f", 0.000003814697265625), "0.0000038147");
  // Round up the last nine
  EXPECT_EQ(format("%.11f", 0.000003814697265625), "0.00000381470");
  EXPECT_EQ(format("%.12f", 0.000003814697265625), "0.000003814697");

  // Round to even (down)
  EXPECT_EQ(format("%.43f", exact_value),
            "0.0000000000000568434188608080148696899414062");
  // Exact
  EXPECT_EQ(format("%.44f", exact_value),
            "0.00000000000005684341886080801486968994140625");
  // Round to even (up), let make the last digits 75 instead of 25
  EXPECT_EQ(format("%.43f", exact_value + std::pow(2, -43)),
            "0.0000000000001705302565824240446090698242188");
  // Exact, just to check.
  EXPECT_EQ(format("%.44f", exact_value + std::pow(2, -43)),
            "0.00000000000017053025658242404460906982421875");

  // This value has to be small enough that it won't fit in the uint128
  // representation for printing.
  const double small_exact_value =
      0.000000000000000000000000000000000000752316384526264005099991383822237233803945956334136013765601092018187046051025390625;  // NOLINT
  assert(small_exact_value == std::pow(2, -120));
  // Round up at a 5xx.
  EXPECT_EQ(format("%.37f", small_exact_value),
            "0.0000000000000000000000000000000000008");
  // Round down at a <5
  EXPECT_EQ(format("%.38f", small_exact_value),
            "0.00000000000000000000000000000000000075");
  // Round up at a >5
  EXPECT_EQ(format("%.41f", small_exact_value),
            "0.00000000000000000000000000000000000075232");
  // Nine handling
  EXPECT_EQ(format("%.55f", small_exact_value),
            "0.0000000000000000000000000000000000007523163845262640051");
  EXPECT_EQ(format("%.56f", small_exact_value),
            "0.00000000000000000000000000000000000075231638452626400510");
  EXPECT_EQ(format("%.57f", small_exact_value),
            "0.000000000000000000000000000000000000752316384526264005100");
  EXPECT_EQ(format("%.58f", small_exact_value),
            "0.0000000000000000000000000000000000007523163845262640051000");
  // Round down the last nine
  EXPECT_EQ(format("%.59f", small_exact_value),
            "0.00000000000000000000000000000000000075231638452626400509999");
  // Round up the last nine
  EXPECT_EQ(format("%.79f", small_exact_value),
            "0.000000000000000000000000000000000000"
            "7523163845262640050999913838222372338039460");

  // Round to even (down)
  EXPECT_EQ(format("%.119f", small_exact_value),
            "0.000000000000000000000000000000000000"
            "75231638452626400509999138382223723380"
            "394595633413601376560109201818704605102539062");
  // Exact
  EXPECT_EQ(format("%.120f", small_exact_value),
            "0.000000000000000000000000000000000000"
            "75231638452626400509999138382223723380"
            "3945956334136013765601092018187046051025390625");
  // Round to even (up), let make the last digits 75 instead of 25
  EXPECT_EQ(format("%.119f", small_exact_value + std::pow(2, -119)),
            "0.000000000000000000000000000000000002"
            "25694915357879201529997415146671170141"
            "183786900240804129680327605456113815307617188");
  // Exact, just to check.
  EXPECT_EQ(format("%.120f", small_exact_value + std::pow(2, -119)),
            "0.000000000000000000000000000000000002"
            "25694915357879201529997415146671170141"
            "1837869002408041296803276054561138153076171875");
}

// We don't actually store the results. This is just to exercise the rest of the
// machinery.
struct NullSink {
  friend void AbslFormatFlush(NullSink *sink, string_view str) {}
};

template <typename... T>
bool FormatWithNullSink(absl::string_view fmt, const T &... a) {
  NullSink sink;
  FormatArgImpl args[] = {FormatArgImpl(a)...};
  return FormatUntyped(&sink, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args));
}

TEST_F(FormatConvertTest, ExtremeWidthPrecision) {
  for (const char *fmt : {"f"}) {
    for (double d : {1e-100, 1.0, 1e100}) {
      constexpr int max = std::numeric_limits<int>::max();
      EXPECT_TRUE(FormatWithNullSink(std::string("%.*") + fmt, max, d));
      EXPECT_TRUE(FormatWithNullSink(std::string("%1.*") + fmt, max, d));
      EXPECT_TRUE(FormatWithNullSink(std::string("%*") + fmt, max, d));
      EXPECT_TRUE(FormatWithNullSink(std::string("%*.*") + fmt, max, max, d));
    }
  }
}

TEST_F(FormatConvertTest, LongDouble) {
#ifdef _MSC_VER
  // MSVC has a different rounding policy than us so we can't test our
  // implementation against the native one there.
  return;
#endif  // _MSC_VER
  const char *const kFormats[] = {"%",    "%.3", "%8.5", "%9",  "%.5000",
                                  "%.60", "%+",  "% ",   "%-10"};

  std::vector<long double> doubles = {
      0.0,
      -0.0,
      std::numeric_limits<long double>::max(),
      -std::numeric_limits<long double>::max(),
      std::numeric_limits<long double>::min(),
      -std::numeric_limits<long double>::min(),
      std::numeric_limits<long double>::infinity(),
      -std::numeric_limits<long double>::infinity()};

  for (long double base : {1.L, 12.L, 123.L, 1234.L, 12345.L, 123456.L,
                           1234567.L, 12345678.L, 123456789.L, 1234567890.L,
                           12345678901.L, 123456789012.L, 1234567890123.L,
                           // This value is not representable in double, but it
                           // is in long double that uses the extended format.
                           // This is to verify that we are not truncating the
                           // value mistakenly through a double.
                           10000000000000000.25L}) {
    for (int exp : {-1000, -500, 0, 500, 1000}) {
      for (int sign : {1, -1}) {
        doubles.push_back(sign * std::ldexp(base, exp));
        doubles.push_back(sign / std::ldexp(base, exp));
      }
    }
  }

  // Regression tests
  //
  // Using a string literal because not all platforms support hex literals or it
  // might be out of range.
  doubles.push_back(std::strtold("-0xf.ffffffb5feafffbp-16324L", nullptr));

  for (const char *fmt : kFormats) {
    for (char f : {'f', 'F',  //
                   'g', 'G',  //
                   'a', 'A',  //
                   'e', 'E'}) {
      std::string fmt_str = std::string(fmt) + 'L' + f;

      if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F') {
        // This particular test takes way too long with snprintf.
        // Disable for the case we are not implementing natively.
        continue;
      }

      for (auto d : doubles) {
        FormatArgImpl arg(d);
        UntypedFormatSpecImpl format(fmt_str);
        // We use ASSERT_EQ here because failures are usually correlated and a
        // bug would print way too many failed expectations causing the test to
        // time out.
        ASSERT_EQ(StrPrint(fmt_str.c_str(), d), FormatPack(format, {&arg, 1}))
            << fmt_str << " " << StrPrint("%.18Lg", d) << " "
            << StrPrint("%La", d) << " " << StrPrint("%.1080Lf", d);
      }
    }
  }
}

TEST_F(FormatConvertTest, IntAsFloat) {
  const int kMin = std::numeric_limits<int>::min();
  const int kMax = std::numeric_limits<int>::max();
  const int ia[] = {
    1, 2, 3, 123,
    -1, -2, -3, -123,
    0, kMax - 1, kMax, kMin + 1, kMin };
  for (const int fx : ia) {
    SCOPED_TRACE(fx);
    const FormatArgImpl args[] = {FormatArgImpl(fx)};
    struct Expectation {
      int line;
      std::string out;
      const char *fmt;
    };
    const double dx = static_cast<double>(fx);
    const Expectation kExpect[] = {
      { __LINE__, StrPrint("%f", dx), "%f" },
      { __LINE__, StrPrint("%12f", dx), "%12f" },
      { __LINE__, StrPrint("%.12f", dx), "%.12f" },
      { __LINE__, StrPrint("%12a", dx), "%12a" },
      { __LINE__, StrPrint("%.12a", dx), "%.12a" },
    };
    for (const Expectation &e : kExpect) {
      SCOPED_TRACE(e.line);
      SCOPED_TRACE(e.fmt);
      UntypedFormatSpecImpl format(e.fmt);
      EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args)));
    }
  }
}

template <typename T>
bool FormatFails(const char* test_format, T value) {
  std::string format_string = std::string("<<") + test_format + ">>";
  UntypedFormatSpecImpl format(format_string);

  int one = 1;
  const FormatArgImpl args[] = {FormatArgImpl(value), FormatArgImpl(one)};
  EXPECT_EQ(FormatPack(format, absl::MakeSpan(args)), "")
      << "format=" << test_format << " value=" << value;
  return FormatPack(format, absl::MakeSpan(args)).empty();
}

TEST_F(FormatConvertTest, ExpectedFailures) {
  // Int input
  EXPECT_TRUE(FormatFails("%p", 1));
  EXPECT_TRUE(FormatFails("%s", 1));
  EXPECT_TRUE(FormatFails("%n", 1));

  // Double input
  EXPECT_TRUE(FormatFails("%p", 1.));
  EXPECT_TRUE(FormatFails("%s", 1.));
  EXPECT_TRUE(FormatFails("%n", 1.));
  EXPECT_TRUE(FormatFails("%c", 1.));
  EXPECT_TRUE(FormatFails("%d", 1.));
  EXPECT_TRUE(FormatFails("%x", 1.));
  EXPECT_TRUE(FormatFails("%*d", 1.));

  // String input
  EXPECT_TRUE(FormatFails("%n", ""));
  EXPECT_TRUE(FormatFails("%c", ""));
  EXPECT_TRUE(FormatFails("%d", ""));
  EXPECT_TRUE(FormatFails("%x", ""));
  EXPECT_TRUE(FormatFails("%f", ""));
  EXPECT_TRUE(FormatFails("%*d", ""));
}

}  // namespace
}  // namespace str_format_internal
ABSL_NAMESPACE_END
}  // namespace absl