X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ftest%2FStringTest.cpp;h=76cf6ac40f78c0bdd8d300ed2dff5e98e945b02c;hb=b179601dfc42d8e3230d6477d7db8f3d5a8f64dc;hp=63f9491ebe81cb2956cc2fa2c14555b9ff216817;hpb=1c60d7571403a19794be9912a7b4247e89cefe1a;p=folly.git diff --git a/folly/test/StringTest.cpp b/folly/test/StringTest.cpp index 63f9491e..76cf6ac4 100644 --- a/folly/test/StringTest.cpp +++ b/folly/test/StringTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015 Facebook, Inc. + * Copyright 2016 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,12 +16,10 @@ #include -#include -#include -#include +#include #include -#include +#include using namespace folly; using namespace std; @@ -37,12 +35,15 @@ TEST(StringPrintf, BasicTest) { TEST(StringPrintf, NumericFormats) { EXPECT_EQ("12", stringPrintf("%d", 12)); - EXPECT_EQ("5000000000", stringPrintf("%ld", 5000000000UL)); - EXPECT_EQ("5000000000", stringPrintf("%ld", 5000000000L)); - EXPECT_EQ("-5000000000", stringPrintf("%ld", -5000000000L)); + EXPECT_EQ("2000000000", stringPrintf("%ld", 2000000000UL)); + EXPECT_EQ("2000000000", stringPrintf("%ld", 2000000000L)); + EXPECT_EQ("-2000000000", stringPrintf("%ld", -2000000000L)); + EXPECT_EQ("5000000000", stringPrintf("%lld", 5000000000ULL)); + EXPECT_EQ("5000000000", stringPrintf("%lld", 5000000000LL)); + EXPECT_EQ("-5000000000", stringPrintf("%lld", -5000000000LL)); EXPECT_EQ("-1", stringPrintf("%d", 0xffffffff)); - EXPECT_EQ("-1", stringPrintf("%ld", 0xffffffffffffffff)); - EXPECT_EQ("-1", stringPrintf("%ld", 0xffffffffffffffffUL)); + EXPECT_EQ("-1", stringPrintf("%" PRId64, 0xffffffffffffffff)); + EXPECT_EQ("-1", stringPrintf("%" PRId64, 0xffffffffffffffffUL)); EXPECT_EQ("7.7", stringPrintf("%1.1f", 7.7)); EXPECT_EQ("7.7", stringPrintf("%1.1lf", 7.7)); @@ -155,39 +156,6 @@ TEST(StringPrintf, oldStringAppendf) { EXPECT_EQ(string("helloa/b/c/d"), s); } -// A simple benchmark that tests various output sizes for a simple -// input; the goal is to measure the output buffer resize code cost. -void stringPrintfOutputSize(int iters, int param) { - string buffer; - BENCHMARK_SUSPEND { buffer.resize(param, 'x'); } - - for (int64_t i = 0; i < iters; ++i) { - string s = stringPrintf("msg: %d, %d, %s", 10, 20, buffer.c_str()); - } -} - -// The first few of these tend to fit in the inline buffer, while the -// subsequent ones cross that limit, trigger a second vsnprintf, and -// exercise a different codepath. -BENCHMARK_PARAM(stringPrintfOutputSize, 1) -BENCHMARK_PARAM(stringPrintfOutputSize, 4) -BENCHMARK_PARAM(stringPrintfOutputSize, 16) -BENCHMARK_PARAM(stringPrintfOutputSize, 64) -BENCHMARK_PARAM(stringPrintfOutputSize, 256) -BENCHMARK_PARAM(stringPrintfOutputSize, 1024) - -// Benchmark simple stringAppendf behavior to show a pathology Lovro -// reported (t5735468). -BENCHMARK(stringPrintfAppendfBenchmark, iters) { - for (unsigned int i = 0; i < iters; ++i) { - string s; - BENCHMARK_SUSPEND { s.reserve(300000); } - for (int j = 0; j < 300000; ++j) { - stringAppendf(&s, "%d", 1); - } - } -} - TEST(Escape, cEscape) { EXPECT_EQ("hello world", cEscape("hello world")); EXPECT_EQ("hello \\\\world\\\" goodbye", @@ -303,96 +271,6 @@ TEST(Escape, uriUnescapePercentDecoding) { } } -namespace { -fbstring cbmString; -fbstring cbmEscapedString; -fbstring cEscapedString; -fbstring cUnescapedString; -const size_t kCBmStringLength = 64 << 10; -const uint32_t kCPrintablePercentage = 90; - -fbstring uribmString; -fbstring uribmEscapedString; -fbstring uriEscapedString; -fbstring uriUnescapedString; -const size_t kURIBmStringLength = 256; -const uint32_t kURIPassThroughPercentage = 50; - -void initBenchmark() { - std::mt19937 rnd; - - // C escape - std::uniform_int_distribution printable(32, 126); - std::uniform_int_distribution nonPrintable(0, 160); - std::uniform_int_distribution percentage(0, 99); - - cbmString.reserve(kCBmStringLength); - for (size_t i = 0; i < kCBmStringLength; ++i) { - unsigned char c; - if (percentage(rnd) < kCPrintablePercentage) { - c = printable(rnd); - } else { - c = nonPrintable(rnd); - // Generate characters in both non-printable ranges: - // 0..31 and 127..255 - if (c >= 32) { - c += (126 - 32) + 1; - } - } - cbmString.push_back(c); - } - - cbmEscapedString = cEscape(cbmString); - - // URI escape - std::uniform_int_distribution passthrough('a', 'z'); - std::string encodeChars = " ?!\"',+[]"; - std::uniform_int_distribution encode(0, encodeChars.size() - 1); - - uribmString.reserve(kURIBmStringLength); - for (size_t i = 0; i < kURIBmStringLength; ++i) { - unsigned char c; - if (percentage(rnd) < kURIPassThroughPercentage) { - c = passthrough(rnd); - } else { - c = encodeChars[encode(rnd)]; - } - uribmString.push_back(c); - } - - uribmEscapedString = uriEscape(uribmString); -} - -BENCHMARK(BM_cEscape, iters) { - while (iters--) { - cEscapedString = cEscape(cbmString); - doNotOptimizeAway(cEscapedString.size()); - } -} - -BENCHMARK(BM_cUnescape, iters) { - while (iters--) { - cUnescapedString = cUnescape(cbmEscapedString); - doNotOptimizeAway(cUnescapedString.size()); - } -} - -BENCHMARK(BM_uriEscape, iters) { - while (iters--) { - uriEscapedString = uriEscape(uribmString); - doNotOptimizeAway(uriEscapedString.size()); - } -} - -BENCHMARK(BM_uriUnescape, iters) { - while (iters--) { - uriUnescapedString = uriUnescape(uribmEscapedString); - doNotOptimizeAway(uriUnescapedString.size()); - } -} - -} // namespace - namespace { double pow2(int exponent) { @@ -535,8 +413,8 @@ TEST(PrettyToDouble, Basic) { double recoveredX = 0; try{ recoveredX = prettyToDouble(testString, formatType); - } catch (std::range_error &ex){ - EXPECT_TRUE(false); + } catch (const std::range_error& ex) { + EXPECT_TRUE(false) << testCase.prettyString << " -> " << ex.what(); } double relativeError = fabs(x) < 1e-5 ? (x-recoveredX) : (x - recoveredX) / x; @@ -1030,8 +908,36 @@ TEST(Split, fixed_convert) { EXPECT_EQ(13, b); EXPECT_EQ("14.7:b", d); - EXPECT_THROW(folly::split(':', "a:13:14.7:b", a, b, c), - std::range_error); + + // Enable verifying that a line only contains one field + EXPECT_TRUE(folly::split(' ', "hello", a)); + EXPECT_FALSE(folly::split(' ', "hello world", a)); +} + +namespace my { + +enum class Color { + Red, + Blue, +}; + +void parseTo(folly::StringPiece in, Color& out) { + if (in == "R") { + out = Color::Red; + } else if (in == "B") { + out = Color::Blue; + } else { + throw runtime_error(""); + } +} +} + +TEST(Split, fixed_convert_custom) { + my::Color c1, c2; + + EXPECT_TRUE(folly::split(',', "R,B", c1, c2)); + EXPECT_EQ(c1, my::Color::Red); + EXPECT_EQ(c2, my::Color::Blue); } TEST(String, join) { @@ -1068,7 +974,7 @@ TEST(String, hexlify) { string input1 = "0123"; string output1; EXPECT_TRUE(hexlify(input1, output1)); - EXPECT_EQ(output1, "30313233"); + EXPECT_EQ("30313233", output1); fbstring input2 = "abcdefg"; input2[1] = 0; @@ -1076,7 +982,11 @@ TEST(String, hexlify) { input2[5] = 0xb6; fbstring output2; EXPECT_TRUE(hexlify(input2, output2)); - EXPECT_EQ(output2, "610063ff65b667"); + EXPECT_EQ("610063ff65b667", output2); + + EXPECT_EQ("666f6f626172", hexlify("foobar")); + auto bytes = folly::make_array(1, 2, 3, 4); + EXPECT_EQ("01020304", hexlify(ByteRange{bytes.data(), bytes.size()})); } TEST(String, unhexlify) { @@ -1104,6 +1014,10 @@ TEST(String, unhexlify) { string input4 = "xy"; string output4; EXPECT_FALSE(unhexlify(input4, output4)); + + EXPECT_EQ("foobar", unhexlify("666f6f626172")); + EXPECT_EQ(StringPiece("foo\0bar", 7), unhexlify("666f6f00626172")); + EXPECT_THROW(unhexlify("666f6fzz626172"), std::domain_error); } TEST(String, backslashify) { @@ -1212,84 +1126,6 @@ TEST(String, toLowerAsciiUnaligned) { } } -////////////////////////////////////////////////////////////////////// - -BENCHMARK(splitOnSingleChar, iters) { - static const std::string line = "one:two:three:four"; - for (size_t i = 0; i < iters << 4; ++i) { - std::vector pieces; - folly::split(':', line, pieces); - } -} - -BENCHMARK(splitOnSingleCharFixed, iters) { - static const std::string line = "one:two:three:four"; - for (size_t i = 0; i < iters << 4; ++i) { - StringPiece a, b, c, d; - folly::split(':', line, a, b, c, d); - } -} - -BENCHMARK(splitOnSingleCharFixedAllowExtra, iters) { - static const std::string line = "one:two:three:four"; - for (size_t i = 0; i < iters << 4; ++i) { - StringPiece a, b, c, d; - folly::split(':', line, a, b, c, d); - } -} - -BENCHMARK(splitStr, iters) { - static const std::string line = "one-*-two-*-three-*-four"; - for (size_t i = 0; i < iters << 4; ++i) { - std::vector pieces; - folly::split("-*-", line, pieces); - } -} - -BENCHMARK(splitStrFixed, iters) { - static const std::string line = "one-*-two-*-three-*-four"; - for (size_t i = 0; i < iters << 4; ++i) { - StringPiece a, b, c, d; - folly::split("-*-", line, a, b, c, d); - } -} - -BENCHMARK(boost_splitOnSingleChar, iters) { - static const std::string line = "one:two:three:four"; - bool(*pred)(char) = [] (char c) -> bool { return c == ':'; }; - for (size_t i = 0; i < iters << 4; ++i) { - std::vector > pieces; - boost::split(pieces, line, pred); - } -} - -BENCHMARK(joinCharStr, iters) { - static const std::vector input = { - "one", "two", "three", "four", "five", "six", "seven" }; - for (size_t i = 0; i < iters << 4; ++i) { - std::string output; - folly::join(':', input, output); - } -} - -BENCHMARK(joinStrStr, iters) { - static const std::vector input = { - "one", "two", "three", "four", "five", "six", "seven" }; - for (size_t i = 0; i < iters << 4; ++i) { - std::string output; - folly::join(":", input, output); - } -} - -BENCHMARK(joinInt, iters) { - static const auto input = { - 123, 456, 78910, 1112, 1314, 151, 61718 }; - for (size_t i = 0; i < iters << 4; ++i) { - std::string output; - folly::join(":", input, output); - } -} - TEST(String, whitespace) { // trimWhitespace: EXPECT_EQ("kavabanga", @@ -1337,15 +1173,193 @@ TEST(String, whitespace) { EXPECT_EQ("", rtrimWhitespace("\r ")); } -int main(int argc, char *argv[]) { - testing::InitGoogleTest(&argc, argv); - gflags::ParseCommandLineFlags(&argc, &argv, true); - auto ret = RUN_ALL_TESTS(); - if (!ret) { - initBenchmark(); - if (FLAGS_benchmark) { - folly::runBenchmarks(); - } - } - return ret; +TEST(String, stripLeftMargin_really_empty) { + auto input = ""; + auto expected = ""; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_empty) { + auto input = R"TEXT( + )TEXT"; + auto expected = ""; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_only_whitespace) { + // using ~ as a marker + string input = R"TEXT( + ~ + )TEXT"; + input = boost::regex_replace(input, boost::regex("~"), ""); + EXPECT_EQ("\n \n ", input); + auto expected = "\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_only_uneven_whitespace) { + // using ~ as a marker1 + string input = R"TEXT( + ~ + ~ + )TEXT"; + input = boost::regex_replace(input, boost::regex("~"), ""); + EXPECT_EQ("\n \n \n ", input); + auto expected = "\n\n"; + + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_one_line) { + auto input = R"TEXT( + hi there bob! + )TEXT"; + auto expected = "hi there bob!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_two_lines) { + auto input = R"TEXT( + hi there bob! + nice weather today! + )TEXT"; + auto expected = "hi there bob!\nnice weather today!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_three_lines_uneven) { + auto input = R"TEXT( + hi there bob! + nice weather today! + so long! + )TEXT"; + auto expected = " hi there bob!\nnice weather today!\n so long!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_preceding_blank_lines) { + auto input = R"TEXT( + + + hi there bob! + )TEXT"; + auto expected = "\n\nhi there bob!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_succeeding_blank_lines) { + auto input = R"TEXT( + hi there bob! + + + )TEXT"; + auto expected = "hi there bob!\n\n\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_interstitial_undented_whiteline) { + // using ~ as a marker + string input = R"TEXT( + hi there bob! + ~ + so long! + )TEXT"; + input = boost::regex_replace(input, boost::regex(" +~"), ""); + EXPECT_EQ("\n hi there bob!\n\n so long!\n ", input); + auto expected = "hi there bob!\n\nso long!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_interstitial_dedented_whiteline) { + // using ~ as a marker + string input = R"TEXT( + hi there bob! + ~ + so long! + )TEXT"; + input = boost::regex_replace(input, boost::regex("~"), ""); + EXPECT_EQ("\n hi there bob!\n \n so long!\n ", input); + auto expected = "hi there bob!\n\nso long!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_interstitial_equidented_whiteline) { + // using ~ as a marker + string input = R"TEXT( + hi there bob! + ~ + so long! + )TEXT"; + input = boost::regex_replace(input, boost::regex("~"), ""); + EXPECT_EQ("\n hi there bob!\n \n so long!\n ", input); + auto expected = "hi there bob!\n\nso long!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_interstitial_indented_whiteline) { + // using ~ as a marker + string input = R"TEXT( + hi there bob! + ~ + so long! + )TEXT"; + input = boost::regex_replace(input, boost::regex("~"), ""); + EXPECT_EQ("\n hi there bob!\n \n so long!\n ", input); + auto expected = "hi there bob!\n \nso long!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_no_pre_whitespace) { + // using ~ as a marker + string input = R"TEXT( hi there bob! + ~ + so long! + )TEXT"; + input = boost::regex_replace(input, boost::regex("~"), ""); + EXPECT_EQ(" hi there bob!\n \n so long!\n ", input); + auto expected = "hi there bob!\n \nso long!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_no_post_whitespace) { + // using ~ as a marker + string input = R"TEXT( + hi there bob! + ~ + so long! )TEXT"; + input = boost::regex_replace(input, boost::regex("~"), ""); + EXPECT_EQ("\n hi there bob!\n \n so long! ", input); + auto expected = "hi there bob!\n \nso long! "; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +const folly::StringPiece kTestUTF8 = u8"This is \U0001F602 stuff!"; + +TEST(UTF8StringPiece, valid_utf8) { + folly::StringPiece sp = kTestUTF8; + UTF8StringPiece utf8 = sp; + // utf8.size() not available since it's not a random-access range + EXPECT_EQ(16, utf8.walk_size()); +} + +TEST(UTF8StringPiece, valid_suffix) { + UTF8StringPiece utf8 = kTestUTF8.subpiece(8); + EXPECT_EQ(8, utf8.walk_size()); +} + +TEST(UTF8StringPiece, empty_mid_codepoint) { + UTF8StringPiece utf8 = kTestUTF8.subpiece(9, 0); // okay since it's empty + EXPECT_EQ(0, utf8.walk_size()); +} + +TEST(UTF8StringPiece, invalid_mid_codepoint) { + EXPECT_THROW(UTF8StringPiece(kTestUTF8.subpiece(9, 1)), std::out_of_range); +} + +TEST(UTF8StringPiece, valid_implicit_conversion) { + std::string input = u8"\U0001F602\U0001F602\U0001F602"; + auto checkImplicitCtor = [](UTF8StringPiece implicitCtor) { + return implicitCtor.walk_size(); + }; + EXPECT_EQ(3, checkImplicitCtor(input)); }