X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ftest%2FConvTest.cpp;h=0f189590d92e6a20e0b70af94cc7fa64b6840e91;hb=94b8816bbdca38914030842554cc955caee59063;hp=a14eeb2801ef510f3ef4527f825347128e59f698;hpb=47868e4911bee74582d605195525bafa889252d9;p=folly.git diff --git a/folly/test/ConvTest.cpp b/folly/test/ConvTest.cpp index a14eeb28..0f189590 100644 --- a/folly/test/ConvTest.cpp +++ b/folly/test/ConvTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,22 @@ * limitations under the License. */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif + +#include + #include #include -#include -#include +#include + +#include +#include #include +#include #include +#include using namespace std; using namespace folly; @@ -81,7 +91,7 @@ TEST(Conv, Type2Type) { int intV = 42; EXPECT_EQ(to(intV), 42); - float floatV = 4.2; + float floatV = 4.2f; EXPECT_EQ(to(floatV), 4.2f); double doubleV = 0.42; @@ -117,7 +127,7 @@ TEST(Conv, Integral2Integral) { } TEST(Conv, Floating2Floating) { - float f1 = 1e3; + float f1 = 1e3f; double d1 = to(f1); EXPECT_EQ(f1, d1); @@ -137,7 +147,7 @@ TEST(Conv, Floating2Floating) { EXPECT_TRUE(shouldWork == std::numeric_limits::min() || shouldWork == 0.f); } catch (...) { - EXPECT_TRUE(false); + ADD_FAILURE(); } } @@ -197,6 +207,15 @@ void test128Bit2String() { svalue = 0; EXPECT_EQ(to(svalue), "0"); + value = ~__int128(0); + EXPECT_EQ(to(value), "340282366920938463463374607431768211455"); + + svalue = -(Uint(1) << 127); + EXPECT_EQ(to(svalue), "-170141183460469231731687303715884105728"); + + svalue = (Uint(1) << 127) - 1; + EXPECT_EQ(to(svalue), "170141183460469231731687303715884105727"); + // TODO: the following do not compile to<__int128> ... #if 0 @@ -215,8 +234,8 @@ void test128Bit2String() { #endif TEST(Conv, Integral2String) { - testIntegral2String(); - testIntegral2String(); + testIntegral2String(); + testIntegral2String(); #if FOLLY_HAVE_INT128_T test128Bit2String(); @@ -412,9 +431,9 @@ void testString2Integral() { } TEST(Conv, String2Integral) { - testString2Integral(); - testString2Integral(); - testString2Integral(); + testString2Integral(); + testString2Integral(); + testString2Integral(); // Testing the behavior of the StringPiece* API // StringPiece* normally parses as much valid data as it can, @@ -554,27 +573,62 @@ TEST(Conv, FBStringToString) { } TEST(Conv, StringPieceToDouble) { - string s = "2134123.125 zorro"; - StringPiece pc(s); - EXPECT_EQ(to(&pc), 2134123.125); - EXPECT_EQ(pc, " zorro"); - - EXPECT_THROW(to(StringPiece(s)), std::range_error); - EXPECT_EQ(to(StringPiece(s.data(), pc.data())), 2134123.125); + vector> strs{ + make_tuple("2134123.125 zorro", " zorro", 2134123.125), + make_tuple(" 2134123.125 zorro", " zorro", 2134123.125), + make_tuple(" 2134123.125 zorro", " zorro", 2134123.125), + make_tuple(" 2134123.125 zorro ", " zorro ", 2134123.125), + make_tuple("2134123.125zorro", "zorro", 2134123.125), + make_tuple("0 zorro", " zorro", 0.0), + make_tuple(" 0 zorro", " zorro", 0.0), + make_tuple(" 0 zorro", " zorro", 0.0), + make_tuple(" 0 zorro ", " zorro ", 0.0), + make_tuple("0zorro", "zorro", 0.0), + make_tuple("0.0 zorro", " zorro", 0.0), + make_tuple(" 0.0 zorro", " zorro", 0.0), + make_tuple(" 0.0 zorro", " zorro", 0.0), + make_tuple(" 0.0 zorro ", " zorro ", 0.0), + make_tuple("0.0zorro", "zorro", 0.0), + }; + for (const auto& s : strs) { + StringPiece pc(get<0>(s)); + EXPECT_EQ(get<2>(s), to(&pc)) << "\"" << get<0>(s) << "\""; + EXPECT_EQ(get<1>(s), pc); + EXPECT_THROW(to(StringPiece(get<0>(s))), std::range_error); + EXPECT_EQ(get<2>(s), to(StringPiece(get<0>(s), pc.data()))); + } -// Test NaN conversion + // Test NaN conversion try { to("not a number"); - EXPECT_TRUE(false); + ADD_FAILURE(); } catch (const std::range_error &) { } + EXPECT_TRUE(std::isnan(to("nan"))); EXPECT_TRUE(std::isnan(to("NaN"))); + EXPECT_TRUE(std::isnan(to("NAN"))); + EXPECT_TRUE(std::isnan(to("-nan"))); + EXPECT_TRUE(std::isnan(to("-NaN"))); + EXPECT_TRUE(std::isnan(to("-NAN"))); + EXPECT_EQ(to("inf"), numeric_limits::infinity()); + EXPECT_EQ(to("Inf"), numeric_limits::infinity()); + EXPECT_EQ(to("INF"), numeric_limits::infinity()); + EXPECT_EQ(to("inF"), numeric_limits::infinity()); EXPECT_EQ(to("infinity"), numeric_limits::infinity()); + EXPECT_EQ(to("Infinity"), numeric_limits::infinity()); + EXPECT_EQ(to("INFINITY"), numeric_limits::infinity()); + EXPECT_EQ(to("iNfInItY"), numeric_limits::infinity()); EXPECT_THROW(to("infinitX"), std::range_error); EXPECT_EQ(to("-inf"), -numeric_limits::infinity()); + EXPECT_EQ(to("-Inf"), -numeric_limits::infinity()); + EXPECT_EQ(to("-INF"), -numeric_limits::infinity()); + EXPECT_EQ(to("-inF"), -numeric_limits::infinity()); EXPECT_EQ(to("-infinity"), -numeric_limits::infinity()); + EXPECT_EQ(to("-Infinity"), -numeric_limits::infinity()); + EXPECT_EQ(to("-INFINITY"), -numeric_limits::infinity()); + EXPECT_EQ(to("-iNfInItY"), -numeric_limits::infinity()); EXPECT_THROW(to("-infinitX"), std::range_error); } @@ -584,7 +638,7 @@ TEST(Conv, EmptyStringToInt) { try { to(pc); - EXPECT_TRUE(false); + ADD_FAILURE(); } catch (const std::range_error &) { } } @@ -595,7 +649,7 @@ TEST(Conv, CorruptedStringToInt) { try { to(&pc); - EXPECT_TRUE(false); + ADD_FAILURE(); } catch (const std::range_error &) { } } @@ -606,7 +660,7 @@ TEST(Conv, EmptyStringToDouble) { try { to(pc); - EXPECT_TRUE(false); + ADD_FAILURE(); } catch (const std::range_error &) { } } @@ -617,7 +671,7 @@ TEST(Conv, IntToDouble) { /* This seems not work in ubuntu11.10, gcc 4.6.1 try { auto f = to(957837589847); - EXPECT_TRUE(false); + ADD_FAILURE(); } catch (std::range_error& e) { //LOG(INFO) << e.what(); } @@ -628,10 +682,10 @@ TEST(Conv, DoubleToInt) { auto i = to(42.0); EXPECT_EQ(i, 42); try { - auto i = to(42.1); - LOG(ERROR) << "to returned " << i << " instead of throwing"; - EXPECT_TRUE(false); - } catch (std::range_error& e) { + auto i2 = to(42.1); + LOG(ERROR) << "to returned " << i2 << " instead of throwing"; + ADD_FAILURE(); + } catch (std::range_error&) { //LOG(INFO) << e.what(); } } @@ -643,12 +697,12 @@ TEST(Conv, EnumToInt) { auto j = to(x); EXPECT_EQ(j, 42); try { - auto i = to(y); + auto i2 = to(y); LOG(ERROR) << "to returned " - << static_cast(i) + << static_cast(i2) << " instead of throwing"; - EXPECT_TRUE(false); - } catch (std::range_error& e) { + ADD_FAILURE(); + } catch (std::range_error&) { //LOG(INFO) << e.what(); } } @@ -668,12 +722,12 @@ TEST(Conv, IntToEnum) { auto j = to(100); EXPECT_EQ(j, 100); try { - auto i = to(5000000000L); + auto i2 = to(5000000000L); LOG(ERROR) << "to returned " - << static_cast(i) + << static_cast(i2) << " instead of throwing"; - EXPECT_TRUE(false); - } catch (std::range_error& e) { + ADD_FAILURE(); + } catch (std::range_error&) { //LOG(INFO) << e.what(); } } @@ -689,8 +743,8 @@ TEST(Conv, UnsignedEnum) { try { auto i = to(x); LOG(ERROR) << "to returned " << i << " instead of throwing"; - EXPECT_TRUE(false); - } catch (std::range_error& e) { + ADD_FAILURE(); + } catch (std::range_error&) { } } @@ -703,7 +757,7 @@ TEST(Conv, UnsignedEnumClass) { EXPECT_EQ(E::x, to(3000000000U)); EXPECT_EQ(E::x, to("3000000000")); E e; - parseTo("3000000000", e); + EXPECT_TRUE(parseTo("3000000000", e).hasValue()); EXPECT_EQ(E::x, e); EXPECT_THROW(to(E::x), std::range_error); } @@ -728,7 +782,7 @@ TEST(Conv, IntegralToBool) { EXPECT_TRUE(to(42ul)); } -template +template void testStr2Bool() { EXPECT_FALSE(to(Src("0"))); EXPECT_FALSE(to(Src(" 000 "))); @@ -792,6 +846,14 @@ TEST(Conv, StringToBool) { EXPECT_EQ(buf5, sp5.begin()); } +TEST(Conv, Transform) { + const std::vector in{1, 2, 3}; + std::vector out(in.size()); + std::transform(in.begin(), in.end(), out.begin(), to); + const std::vector ref{"1", "2", "3"}; + EXPECT_EQ(ref, out); +} + TEST(Conv, FloatToInt) { EXPECT_EQ(to(42.0f), 42); EXPECT_EQ(to(-128.0f), int8_t(-128)); @@ -823,6 +885,278 @@ TEST(Conv, IntToFloat) { #endif } +TEST(Conv, BoolToFloat) { + EXPECT_EQ(to(true), 1.0); + EXPECT_EQ(to(false), 0.0); +} + +TEST(Conv, FloatToBool) { + EXPECT_EQ(to(1.0), true); + EXPECT_EQ(to(0.0), false); + EXPECT_EQ(to(2.7), true); + EXPECT_EQ(to(std::numeric_limits::max()), true); + EXPECT_EQ(to(std::numeric_limits::min()), true); + EXPECT_EQ(to(std::numeric_limits::lowest()), true); + EXPECT_EQ(to(std::numeric_limits::quiet_NaN()), true); + EXPECT_EQ(to(std::numeric_limits::infinity()), true); + EXPECT_EQ(to(-std::numeric_limits::infinity()), true); +} + +namespace { + +template +void testConvError( + F&& expr, + const char* exprStr, + ConversionCode code, + const char* value, + bool quotedValue, + int line) { + std::string where = to(__FILE__, "(", line, "): "); + try { + auto res = expr(); + ADD_FAILURE() << where << exprStr << " -> " << res; + } catch (const ConversionError& e) { + EXPECT_EQ(code, e.errorCode()) << where << exprStr; + std::string str(e.what()); + EXPECT_FALSE(str.empty()) << where << exprStr << " -> " << str; + auto pos = str.find(':'); + if (value) { + std::ostringstream exp; + exp << str.substr(0, pos) + ": "; + if (quotedValue) { + exp << "\"" << value << "\""; + } else { + exp << value; + } + EXPECT_EQ(exp.str(), str) << where << exprStr << " -> " << str; + } else { + EXPECT_EQ(pos, std::string::npos) << where << exprStr << " -> " << str; + } + } +} +} + +#define EXPECT_CONV_ERROR_QUOTE(expr, code, value, quoted) \ + testConvError( \ + [&] { return expr; }, \ + #expr, \ + ConversionCode::code, \ + value, \ + quoted, \ + __LINE__) + +#define EXPECT_CONV_ERROR(expr, code, value) \ + EXPECT_CONV_ERROR_QUOTE(expr, code, value, true) + +#define EXPECT_CONV_ERROR_STR(type, str, code) \ + EXPECT_CONV_ERROR(to(str), code, str) + +#define EXPECT_CONV_ERROR_STR_NOVAL(type, str, code) \ + EXPECT_CONV_ERROR(to(str), code, nullptr) + +TEST(Conv, ConversionErrorStrToBool) { + EXPECT_CONV_ERROR_STR_NOVAL(bool, StringPiece(), EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR_NOVAL(bool, "", EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR(bool, " ", EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR(bool, " 11 ", BOOL_OVERFLOW); + EXPECT_CONV_ERROR_STR(bool, "other ", BOOL_INVALID_VALUE); + EXPECT_CONV_ERROR_STR(bool, " bla", BOOL_INVALID_VALUE); + EXPECT_CONV_ERROR(to(" offbla"), NON_WHITESPACE_AFTER_END, "bla"); +} + +TEST(Conv, ConversionErrorStrToFloat) { + EXPECT_CONV_ERROR_STR_NOVAL(float, StringPiece(), EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR_NOVAL(float, "", EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR(float, " ", EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR(float, " junk", STRING_TO_FLOAT_ERROR); + EXPECT_CONV_ERROR(to(" 1bla"), NON_WHITESPACE_AFTER_END, "bla"); +} + +TEST(Conv, ConversionErrorStrToInt) { + // empty string handling + EXPECT_CONV_ERROR_STR_NOVAL(int, StringPiece(), EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR_NOVAL(int, "", EMPTY_INPUT_STRING); + EXPECT_CONV_ERROR_STR(int, " ", EMPTY_INPUT_STRING); + + // signed integers + EXPECT_CONV_ERROR_STR(int, " *", INVALID_LEADING_CHAR); + EXPECT_CONV_ERROR_STR(int, " +", NO_DIGITS); + EXPECT_CONV_ERROR_STR(int, " +*", NON_DIGIT_CHAR); + EXPECT_CONV_ERROR_STR(int8_t, " 128", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_STR(int8_t, " -129", NEGATIVE_OVERFLOW); + EXPECT_CONV_ERROR_STR(int8_t, " 1000", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_STR(int8_t, "-1000", NEGATIVE_OVERFLOW); + EXPECT_CONV_ERROR(to(" -13bla"), NON_WHITESPACE_AFTER_END, "bla"); + + // unsigned integers + EXPECT_CONV_ERROR_STR(unsigned, " -", NON_DIGIT_CHAR); + EXPECT_CONV_ERROR_STR(uint8_t, " 256", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR(to("42bla"), NON_WHITESPACE_AFTER_END, "bla"); +} + +#define EXPECT_CONV_ERROR_PP_VAL(type, str, code, val) \ + do { \ + StringPiece input(str); \ + EXPECT_CONV_ERROR(to(input.begin(), input.end()), code, val); \ + } while (0) + +#define EXPECT_CONV_ERROR_PP(type, str, code) \ + EXPECT_CONV_ERROR_PP_VAL(type, str, code, str) + +TEST(Conv, ConversionErrorPtrPairToInt) { + // signed integers + EXPECT_CONV_ERROR_PP(int, "", INVALID_LEADING_CHAR); + EXPECT_CONV_ERROR_PP(int, " ", INVALID_LEADING_CHAR); + EXPECT_CONV_ERROR_PP(int, "*", INVALID_LEADING_CHAR); + EXPECT_CONV_ERROR_PP(int, "+", NO_DIGITS); + EXPECT_CONV_ERROR_PP(int8_t, "128", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_PP(int8_t, "-129", NEGATIVE_OVERFLOW); + EXPECT_CONV_ERROR_PP(int8_t, "1000", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_PP(int8_t, "-1000", NEGATIVE_OVERFLOW); + EXPECT_CONV_ERROR_PP(int, "-junk", NON_DIGIT_CHAR); + + // unsigned integers + EXPECT_CONV_ERROR_PP(unsigned, "", NO_DIGITS); + EXPECT_CONV_ERROR_PP(uint8_t, "256", POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_PP(unsigned, "junk", NON_DIGIT_CHAR); +} + +namespace { + +template +std::string prefixWithType(V value) { + std::ostringstream oss; +#ifdef FOLLY_HAS_RTTI + oss << "(" << demangle(typeid(T)) << ") "; +#endif + oss << to(value); + return oss.str(); +} +} + +#define EXPECT_CONV_ERROR_ARITH(type, val, code) \ + EXPECT_CONV_ERROR_QUOTE( \ + to(val), code, prefixWithType(val).c_str(), false) + +TEST(Conv, ConversionErrorIntToInt) { + EXPECT_CONV_ERROR_ARITH(signed char, 128, ARITH_POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_ARITH(unsigned char, -1, ARITH_NEGATIVE_OVERFLOW); +} + +TEST(Conv, ConversionErrorFloatToFloat) { + EXPECT_CONV_ERROR_ARITH( + float, std::numeric_limits::max(), ARITH_POSITIVE_OVERFLOW); + EXPECT_CONV_ERROR_ARITH( + float, std::numeric_limits::lowest(), ARITH_NEGATIVE_OVERFLOW); +} + +TEST(Conv, ConversionErrorIntToFloat) { + EXPECT_CONV_ERROR_ARITH( + float, std::numeric_limits::max(), ARITH_LOSS_OF_PRECISION); +} + +TEST(Conv, ConversionErrorFloatToInt) { + EXPECT_CONV_ERROR_ARITH(int8_t, 65.5, ARITH_LOSS_OF_PRECISION); +} + +TEST(Conv, TryStringToBool) { + auto rv1 = folly::tryTo("xxxx"); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo("false"); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_FALSE(rv2.value()); + auto rv3 = folly::tryTo("yes"); + EXPECT_TRUE(rv3.hasValue()); + EXPECT_TRUE(rv3.value()); +} + +TEST(Conv, TryStringToInt) { + auto rv1 = folly::tryTo("1000000000000000000000000000000"); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo("4711"); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_EQ(rv2.value(), 4711); +} + +TEST(Conv, TryStringToEnum) { + enum class A { x = 42, y = 420, z = 65 }; + auto rv1 = folly::tryTo("1000000000000000000000000000000"); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo("42"); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_EQ(A::x, rv2.value()); + auto rv3 = folly::tryTo("50"); + EXPECT_TRUE(rv3.hasValue()); + EXPECT_EQ(static_cast(50), rv3.value()); +} + +TEST(Conv, TryStringToFloat) { + auto rv1 = folly::tryTo(""); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo("3.14"); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_NEAR(rv2.value(), 3.14, 1e-5); +} + +TEST(Conv, TryStringToDouble) { + auto rv1 = folly::tryTo(""); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo("3.14"); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_NEAR(rv2.value(), 3.14, 1e-10); +} + +TEST(Conv, TryIntToInt) { + auto rv1 = folly::tryTo(256); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo(255); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_EQ(rv2.value(), 255); +} + +TEST(Conv, TryFloatToFloat) { + auto rv1 = folly::tryTo(1e100); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo(25.5f); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_NEAR(rv2.value(), 25.5, 1e-10); +} + +TEST(Conv, TryFloatToInt) { + auto rv1 = folly::tryTo(100.001); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo(100.0); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_EQ(rv2.value(), 100); +} + +TEST(Conv, TryIntToFloat) { + auto rv1 = folly::tryTo(std::numeric_limits::max()); + EXPECT_FALSE(rv1.hasValue()); + auto rv2 = folly::tryTo(1000ULL); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_EQ(rv2.value(), 1000.0f); +} + +TEST(Conv, TryPtrPairToInt) { + StringPiece sp1("1000000000000000000000000000000"); + auto rv1 = folly::tryTo(sp1.begin(), sp1.end()); + EXPECT_FALSE(rv1.hasValue()); + StringPiece sp2("4711"); + auto rv2 = folly::tryTo(sp2.begin(), sp2.end()); + EXPECT_TRUE(rv2.hasValue()); + EXPECT_EQ(rv2.value(), 4711); + StringPiece sp3("-4711"); + auto rv3 = folly::tryTo(sp3.begin(), sp3.end()); + EXPECT_TRUE(rv3.hasValue()); + EXPECT_EQ(rv3.value(), -4711); + StringPiece sp4("4711"); + auto rv4 = folly::tryTo(sp4.begin(), sp4.end()); + EXPECT_TRUE(rv4.hasValue()); + EXPECT_EQ(rv4.value(), 4711); +} + TEST(Conv, NewUint64ToString) { char buf[21]; @@ -888,10 +1222,12 @@ struct Dimensions { } }; -void parseTo(folly::StringPiece in, Dimensions& out) { - out.w = folly::to(&in); - in.removePrefix("x"); - out.h = folly::to(&in); +Expected parseTo( + folly::StringPiece in, + Dimensions& out) { + return parseTo(in, out.w) + .then([](StringPiece sp) { return sp.removePrefix("x"), sp; }) + .then([&](StringPiece sp) { return parseTo(sp, out.h); }); } template @@ -914,3 +1250,10 @@ TEST(Conv, custom_kkproviders) { EXPECT_GT(str.capacity(), 2000); EXPECT_LT(str.capacity(), 2500); } + +TEST(Conv, TryToThenWithVoid) { + auto x = tryTo("42").then([](int) {}); + EXPECT_TRUE(x.hasValue()); + Unit u = x.value(); + (void)u; +}