+TEST(Conv, BoolToFloat) {
+ EXPECT_EQ(to<double>(true), 1.0);
+ EXPECT_EQ(to<double>(false), 0.0);
+}
+
+TEST(Conv, FloatToBool) {
+ EXPECT_EQ(to<bool>(1.0), true);
+ EXPECT_EQ(to<bool>(0.0), false);
+ EXPECT_EQ(to<bool>(2.7), true);
+ EXPECT_EQ(to<bool>(std::numeric_limits<double>::max()), true);
+ EXPECT_EQ(to<bool>(std::numeric_limits<double>::min()), true);
+ EXPECT_EQ(to<bool>(std::numeric_limits<double>::lowest()), true);
+ EXPECT_EQ(to<bool>(std::numeric_limits<double>::quiet_NaN()), true);
+ EXPECT_EQ(to<bool>(std::numeric_limits<double>::infinity()), true);
+ EXPECT_EQ(to<bool>(-std::numeric_limits<double>::infinity()), true);
+}
+
+namespace {
+
+template <typename F>
+void testConvError(
+ F&& expr,
+ const char* exprStr,
+ ConversionCode code,
+ const char* value,
+ bool quotedValue,
+ int line) {
+ std::string where = to<std::string>(__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<type>(str), code, str)
+
+#define EXPECT_CONV_ERROR_STR_NOVAL(type, str, code) \
+ EXPECT_CONV_ERROR(to<type>(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<bool>(" 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<float>(" 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<int>(" -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<unsigned>("42bla"), NON_WHITESPACE_AFTER_END, "bla");
+}
+
+#define EXPECT_CONV_ERROR_PP_VAL(type, str, code, val) \
+ do { \
+ StringPiece input(str); \
+ EXPECT_CONV_ERROR(to<type>(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 <typename T, typename V>
+std::string prefixWithType(V value) {
+ std::ostringstream oss;
+#ifdef FOLLY_HAS_RTTI
+ oss << "(" << demangle(typeid(T)) << ") ";
+#endif
+ oss << to<std::string>(value);
+ return oss.str();
+}
+}
+
+#define EXPECT_CONV_ERROR_ARITH(type, val, code) \
+ EXPECT_CONV_ERROR_QUOTE( \
+ to<type>(val), code, prefixWithType<type>(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<double>::max(), ARITH_POSITIVE_OVERFLOW);
+ EXPECT_CONV_ERROR_ARITH(
+ float, std::numeric_limits<double>::lowest(), ARITH_NEGATIVE_OVERFLOW);
+}
+
+TEST(Conv, ConversionErrorIntToFloat) {
+ EXPECT_CONV_ERROR_ARITH(
+ float, std::numeric_limits<long long>::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<bool>("xxxx");
+ EXPECT_FALSE(rv1.hasValue());
+ auto rv2 = folly::tryTo<bool>("false");
+ EXPECT_TRUE(rv2.hasValue());
+ EXPECT_FALSE(rv2.value());
+ auto rv3 = folly::tryTo<bool>("yes");
+ EXPECT_TRUE(rv3.hasValue());
+ EXPECT_TRUE(rv3.value());
+}
+
+TEST(Conv, TryStringToInt) {
+ auto rv1 = folly::tryTo<int>("1000000000000000000000000000000");
+ EXPECT_FALSE(rv1.hasValue());
+ auto rv2 = folly::tryTo<int>("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<A>("1000000000000000000000000000000");
+ EXPECT_FALSE(rv1.hasValue());
+ auto rv2 = folly::tryTo<A>("42");
+ EXPECT_TRUE(rv2.hasValue());
+ EXPECT_EQ(A::x, rv2.value());
+ auto rv3 = folly::tryTo<A>("50");
+ EXPECT_TRUE(rv3.hasValue());
+ EXPECT_EQ(static_cast<A>(50), rv3.value());
+}
+
+TEST(Conv, TryStringToFloat) {
+ auto rv1 = folly::tryTo<float>("");
+ EXPECT_FALSE(rv1.hasValue());
+ auto rv2 = folly::tryTo<float>("3.14");
+ EXPECT_TRUE(rv2.hasValue());
+ EXPECT_NEAR(rv2.value(), 3.14, 1e-5);
+}
+
+TEST(Conv, TryStringToDouble) {
+ auto rv1 = folly::tryTo<double>("");
+ EXPECT_FALSE(rv1.hasValue());
+ auto rv2 = folly::tryTo<double>("3.14");
+ EXPECT_TRUE(rv2.hasValue());
+ EXPECT_NEAR(rv2.value(), 3.14, 1e-10);
+}
+
+TEST(Conv, TryIntToInt) {
+ auto rv1 = folly::tryTo<uint8_t>(256);
+ EXPECT_FALSE(rv1.hasValue());
+ auto rv2 = folly::tryTo<uint8_t>(255);
+ EXPECT_TRUE(rv2.hasValue());
+ EXPECT_EQ(rv2.value(), 255);
+}
+
+TEST(Conv, TryFloatToFloat) {
+ auto rv1 = folly::tryTo<float>(1e100);
+ EXPECT_FALSE(rv1.hasValue());
+ auto rv2 = folly::tryTo<double>(25.5f);
+ EXPECT_TRUE(rv2.hasValue());
+ EXPECT_NEAR(rv2.value(), 25.5, 1e-10);
+}
+
+TEST(Conv, TryFloatToInt) {
+ auto rv1 = folly::tryTo<int>(100.001);
+ EXPECT_FALSE(rv1.hasValue());
+ auto rv2 = folly::tryTo<int>(100.0);
+ EXPECT_TRUE(rv2.hasValue());
+ EXPECT_EQ(rv2.value(), 100);
+}
+
+TEST(Conv, TryIntToFloat) {
+ auto rv1 = folly::tryTo<float>(std::numeric_limits<uint64_t>::max());
+ EXPECT_FALSE(rv1.hasValue());
+ auto rv2 = folly::tryTo<float>(1000ULL);
+ EXPECT_TRUE(rv2.hasValue());
+ EXPECT_EQ(rv2.value(), 1000.0f);
+}
+
+TEST(Conv, TryPtrPairToInt) {
+ StringPiece sp1("1000000000000000000000000000000");
+ auto rv1 = folly::tryTo<int>(sp1.begin(), sp1.end());
+ EXPECT_FALSE(rv1.hasValue());
+ StringPiece sp2("4711");
+ auto rv2 = folly::tryTo<int>(sp2.begin(), sp2.end());
+ EXPECT_TRUE(rv2.hasValue());
+ EXPECT_EQ(rv2.value(), 4711);
+ StringPiece sp3("-4711");
+ auto rv3 = folly::tryTo<int>(sp3.begin(), sp3.end());
+ EXPECT_TRUE(rv3.hasValue());
+ EXPECT_EQ(rv3.value(), -4711);
+ StringPiece sp4("4711");
+ auto rv4 = folly::tryTo<uint16_t>(sp4.begin(), sp4.end());
+ EXPECT_TRUE(rv4.hasValue());
+ EXPECT_EQ(rv4.value(), 4711);
+}
+