ostream-test.cc
1 // Formatting library for C++ - std::ostream support tests 2 // 3 // Copyright (c) 2012 - present, Victor Zverovich 4 // All rights reserved. 5 // 6 // For the license information refer to format.h. 7 8 #include <fstream> 9 10 #include "fmt/format.h" 11 12 using fmt::runtime; 13 14 struct test {}; 15 16 // Test that there is no issues with specializations when fmt/ostream.h is 17 // included after fmt/format.h. 18 namespace fmt { 19 template <> struct formatter<test> : formatter<int> { 20 auto format(const test&, format_context& ctx) -> decltype(ctx.out()) { 21 return formatter<int>::format(42, ctx); 22 } 23 }; 24 } // namespace fmt 25 26 #include <sstream> 27 28 #include "fmt/compile.h" 29 #include "fmt/ostream.h" 30 #include "fmt/ranges.h" 31 #include "gmock/gmock.h" 32 #include "gtest-extra.h" 33 #include "util.h" 34 35 auto operator<<(std::ostream& os, const date& d) -> std::ostream& { 36 os << d.year() << '-' << d.month() << '-' << d.day(); 37 return os; 38 } 39 40 auto operator<<(std::wostream& os, const date& d) -> std::wostream& { 41 os << d.year() << L'-' << d.month() << L'-' << d.day(); 42 return os; 43 } 44 45 // Make sure that overloaded comma operators do no harm to is_streamable. 46 struct type_with_comma_op {}; 47 template <typename T> void operator,(type_with_comma_op, const T&); 48 template <typename T> type_with_comma_op operator<<(T&, const date&); 49 50 enum streamable_enum {}; 51 52 auto operator<<(std::ostream& os, streamable_enum) -> std::ostream& { 53 return os << "streamable_enum"; 54 } 55 56 enum unstreamable_enum {}; 57 auto format_as(unstreamable_enum e) -> int { return e; } 58 59 struct empty_test {}; 60 auto operator<<(std::ostream& os, empty_test) -> std::ostream& { 61 return os << ""; 62 } 63 64 namespace fmt { 65 template <> struct formatter<test_string> : ostream_formatter {}; 66 template <> struct formatter<date> : ostream_formatter {}; 67 template <> struct formatter<streamable_enum> : ostream_formatter {}; 68 template <> struct formatter<empty_test> : ostream_formatter {}; 69 } // namespace fmt 70 71 TEST(ostream_test, enum) { 72 EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum())); 73 EXPECT_EQ("0", fmt::format("{}", unstreamable_enum())); 74 } 75 76 TEST(ostream_test, format) { 77 EXPECT_EQ("a string", fmt::format("{0}", test_string("a string"))); 78 EXPECT_EQ("The date is 2012-12-9", 79 fmt::format("The date is {0}", date(2012, 12, 9))); 80 } 81 82 TEST(ostream_test, format_specs) { 83 using fmt::format_error; 84 EXPECT_EQ("def ", fmt::format("{0:<5}", test_string("def"))); 85 EXPECT_EQ(" def", fmt::format("{0:>5}", test_string("def"))); 86 EXPECT_EQ(" def ", fmt::format("{0:^5}", test_string("def"))); 87 EXPECT_EQ("def**", fmt::format("{0:*<5}", test_string("def"))); 88 EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), test_string()), 89 format_error, "invalid format specifier"); 90 EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), test_string()), 91 format_error, "invalid format specifier"); 92 EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), test_string()), 93 format_error, "invalid format specifier"); 94 EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), test_string()), 95 format_error, "invalid format specifier"); 96 EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), test_string()), 97 format_error, "format specifier requires numeric argument"); 98 EXPECT_EQ("test ", fmt::format("{0:13}", test_string("test"))); 99 EXPECT_EQ("test ", fmt::format("{0:{1}}", test_string("test"), 13)); 100 EXPECT_EQ("te", fmt::format("{0:.2}", test_string("test"))); 101 EXPECT_EQ("te", fmt::format("{0:.{1}}", test_string("test"), 2)); 102 } 103 104 TEST(ostream_test, empty_custom_output) { 105 EXPECT_EQ("", fmt::format("{}", empty_test())); 106 } 107 108 TEST(ostream_test, print) { 109 { 110 std::ostringstream os; 111 fmt::print(os, "Don't {}!", "panic"); 112 EXPECT_EQ("Don't panic!", os.str()); 113 } 114 115 { 116 std::ostringstream os; 117 fmt::println(os, "Don't {}!", "panic"); 118 EXPECT_EQ("Don't panic!\n", os.str()); 119 } 120 } 121 122 TEST(ostream_test, write_to_ostream) { 123 std::ostringstream os; 124 fmt::memory_buffer buffer; 125 const char* foo = "foo"; 126 buffer.append(foo, foo + std::strlen(foo)); 127 fmt::detail::write_buffer(os, buffer); 128 EXPECT_EQ("foo", os.str()); 129 } 130 131 TEST(ostream_test, write_to_ostream_max_size) { 132 auto max_size = fmt::detail::max_value<size_t>(); 133 auto max_streamsize = fmt::detail::max_value<std::streamsize>(); 134 if (max_size <= fmt::detail::to_unsigned(max_streamsize)) return; 135 136 struct test_buffer final : fmt::detail::buffer<char> { 137 explicit test_buffer(size_t size) 138 : fmt::detail::buffer<char>(nullptr, size, size) {} 139 void grow(size_t) override {} 140 } buffer(max_size); 141 142 struct mock_streambuf : std::streambuf { 143 MOCK_METHOD(std::streamsize, xsputn, (const void*, std::streamsize)); 144 auto xsputn(const char* s, std::streamsize n) -> std::streamsize override { 145 const void* v = s; 146 return xsputn(v, n); 147 } 148 } streambuf; 149 150 struct test_ostream : std::ostream { 151 explicit test_ostream(mock_streambuf& output_buffer) 152 : std::ostream(&output_buffer) {} 153 } os(streambuf); 154 155 testing::InSequence sequence; 156 const char* data = nullptr; 157 using ustreamsize = std::make_unsigned<std::streamsize>::type; 158 ustreamsize size = max_size; 159 do { 160 auto n = std::min(size, fmt::detail::to_unsigned(max_streamsize)); 161 EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n))) 162 .WillOnce(testing::Return(max_streamsize)); 163 data += n; 164 size -= n; 165 } while (size != 0); 166 fmt::detail::write_buffer(os, buffer); 167 } 168 169 TEST(ostream_test, join) { 170 int v[3] = {1, 2, 3}; 171 EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, v + 3, ", "))); 172 } 173 174 TEST(ostream_test, join_fallback_formatter) { 175 auto strs = std::vector<test_string>{test_string("foo"), test_string("bar")}; 176 EXPECT_EQ("foo, bar", fmt::format("{}", fmt::join(strs, ", "))); 177 } 178 179 #if FMT_USE_CONSTEXPR 180 TEST(ostream_test, constexpr_string) { 181 EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), std::string("42"))); 182 EXPECT_EQ("a string", 183 fmt::format(FMT_STRING("{0}"), test_string("a string"))); 184 } 185 #endif 186 187 namespace fmt_test { 188 struct abc {}; 189 190 template <typename Output> auto operator<<(Output& out, abc) -> Output& { 191 return out << "abc"; 192 } 193 } // namespace fmt_test 194 195 template <typename T> struct test_template {}; 196 197 template <typename T> 198 auto operator<<(std::ostream& os, test_template<T>) -> std::ostream& { 199 return os << 1; 200 } 201 202 namespace fmt { 203 template <typename T> struct formatter<test_template<T>> : formatter<int> { 204 auto format(test_template<T>, format_context& ctx) -> decltype(ctx.out()) { 205 return formatter<int>::format(2, ctx); 206 } 207 }; 208 209 template <> struct formatter<fmt_test::abc> : ostream_formatter {}; 210 } // namespace fmt 211 212 TEST(ostream_test, template) { 213 EXPECT_EQ("2", fmt::format("{}", test_template<int>())); 214 } 215 216 TEST(ostream_test, format_to_n) { 217 char buffer[4]; 218 buffer[3] = 'x'; 219 auto result = fmt::format_to_n(buffer, 3, "{}", fmt_test::abc()); 220 EXPECT_EQ(3u, result.size); 221 EXPECT_EQ(buffer + 3, result.out); 222 EXPECT_EQ("abcx", fmt::string_view(buffer, 4)); 223 result = fmt::format_to_n(buffer, 3, "x{}y", fmt_test::abc()); 224 EXPECT_EQ(5u, result.size); 225 EXPECT_EQ(buffer + 3, result.out); 226 EXPECT_EQ("xabx", fmt::string_view(buffer, 4)); 227 } 228 229 struct copyfmt_test {}; 230 231 std::ostream& operator<<(std::ostream& os, copyfmt_test) { 232 std::ios ios(nullptr); 233 ios.copyfmt(os); 234 return os << "foo"; 235 } 236 237 namespace fmt { 238 template <> struct formatter<copyfmt_test> : ostream_formatter {}; 239 } // namespace fmt 240 241 TEST(ostream_test, copyfmt) { 242 EXPECT_EQ("foo", fmt::format("{}", copyfmt_test())); 243 } 244 245 TEST(ostream_test, to_string) { 246 EXPECT_EQ("abc", fmt::to_string(fmt_test::abc())); 247 } 248 249 TEST(ostream_test, range) { 250 auto strs = std::vector<test_string>{test_string("foo"), test_string("bar")}; 251 EXPECT_EQ("[foo, bar]", fmt::format("{}", strs)); 252 } 253 254 struct abstract { 255 virtual ~abstract() = default; 256 virtual void f() = 0; 257 friend auto operator<<(std::ostream& os, const abstract&) -> std::ostream& { 258 return os; 259 } 260 }; 261 262 namespace fmt { 263 template <> struct formatter<abstract> : ostream_formatter {}; 264 } // namespace fmt 265 266 void format_abstract_compiles(const abstract& a) { 267 fmt::format(FMT_COMPILE("{}"), a); 268 } 269 270 TEST(ostream_test, is_formattable) { 271 EXPECT_TRUE(fmt::is_formattable<std::string>()); 272 EXPECT_TRUE(fmt::is_formattable<fmt::detail::std_string_view<char>>()); 273 } 274 275 struct streamable_and_unformattable {}; 276 277 auto operator<<(std::ostream& os, streamable_and_unformattable) 278 -> std::ostream& { 279 return os << "foo"; 280 } 281 282 TEST(ostream_test, streamed) { 283 EXPECT_FALSE(fmt::is_formattable<streamable_and_unformattable>()); 284 EXPECT_EQ(fmt::format("{}", fmt::streamed(streamable_and_unformattable())), 285 "foo"); 286 } 287 288 TEST(ostream_test, closed_ofstream) { 289 std::ofstream ofs; 290 fmt::print(ofs, "discard"); 291 }