/* * Copyright 2012-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS 1 #endif #include #include #include #include #include #include #include #include using namespace folly; using namespace std; TEST(StringPrintf, BasicTest) { EXPECT_EQ("abc", stringPrintf("%s", "abc")); EXPECT_EQ("abc", stringPrintf("%sbc", "a")); EXPECT_EQ("abc", stringPrintf("a%sc", "b")); EXPECT_EQ("abc", stringPrintf("ab%s", "c")); EXPECT_EQ("abc", stringPrintf("abc")); } TEST(StringPrintf, NumericFormats) { EXPECT_EQ("12", stringPrintf("%d", 12)); 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("%" PRId64, static_cast(0xffffffffffffffffLL))); EXPECT_EQ( "-1", stringPrintf("%" PRId64, static_cast(0xffffffffffffffffULL))); EXPECT_EQ("7.7", stringPrintf("%1.1f", 7.7)); EXPECT_EQ("7.7", stringPrintf("%1.1lf", 7.7)); EXPECT_EQ("7.70000000000000018", stringPrintf("%.17f", 7.7)); EXPECT_EQ("7.70000000000000018", stringPrintf("%.17lf", 7.7)); } TEST(StringPrintf, Appending) { string s; stringAppendf(&s, "a%s", "b"); stringAppendf(&s, "%c", 'c'); EXPECT_EQ(s, "abc"); stringAppendf(&s, " %d", 123); EXPECT_EQ(s, "abc 123"); } void vprintfCheck(const char* expected, const char* fmt, ...) { va_list apOrig; va_start(apOrig, fmt); SCOPE_EXIT { va_end(apOrig); }; va_list ap; va_copy(ap, apOrig); SCOPE_EXIT { va_end(ap); }; // Check both APIs for calling stringVPrintf() EXPECT_EQ(expected, stringVPrintf(fmt, ap)); va_end(ap); va_copy(ap, apOrig); std::string out; stringVPrintf(&out, fmt, ap); va_end(ap); va_copy(ap, apOrig); EXPECT_EQ(expected, out); // Check stringVAppendf() as well std::string prefix = "foobar"; out = prefix; EXPECT_EQ(prefix + expected, stringVAppendf(&out, fmt, ap)); va_end(ap); va_copy(ap, apOrig); } void vprintfError(const char* fmt, ...) { va_list ap; va_start(ap, fmt); SCOPE_EXIT { va_end(ap); }; #ifdef HAVE_VSNPRINTF_ERRORS // OSX's sprintf family does not return a negative number on a bad format // string, but Linux does. It's unclear to me which behavior is more // correct. EXPECT_THROW({ stringVPrintf(fmt, ap); }, std::runtime_error); #endif } TEST(StringPrintf, VPrintf) { vprintfCheck("foo", "%s", "foo"); vprintfCheck( "long string requiring reallocation 1 2 3 0x12345678", "%s %s %d %d %d %#x", "long string", "requiring reallocation", 1, 2, 3, 0x12345678); vprintfError("bogus%", "foo"); } TEST(StringPrintf, VariousSizes) { // Test a wide variety of output sizes, making sure to cross the // vsnprintf buffer boundary implementation detail. for (int i = 0; i < 4096; ++i) { string expected(i + 1, 'a'); expected = "X" + expected + "X"; string result = stringPrintf("%s", expected.c_str()); EXPECT_EQ(expected.size(), result.size()); EXPECT_EQ(expected, result); } // clang-format off EXPECT_EQ("abc12345678910111213141516171819202122232425xyz", stringPrintf("abc%d%d%d%d%d%d%d%d%d%d%d%d%d%d" "%d%d%d%d%d%d%d%d%d%d%dxyz", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)); // clang-format on } TEST(StringPrintf, oldStringPrintfTests) { EXPECT_EQ(string("a/b/c/d"), stringPrintf("%s/%s/%s/%s", "a", "b", "c", "d")); EXPECT_EQ(string(" 5 10"), stringPrintf("%5d %5d", 5, 10)); // check printing w/ a big buffer for (int size = (1 << 8); size <= (1 << 15); size <<= 1) { string a(size, 'z'); string b = stringPrintf("%s", a.c_str()); EXPECT_EQ(a.size(), b.size()); } } TEST(StringPrintf, oldStringAppendf) { string s = "hello"; stringAppendf(&s, "%s/%s/%s/%s", "a", "b", "c", "d"); EXPECT_EQ(string("helloa/b/c/d"), s); } TEST(Escape, cEscape) { EXPECT_EQ("hello world", cEscape("hello world")); EXPECT_EQ( "hello \\\\world\\\" goodbye", cEscape("hello \\world\" goodbye")); EXPECT_EQ("hello\\nworld", cEscape("hello\nworld")); EXPECT_EQ("hello\\377\\376", cEscape("hello\xff\xfe")); } TEST(Escape, cUnescape) { EXPECT_EQ("hello world", cUnescape("hello world")); EXPECT_EQ( "hello \\world\" goodbye", cUnescape("hello \\\\world\\\" goodbye")); EXPECT_EQ("hello\nworld", cUnescape("hello\\nworld")); EXPECT_EQ("hello\nworld", cUnescape("hello\\012world")); EXPECT_EQ("hello\nworld", cUnescape("hello\\x0aworld")); EXPECT_EQ("hello\xff\xfe", cUnescape("hello\\377\\376")); EXPECT_EQ("hello\xff\xfe", cUnescape("hello\\xff\\xfe")); EXPECT_EQ("hello\\", cUnescape("hello\\", false)); EXPECT_THROW_RE( cUnescape("hello\\"), std::invalid_argument, "incomplete escape sequence"); EXPECT_THROW_RE( cUnescape("hello\\x"), std::invalid_argument, "incomplete hex escape sequence"); EXPECT_THROW_RE( cUnescape("hello\\q"), std::invalid_argument, "invalid escape sequence"); } TEST(Escape, uriEscape) { EXPECT_EQ("hello%2c%20%2fworld", uriEscape("hello, /world")); EXPECT_EQ( "hello%2c%20/world", uriEscape("hello, /world", UriEscapeMode::PATH)); EXPECT_EQ( "hello%2c+%2fworld", uriEscape("hello, /world", UriEscapeMode::QUERY)); EXPECT_EQ( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~", uriEscape( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~")); } TEST(Escape, uriUnescape) { EXPECT_EQ("hello, /world", uriUnescape("hello, /world")); EXPECT_EQ("hello, /world", uriUnescape("hello%2c%20%2fworld")); EXPECT_EQ("hello,+/world", uriUnescape("hello%2c+%2fworld")); EXPECT_EQ( "hello, /world", uriUnescape("hello%2c+%2fworld", UriEscapeMode::QUERY)); EXPECT_EQ("hello/", uriUnescape("hello%2f")); EXPECT_EQ("hello/", uriUnescape("hello%2F")); EXPECT_THROW({ uriUnescape("hello%"); }, std::invalid_argument); EXPECT_THROW({ uriUnescape("hello%2"); }, std::invalid_argument); EXPECT_THROW( { uriUnescape("hello%2g"); }, std::invalid_argument); } namespace { void expectPrintable(StringPiece s) { for (char c : s) { EXPECT_LE(32, c); EXPECT_GE(127, c); } } } // namespace TEST(Escape, uriEscapeAllCombinations) { char c[3]; c[2] = '\0'; StringPiece in(c, 2); fbstring tmp; fbstring out; for (int i = 0; i < 256; ++i) { c[0] = i; for (int j = 0; j < 256; ++j) { c[1] = j; tmp.clear(); out.clear(); uriEscape(in, tmp); expectPrintable(tmp); uriUnescape(tmp, out); EXPECT_EQ(in, out); } } } namespace { bool isHex(int v) { return ( (v >= '0' && v <= '9') || (v >= 'A' && v <= 'F') || (v >= 'a' && v <= 'f')); } } // namespace TEST(Escape, uriUnescapePercentDecoding) { char c[4] = {'%', '\0', '\0', '\0'}; StringPiece in(c, 3); fbstring out; unsigned int expected = 0; for (int i = 0; i < 256; ++i) { c[1] = i; for (int j = 0; j < 256; ++j) { c[2] = j; if (isHex(i) && isHex(j)) { out.clear(); uriUnescape(in, out); EXPECT_EQ(1, out.size()); EXPECT_EQ(1, sscanf(c + 1, "%x", &expected)); unsigned char v = out[0]; EXPECT_EQ(expected, v); } else { EXPECT_THROW({ uriUnescape(in, out); }, std::invalid_argument); } } } } namespace { double pow2(int exponent) { return double(int64_t(1) << exponent); } } // namespace struct PrettyTestCase { std::string prettyString; double realValue; PrettyType prettyType; }; PrettyTestCase prettyTestCases[] = { {string("853 ms"), 85.3e-2, PRETTY_TIME_HMS}, {string("8.53 s "), 85.3e-1, PRETTY_TIME_HMS}, {string("1.422 m "), 85.3, PRETTY_TIME_HMS}, {string("14.22 m "), 85.3e1, PRETTY_TIME_HMS}, {string("2.369 h "), 85.3e2, PRETTY_TIME_HMS}, {string("2.369e+04 h "), 85.3e6, PRETTY_TIME_HMS}, {string("8.53e+07 s "), 85.3e6, PRETTY_TIME}, {string("8.53e+07 s "), 85.3e6, PRETTY_TIME}, {string("85.3 ms"), 85.3e-3, PRETTY_TIME}, {string("85.3 us"), 85.3e-6, PRETTY_TIME}, {string("85.3 ns"), 85.3e-9, PRETTY_TIME}, {string("85.3 ps"), 85.3e-12, PRETTY_TIME}, {string("8.53e-14 s "), 85.3e-15, PRETTY_TIME}, {string("0 s "), 0, PRETTY_TIME}, {string("1 s "), 1.0, PRETTY_TIME}, {string("1 ms"), 1.0e-3, PRETTY_TIME}, {string("1 us"), 1.0e-6, PRETTY_TIME}, {string("1 ns"), 1.0e-9, PRETTY_TIME}, {string("1 ps"), 1.0e-12, PRETTY_TIME}, // check bytes printing {string("853 B "), 853., PRETTY_BYTES}, {string("833 kB"), 853.e3, PRETTY_BYTES}, {string("813.5 MB"), 853.e6, PRETTY_BYTES}, {string("7.944 GB"), 8.53e9, PRETTY_BYTES}, {string("794.4 GB"), 853.e9, PRETTY_BYTES}, {string("775.8 TB"), 853.e12, PRETTY_BYTES}, {string("0 B "), 0, PRETTY_BYTES}, {string("1 B "), pow2(0), PRETTY_BYTES}, {string("1 kB"), pow2(10), PRETTY_BYTES}, {string("1 MB"), pow2(20), PRETTY_BYTES}, {string("1 GB"), pow2(30), PRETTY_BYTES}, {string("1 TB"), pow2(40), PRETTY_BYTES}, {string("1 PB"), pow2(50), PRETTY_BYTES}, {string("1 EB"), pow2(60), PRETTY_BYTES}, {string("853 B "), 853., PRETTY_BYTES_IEC}, {string("833 KiB"), 853.e3, PRETTY_BYTES_IEC}, {string("813.5 MiB"), 853.e6, PRETTY_BYTES_IEC}, {string("7.944 GiB"), 8.53e9, PRETTY_BYTES_IEC}, {string("794.4 GiB"), 853.e9, PRETTY_BYTES_IEC}, {string("775.8 TiB"), 853.e12, PRETTY_BYTES_IEC}, {string("1.776 PiB"), 2e15, PRETTY_BYTES_IEC}, {string("1.735 EiB"), 2e18, PRETTY_BYTES_IEC}, {string("0 B "), 0, PRETTY_BYTES_IEC}, {string("1 B "), pow2(0), PRETTY_BYTES_IEC}, {string("1 KiB"), pow2(10), PRETTY_BYTES_IEC}, {string("1 MiB"), pow2(20), PRETTY_BYTES_IEC}, {string("1 GiB"), pow2(30), PRETTY_BYTES_IEC}, {string("1 TiB"), pow2(40), PRETTY_BYTES_IEC}, {string("1 PiB"), pow2(50), PRETTY_BYTES_IEC}, {string("1 EiB"), pow2(60), PRETTY_BYTES_IEC}, // check bytes metric printing {string("853 B "), 853., PRETTY_BYTES_METRIC}, {string("853 kB"), 853.e3, PRETTY_BYTES_METRIC}, {string("853 MB"), 853.e6, PRETTY_BYTES_METRIC}, {string("8.53 GB"), 8.53e9, PRETTY_BYTES_METRIC}, {string("853 GB"), 853.e9, PRETTY_BYTES_METRIC}, {string("853 TB"), 853.e12, PRETTY_BYTES_METRIC}, {string("0 B "), 0, PRETTY_BYTES_METRIC}, {string("1 B "), 1.0, PRETTY_BYTES_METRIC}, {string("1 kB"), 1.0e+3, PRETTY_BYTES_METRIC}, {string("1 MB"), 1.0e+6, PRETTY_BYTES_METRIC}, {string("1 GB"), 1.0e+9, PRETTY_BYTES_METRIC}, {string("1 TB"), 1.0e+12, PRETTY_BYTES_METRIC}, {string("1 PB"), 1.0e+15, PRETTY_BYTES_METRIC}, {string("1 EB"), 1.0e+18, PRETTY_BYTES_METRIC}, // check metric-units (powers of 1000) printing {string("853 "), 853., PRETTY_UNITS_METRIC}, {string("853 k"), 853.e3, PRETTY_UNITS_METRIC}, {string("853 M"), 853.e6, PRETTY_UNITS_METRIC}, {string("8.53 bil"), 8.53e9, PRETTY_UNITS_METRIC}, {string("853 bil"), 853.e9, PRETTY_UNITS_METRIC}, {string("853 tril"), 853.e12, PRETTY_UNITS_METRIC}, // check binary-units (powers of 1024) printing {string("0 "), 0, PRETTY_UNITS_BINARY}, {string("1 "), pow2(0), PRETTY_UNITS_BINARY}, {string("1 k"), pow2(10), PRETTY_UNITS_BINARY}, {string("1 M"), pow2(20), PRETTY_UNITS_BINARY}, {string("1 G"), pow2(30), PRETTY_UNITS_BINARY}, {string("1 T"), pow2(40), PRETTY_UNITS_BINARY}, {string("1023 "), pow2(10) - 1, PRETTY_UNITS_BINARY}, {string("1024 k"), pow2(20) - 1, PRETTY_UNITS_BINARY}, {string("1024 M"), pow2(30) - 1, PRETTY_UNITS_BINARY}, {string("1024 G"), pow2(40) - 1, PRETTY_UNITS_BINARY}, {string("0 "), 0, PRETTY_UNITS_BINARY_IEC}, {string("1 "), pow2(0), PRETTY_UNITS_BINARY_IEC}, {string("1 Ki"), pow2(10), PRETTY_UNITS_BINARY_IEC}, {string("1 Mi"), pow2(20), PRETTY_UNITS_BINARY_IEC}, {string("1 Gi"), pow2(30), PRETTY_UNITS_BINARY_IEC}, {string("1 Ti"), pow2(40), PRETTY_UNITS_BINARY_IEC}, {string("1023 "), pow2(10) - 1, PRETTY_UNITS_BINARY_IEC}, {string("1024 Ki"), pow2(20) - 1, PRETTY_UNITS_BINARY_IEC}, {string("1024 Mi"), pow2(30) - 1, PRETTY_UNITS_BINARY_IEC}, {string("1024 Gi"), pow2(40) - 1, PRETTY_UNITS_BINARY_IEC}, // check border SI cases {string("1 Y"), 1e24, PRETTY_SI}, {string("10 Y"), 1e25, PRETTY_SI}, {string("1 y"), 1e-24, PRETTY_SI}, {string("10 y"), 1e-23, PRETTY_SI}, // check that negative values work {string("-85.3 s "), -85.3, PRETTY_TIME}, {string("-85.3 ms"), -85.3e-3, PRETTY_TIME}, {string("-85.3 us"), -85.3e-6, PRETTY_TIME}, {string("-85.3 ns"), -85.3e-9, PRETTY_TIME}, // end of test {string("endoftest"), 0, PRETTY_NUM_TYPES}, }; TEST(PrettyPrint, Basic) { for (int i = 0; prettyTestCases[i].prettyType != PRETTY_NUM_TYPES; ++i) { const PrettyTestCase& prettyTest = prettyTestCases[i]; EXPECT_EQ( prettyTest.prettyString, prettyPrint(prettyTest.realValue, prettyTest.prettyType)); } } TEST(PrettyToDouble, Basic) { // check manually created tests for (int i = 0; prettyTestCases[i].prettyType != PRETTY_NUM_TYPES; ++i) { PrettyTestCase testCase = prettyTestCases[i]; PrettyType formatType = testCase.prettyType; double x = testCase.realValue; std::string testString = testCase.prettyString; double recoveredX = 0; try { recoveredX = prettyToDouble(testString, formatType); } catch (const std::exception& ex) { ADD_FAILURE() << testCase.prettyString << " -> " << ex.what(); } double relativeError = fabs(x) < 1e-5 ? (x - recoveredX) : (x - recoveredX) / x; EXPECT_NEAR(0, relativeError, 1e-3); } // checks for compatibility with prettyPrint over the whole parameter space for (int i = 0; i < PRETTY_NUM_TYPES; ++i) { PrettyType formatType = static_cast(i); for (double x = 1e-18; x < 1e40; x *= 1.9) { bool addSpace = static_cast(i) == PRETTY_SI; for (int it = 0; it < 2; ++it, addSpace = true) { double recoveredX = 0; try { recoveredX = prettyToDouble(prettyPrint(x, formatType, addSpace), formatType); } catch (const std::exception& ex) { ADD_FAILURE() << folly::exceptionStr(ex); } double relativeError = (x - recoveredX) / x; EXPECT_NEAR(0, relativeError, 1e-3); } } } // check for incorrect values EXPECT_THROW(prettyToDouble("10Mx", PRETTY_SI), std::range_error); EXPECT_THROW(prettyToDouble("10 Mx", PRETTY_SI), std::range_error); EXPECT_THROW(prettyToDouble("10 M x", PRETTY_SI), std::range_error); StringPiece testString = "10Mx"; EXPECT_DOUBLE_EQ(prettyToDouble(&testString, PRETTY_UNITS_METRIC), 10e6); EXPECT_EQ(testString, "x"); } TEST(PrettyPrint, HexDump) { std::string a("abc\x00\x02\xa0", 6); // embedded NUL EXPECT_EQ( "00000000 61 62 63 00 02 a0 " "|abc... |\n", hexDump(a.data(), a.size())); a = "abcdefghijklmnopqrstuvwxyz"; EXPECT_EQ( "00000000 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 " "|abcdefghijklmnop|\n" "00000010 71 72 73 74 75 76 77 78 79 7a " "|qrstuvwxyz |\n", hexDump(a.data(), a.size())); } TEST(System, errnoStr) { errno = EACCES; EXPECT_EQ(EACCES, errno); EXPECT_EQ(EACCES, errno); // twice to make sure EXPECT_EQ doesn't change it fbstring expected = strerror(ENOENT); errno = EACCES; EXPECT_EQ(expected, errnoStr(ENOENT)); // Ensure that errno isn't changed EXPECT_EQ(EACCES, errno); // Per POSIX, all errno values are positive, so -1 is invalid errnoStr(-1); // Ensure that errno isn't changed EXPECT_EQ(EACCES, errno); } namespace { template