/*
- * Copyright 2015 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.
*/
#include <folly/Format.h>
-
-#include <gflags/gflags.h>
-#include <gtest/gtest.h>
+#include <folly/Utility.h>
+#include <folly/portability/GTest.h>
#include <string>
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<int> v1 {10, 20, 30};
EXPECT_EQ("0020", sformat("{0[1]:04}", v1));
EXPECT_EQ("0020", svformat("{1:04}", v1));
EXPECT_EQ("0042", sformat("{0[3]:04}", defaulted(v2, 42)));
EXPECT_EQ("0042", svformat("{3:04}", defaulted(v2, 42)));
- const int p[] = {10, 20, 30};
- const int* q = p;
- EXPECT_EQ("0020", sformat("{0[1]:04}", p));
- EXPECT_EQ("0020", svformat("{1:04}", p));
- EXPECT_EQ("0020", sformat("{0[1]:04}", q));
- EXPECT_EQ("0020", svformat("{1:04}", q));
- EXPECT_NE("", sformat("{}", q));
-
- EXPECT_EQ("0x", sformat("{}", p).substr(0, 2));
- EXPECT_EQ("10", svformat("{}", p));
- EXPECT_EQ("0x", sformat("{}", q).substr(0, 2));
- EXPECT_EQ("10", svformat("{}", q));
- q = nullptr;
- EXPECT_EQ("(null)", sformat("{}", q));
+ {
+ const int p[] = { 10, 20, 30 };
+ const int* q = p;
+ EXPECT_EQ("0020", sformat("{0[1]:04}", p));
+ EXPECT_EQ("0020", svformat("{1:04}", p));
+ EXPECT_EQ("0020", sformat("{0[1]:04}", q));
+ EXPECT_EQ("0020", svformat("{1:04}", q));
+ EXPECT_NE("", sformat("{}", q));
+
+ EXPECT_EQ("0x", sformat("{}", p).substr(0, 2));
+ EXPECT_EQ("10", svformat("{}", p));
+ EXPECT_EQ("0x", sformat("{}", q).substr(0, 2));
+ EXPECT_EQ("10", svformat("{}", q));
+ q = nullptr;
+ EXPECT_EQ("(null)", sformat("{}", q));
+ }
std::map<int, std::string> m { {10, "hello"}, {20, "world"} };
EXPECT_EQ("worldXX", sformat("{[20]:X<7}", m));
}
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));
const KeyValue& kv_;
};
-} // namespace
+} // namespace folly
TEST(Format, Custom) {
KeyValue kv { "hello", 42 };
}
TEST(Format, BogusFormatString) {
- // format() will crash the program if the format string is invalid.
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("{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");
+ 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<int> 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_THROW_STR(sformat("{0[test}"), std::range_error,
- "Non-whitespace: [");
+ EXPECT_THROW_STR(sformat("{0[test}"), std::range_error, "Non-whitespace");
}
template <bool containerMode, class... Args>
auto appender = [&result](StringPiece s) {
result.append(s.data(), s.size());
};
- std::get<K>(this->values_).format(arg, appender);
+ this->template getFormatValue<K>().format(arg, appender);
result = sformat("{{{}}}", result);
cb(StringPiece(result));
}
"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();
+TEST(Format, Temporary) {
+ constexpr StringPiece kStr = "A long string that should go on the heap";
+ auto fmt = format("{}", kStr.str()); // Pass a temporary std::string.
+ EXPECT_EQ(fmt.str(), kStr);
+ // The formatter can be reused.
+ EXPECT_EQ(fmt.str(), kStr);
+}
+
+namespace {
+
+struct NoncopyableInt : MoveOnly {
+ explicit NoncopyableInt(int v) : value(v) {}
+ int value;
+};
+
+} // namespace
+
+namespace folly {
+
+template <>
+class FormatValue<NoncopyableInt> {
+ public:
+ explicit FormatValue(const NoncopyableInt& v) : v_(v) {}
+
+ template <class FormatCallback>
+ void format(FormatArg& arg, FormatCallback& cb) const {
+ FormatValue<int>(v_.value).format(arg, cb);
+ }
+
+ private:
+ const NoncopyableInt& v_;
+};
+
+} // namespace
+
+TEST(Format, NoncopyableArg) {
+ {
+ // Test that lvalues are held by reference.
+ NoncopyableInt v(1);
+ auto fmt = format("{}", v);
+ EXPECT_EQ(fmt.str(), "1");
+ // The formatter can be reused.
+ EXPECT_EQ(fmt.str(), "1");
+ }
+
+ {
+ // Test that rvalues are moved.
+ auto fmt = format("{}", NoncopyableInt(1));
+ EXPECT_EQ(fmt.str(), "1");
+ }
}