X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ftest%2FFormatTest.cpp;h=832c7c676152b4fd6f2068ab184f76a1c8b8d60e;hb=b015c8c3a85860ce011a6e497ba5076ed8be4232;hp=a72be0a0676bc3225aa5245804e87acf7f4ce726;hpb=b942767a0f464533e65ea38c639bf5f535b8f1e0;p=folly.git diff --git a/folly/test/FormatTest.cpp b/folly/test/FormatTest.cpp index a72be0a0..832c7c67 100644 --- a/folly/test/FormatTest.cpp +++ b/folly/test/FormatTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2014 Facebook, Inc. + * Copyright 2015 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +16,8 @@ #include -#include -#include #include -#include -#include -#include -#include - #include using namespace folly; @@ -36,7 +29,8 @@ void compareOctal(Uint u) { char* p = buf1 + detail::uintToOctal(buf1, detail::kMaxOctalLength, u); char buf2[detail::kMaxOctalLength + 1]; - sprintf(buf2, "%jo", static_cast(u)); + EXPECT_LT(snprintf(buf2, sizeof(buf2), "%jo", static_cast(u)), + sizeof(buf2)); EXPECT_EQ(std::string(buf2), std::string(p)); } @@ -48,7 +42,8 @@ void compareHex(Uint u) { char* p = buf1 + detail::uintToHexLower(buf1, detail::kMaxHexLength, u); char buf2[detail::kMaxHexLength + 1]; - sprintf(buf2, "%jx", static_cast(u)); + EXPECT_LT(snprintf(buf2, sizeof(buf2), "%jx", static_cast(u)), + sizeof(buf2)); EXPECT_EQ(std::string(buf2), std::string(p)); } @@ -109,6 +104,13 @@ TEST(Format, Simple) { EXPECT_EQ("hello ", sformat("{:<7}", "hello")); EXPECT_EQ(" hello", sformat("{:>7}", "hello")); + EXPECT_EQ(" hi", sformat("{:>*}", 4, "hi")); + EXPECT_EQ(" hi!", sformat("{:*}{}", 3, "", "hi!")); + EXPECT_EQ(" 123", sformat("{:*}", 7, 123)); + EXPECT_EQ("123 ", sformat("{:<*}", 7, 123)); + EXPECT_EQ("----<=>----", sformat("{:-^*}", 11, "<=>")); + EXPECT_EQ("+++456+++", sformat("{2:+^*0}", 9, "unused", 456)); + std::vector v1 {10, 20, 30}; EXPECT_EQ("0020", sformat("{0[1]:04}", v1)); EXPECT_EQ("0020", svformat("{1:04}", v1)); @@ -195,30 +197,9 @@ TEST(Format, Simple) { format(&s, "{} {}", 42, 23); format(&s, " hello {:X<7}", "world"); EXPECT_EQ("42 23 hello worldXX", s); - - // Test writing to FILE. I'd use open_memstream but that's not available - // outside of Linux (even though it's in POSIX.1-2008). - { - int fds[2]; - CHECK_ERR(pipe(fds)); - SCOPE_EXIT { closeNoInt(fds[1]); }; - { - FILE* fp = fdopen(fds[1], "wb"); - PCHECK(fp); - SCOPE_EXIT { fclose(fp); }; - writeTo(fp, format("{} {}", 42, 23)); // <= 512 bytes (PIPE_BUF) - } - - char buf[512]; - ssize_t n = readFull(fds[0], buf, sizeof(buf)); - CHECK_GE(n, 0); - - EXPECT_EQ("42 23", std::string(buf, n)); - } } TEST(Format, Float) { - double d = 1; EXPECT_EQ("1", sformat("{}", 1.0)); EXPECT_EQ("0.1", sformat("{}", 0.1)); EXPECT_EQ("0.01", sformat("{}", 0.01)); @@ -259,34 +240,90 @@ TEST(Format, MultiLevel) { EXPECT_EQ("world", sformat("{[0.hello]}", v)); } -TEST(Format, dynamic) { - auto dyn = parseJson( - "{\n" - " \"hello\": \"world\",\n" - " \"x\": [20, 30],\n" - " \"y\": {\"a\" : 42}\n" - "}"); - - EXPECT_EQ("world", sformat("{0[hello]}", dyn)); - EXPECT_THROW(sformat("{0[none]}", dyn), std::out_of_range); - EXPECT_EQ("world", sformat("{0[hello]}", defaulted(dyn, "meow"))); - EXPECT_EQ("meow", sformat("{0[none]}", defaulted(dyn, "meow"))); - - EXPECT_EQ("20", sformat("{0[x.0]}", dyn)); - EXPECT_THROW(sformat("{0[x.2]}", dyn), std::out_of_range); +TEST(Format, separatorDecimalInteger) { + EXPECT_EQ("0", sformat("{:,d}", 0)); + EXPECT_EQ("1", sformat("{:d}", 1)); + EXPECT_EQ("1", sformat("{:,d}", 1)); + EXPECT_EQ("1", sformat("{:,}", 1)); + EXPECT_EQ("123", sformat("{:d}", 123)); + EXPECT_EQ("123", sformat("{:,d}", 123)); + EXPECT_EQ("123", sformat("{:,}", 123)); + EXPECT_EQ("1234", sformat("{:d}", 1234)); + EXPECT_EQ("1,234", sformat("{:,d}", 1234)); + EXPECT_EQ("1,234", sformat("{:,}", 1234)); + EXPECT_EQ("12345678", sformat("{:d}", 12345678)); + EXPECT_EQ("12,345,678", sformat("{:,d}", 12345678)); + EXPECT_EQ("12,345,678", sformat("{:,}", 12345678)); + EXPECT_EQ("-1234", sformat("{:d}", -1234)); + EXPECT_EQ("-1,234", sformat("{:,d}", -1234)); + EXPECT_EQ("-1,234", sformat("{:,}", -1234)); + + int64_t max_int64_t = std::numeric_limits::max(); + int64_t min_int64_t = std::numeric_limits::min(); + uint64_t max_uint64_t = std::numeric_limits::max(); + EXPECT_EQ("9223372036854775807", sformat("{:d}", max_int64_t)); + EXPECT_EQ("9,223,372,036,854,775,807", sformat("{:,d}", max_int64_t)); + EXPECT_EQ("9,223,372,036,854,775,807", sformat("{:,}", max_int64_t)); + EXPECT_EQ("-9223372036854775808", sformat("{:d}", min_int64_t)); + EXPECT_EQ("-9,223,372,036,854,775,808", sformat("{:,d}", min_int64_t)); + EXPECT_EQ("-9,223,372,036,854,775,808", sformat("{:,}", min_int64_t)); + EXPECT_EQ("18446744073709551615", sformat("{:d}", max_uint64_t)); + EXPECT_EQ("18,446,744,073,709,551,615", sformat("{:,d}", max_uint64_t)); + EXPECT_EQ("18,446,744,073,709,551,615", sformat("{:,}", max_uint64_t)); + + EXPECT_EQ(" -1,234", sformat("{: 8,}", -1234)); + EXPECT_EQ("-001,234", sformat("{:08,d}", -1234)); + EXPECT_EQ("-00001,234", sformat("{:010,d}", -1234)); + EXPECT_EQ(" -1,234 ", sformat("{:^ 8,d}", -1234)); +} - // No support for "deep" defaulting (dyn["x"] is not defaulted) - auto v = dyn.at("x"); - EXPECT_EQ("20", sformat("{0[0]}", v)); - EXPECT_THROW(sformat("{0[2]}", v), std::out_of_range); - EXPECT_EQ("20", sformat("{0[0]}", defaulted(v, 42))); - EXPECT_EQ("42", sformat("{0[2]}", defaulted(v, 42))); +// Note that sformat("{:n}", ...) uses the current locale setting to insert the +// appropriate number separator characters. +TEST(Format, separatorNumber) { + EXPECT_EQ("0", sformat("{:n}", 0)); + EXPECT_EQ("1", sformat("{:n}", 1)); + EXPECT_EQ("123", sformat("{:n}", 123)); + EXPECT_EQ("1234", sformat("{:n}", 1234)); + EXPECT_EQ("12345678", sformat("{:n}", 12345678)); + EXPECT_EQ("-1234", sformat("{:n}", -1234)); + + int64_t max_int64_t = std::numeric_limits::max(); + int64_t min_int64_t = std::numeric_limits::min(); + uint64_t max_uint64_t = std::numeric_limits::max(); + EXPECT_EQ("9223372036854775807", sformat("{:n}", max_int64_t)); + EXPECT_EQ("-9223372036854775808", sformat("{:n}", min_int64_t)); + EXPECT_EQ("18446744073709551615", sformat("{:n}", max_uint64_t)); + + EXPECT_EQ(" -1234", sformat("{: 8n}", -1234)); + EXPECT_EQ("-0001234", sformat("{:08n}", -1234)); + EXPECT_EQ("-000001234", sformat("{:010n}", -1234)); + EXPECT_EQ(" -1234 ", sformat("{:^ 8n}", -1234)); +} - EXPECT_EQ("42", sformat("{0[y.a]}", dyn)); +// insertThousandsGroupingUnsafe requires non-const params +static void testGrouping(const char* a_str, const char* expected) { + char str[256]; + char* end_ptr = str + snprintf(str, sizeof(str), "%s", a_str); + ASSERT_LT(end_ptr, str + sizeof(str)); + folly::detail::insertThousandsGroupingUnsafe(str, &end_ptr); + ASSERT_STREQ(expected, str); +} - EXPECT_EQ("(null)", sformat("{}", dynamic(nullptr))); +TEST(Format, separatorUnit) { + testGrouping("0", "0"); + testGrouping("1", "1"); + testGrouping("12", "12"); + testGrouping("123", "123"); + testGrouping("1234", "1,234"); + testGrouping("12345", "12,345"); + testGrouping("123456", "123,456"); + testGrouping("1234567", "1,234,567"); + testGrouping("1234567890", "1,234,567,890"); + testGrouping("9223372036854775807", "9,223,372,036,854,775,807"); + testGrouping("18446744073709551615", "18,446,744,073,709,551,615"); } + namespace { struct KeyValue { @@ -335,11 +372,37 @@ struct Opaque { } // namespace +#define EXPECT_THROW_STR(code, type, str) \ + do { \ + bool caught = false; \ + try { \ + code; \ + } catch (const type& e) { \ + caught = true; \ + EXPECT_TRUE(strstr(e.what(), (str)) != nullptr) << \ + "Expected message [" << (str) << "], actual message [" << \ + e.what(); \ + } catch (const std::exception& e) { \ + caught = true; \ + ADD_FAILURE() << "Caught different exception type; expected " #type \ + ", caught " << folly::demangle(typeid(e)); \ + } catch (...) { \ + caught = true; \ + ADD_FAILURE() << "Caught unknown exception type; expected " #type; \ + } \ + if (!caught) { \ + ADD_FAILURE() << "Expected exception " #type ", caught nothing"; \ + } \ + } while (false) + +#define EXPECT_FORMAT_ERROR(code, str) \ + EXPECT_THROW_STR(code, folly::BadFormatArg, (str)) + TEST(Format, Unformatted) { Opaque o; EXPECT_NE("", sformat("{}", &o)); - EXPECT_DEATH(sformat("{0[0]}", &o), "No formatter available for this type"); - EXPECT_THROW(sformatChecked("{0[0]}", &o), std::invalid_argument); + EXPECT_FORMAT_ERROR(sformat("{0[0]}", &o), + "No formatter available for this type"); } TEST(Format, Nested) { @@ -354,39 +417,43 @@ TEST(Format, OutOfBounds) { std::vector ints{1, 2, 3, 4, 5}; EXPECT_EQ("1 3 5", sformat("{0[0]} {0[2]} {0[4]}", ints)); EXPECT_THROW(sformat("{[5]}", ints), std::out_of_range); - EXPECT_THROW(sformatChecked("{[5]}", ints), std::out_of_range); std::map map{{"hello", 0}, {"world", 1}}; EXPECT_EQ("hello = 0", sformat("hello = {[hello]}", map)); EXPECT_THROW(sformat("{[nope]}", map), std::out_of_range); EXPECT_THROW(svformat("{nope}", map), std::out_of_range); - EXPECT_THROW(svformatChecked("{nope}", map), std::out_of_range); } TEST(Format, BogusFormatString) { - // format() will crash the program if the format string is invalid. - EXPECT_DEATH(sformat("}"), "single '}' in format string"); - EXPECT_DEATH(sformat("foo}bar"), "single '}' in format string"); - EXPECT_DEATH(sformat("foo{bar"), "missing ending '}'"); - EXPECT_DEATH(sformat("{[test]"), "missing ending '}'"); - EXPECT_DEATH(sformat("{-1.3}"), "argument index must be non-negative"); - EXPECT_DEATH(sformat("{1.3}", 0, 1, 2), "index not allowed"); - EXPECT_DEATH(sformat("{0} {} {1}", 0, 1, 2), + EXPECT_FORMAT_ERROR(sformat("}"), "single '}' in format string"); + EXPECT_FORMAT_ERROR(sformat("foo}bar"), "single '}' in format string"); + EXPECT_FORMAT_ERROR(sformat("foo{bar"), "missing ending '}'"); + EXPECT_FORMAT_ERROR(sformat("{[test]"), "missing ending '}'"); + EXPECT_FORMAT_ERROR(sformat("{-1.3}"), "argument index must be non-negative"); + EXPECT_FORMAT_ERROR(sformat("{1.3}", 0, 1, 2), "index not allowed"); + EXPECT_FORMAT_ERROR(sformat("{0} {} {1}", 0, 1, 2), "may not have both default and explicit arg indexes"); - - // formatChecked() should throw exceptions rather than crashing the program - EXPECT_THROW(sformatChecked("}"), std::invalid_argument); - EXPECT_THROW(sformatChecked("foo}bar"), std::invalid_argument); - EXPECT_THROW(sformatChecked("foo{bar"), std::invalid_argument); - EXPECT_THROW(sformatChecked("{[test]"), std::invalid_argument); - EXPECT_THROW(sformatChecked("{-1.3}"), std::invalid_argument); - EXPECT_THROW(sformatChecked("{1.3}", 0, 1, 2), std::invalid_argument); - EXPECT_THROW(sformatChecked("{0} {} {1}", 0, 1, 2), std::invalid_argument); + EXPECT_FORMAT_ERROR(sformat("{:*}", 1.2), + "dynamic field width argument must be integral"); + EXPECT_FORMAT_ERROR(sformat("{} {:*}", "hi"), + "argument index out of range, max=1"); + EXPECT_FORMAT_ERROR( + sformat("{:*0}", 12, "ok"), + "cannot provide width arg index without value arg index" + ); + EXPECT_FORMAT_ERROR( + sformat("{0:*}", 12, "ok"), + "cannot provide value arg index without width arg index" + ); + + std::vector v{1, 2, 3}; + EXPECT_FORMAT_ERROR(svformat("{:*}", v), + "dynamic field width not supported in vformat()"); // This one fails in detail::enforceWhitespace(), which throws // std::range_error - EXPECT_DEATH(sformat("{0[test}"), "Non-whitespace: \\["); - EXPECT_THROW(sformatChecked("{0[test}"), std::exception); + EXPECT_THROW_STR(sformat("{0[test}"), std::range_error, + "Non-whitespace: ["); } template @@ -440,10 +507,3 @@ TEST(Format, Extending) { "another formatter"), "Extending {a {formatter}} in {another formatter}"); } - -int main(int argc, char *argv[]) { - testing::InitGoogleTest(&argc, argv); - gflags::ParseCommandLineFlags(&argc, &argv, true); - return RUN_ALL_TESTS(); -} -