/ externals / fmt / test / ostream-test.cc
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  }