Cover format_to, format_to_n truncation, write failure, and
large writes that exceed the internal buffer.  These paths
were previously untested.

libstdc++-v3/ChangeLog:

        * testsuite/std/format/functions/format_to_ostreambuf.cc: New test.

Signed-off-by: Anlai Lu <[email protected]>
---
 .../format/functions/format_to_ostreambuf.cc  | 244 ++++++++++++++++++
 1 file changed, 244 insertions(+)
 create mode 100644 
libstdc++-v3/testsuite/std/format/functions/format_to_ostreambuf.cc

diff --git 
a/libstdc++-v3/testsuite/std/format/functions/format_to_ostreambuf.cc 
b/libstdc++-v3/testsuite/std/format/functions/format_to_ostreambuf.cc
new file mode 100644
index 000000000..4fc8e7597
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/format/functions/format_to_ostreambuf.cc
@@ -0,0 +1,244 @@
+// { dg-do run { target c++20 } }
+
+#include <format>
+#include <sstream>
+#include <string>
+#include <testsuite_hooks.h>
+
+template<std::streamsize BufSize>
+struct ShortWriteStreambuf : std::streambuf
+{
+  std::string data;
+
+  std::streamsize
+  xsputn(const char_type* s, std::streamsize n) override
+  {
+    std::streamsize limit = std::max<std::streamsize>(0, BufSize - 
data.size());
+    std::streamsize to_write = std::min(n, limit);
+    if (to_write > 0)
+      data.append(s, to_write);
+    return to_write;
+  }
+
+  int_type
+  overflow(int_type c) override
+  {
+    if (data.size() < BufSize)
+      {
+       data.push_back(traits_type::to_char_type(c));
+       return c;
+      }
+    return traits_type::eof();
+  }
+};
+
+void
+test_format_to()
+{
+  {
+    std::ostringstream os;
+    std::format_to(std::ostreambuf_iterator<char>(os), "{}", 42);
+    VERIFY( os.str() == "42" );
+  }
+
+  {
+    std::ostringstream os;
+    std::string big(800, 'y');
+    std::format_to(std::ostreambuf_iterator<char>(os), "{}", big);
+    VERIFY( os.str() == big );
+  }
+
+  {
+    std::ostringstream os;
+    std::string part(700, 'P');
+    std::format_to(std::ostreambuf_iterator<char>(os),
+                  "{}{}{}", part, part, part);
+    VERIFY( os.str() == std::string(2100, 'P') );
+  }
+
+  {
+    std::ostringstream os;
+    std::string body(400, 'B');
+    std::format_to(std::ostreambuf_iterator<char>(os),
+                  "header-{}-footer", body);
+    VERIFY( os.str() == "header-" + std::string(400, 'B') + "-footer" );
+  }
+}
+
+void
+test_format_to_n()
+{
+  {
+    std::ostringstream os;
+    auto res = std::format_to_n(std::ostreambuf_iterator<char>(os),
+                               3, "{}", "hello");
+    VERIFY( res.size == 5 );
+    VERIFY( os.str() == "hel" );
+  }
+
+  {
+    std::ostringstream os;
+    auto res = std::format_to_n(std::ostreambuf_iterator<char>(os),
+                               10, "{}", std::string(100, 'x'));
+    VERIFY( res.size == 100 );
+    VERIFY( os.str() == std::string(10, 'x') );
+  }
+
+  {
+    std::ostringstream os;
+    auto res = std::format_to_n(std::ostreambuf_iterator<char>(os),
+                               10, "{}", std::string(1000, 'x'));
+    VERIFY( res.size == 1000 );
+    VERIFY( os.str() == std::string(10, 'x') );
+  }
+
+  {
+    std::ostringstream os;
+    auto res = std::format_to_n(std::ostreambuf_iterator<char>(os),
+                               256, "{}", std::string(1000, 'x'));
+    VERIFY( res.size == 1000 );
+    VERIFY( os.str() == std::string(256, 'x') );
+  }
+
+  {
+    std::ostringstream os;
+    auto res = std::format_to_n(std::ostreambuf_iterator<char>(os),
+                               257, "{}", std::string(1000, 'x'));
+    VERIFY( res.size == 1000 );
+    VERIFY( os.str() == std::string(257, 'x') );
+  }
+
+  {
+    std::ostringstream os;
+    auto res = std::format_to_n(std::ostreambuf_iterator<char>(os),
+                               8, "{}{}{}", "AAAA", "BBBB", "CCCC");
+    VERIFY( res.size == 12 );
+    VERIFY( os.str() == "AAAABBBB" );
+  }
+
+  {
+    std::ostringstream os;
+    auto res = std::format_to_n(std::ostreambuf_iterator<char>(os),
+                               2, "{}{}", "AAAA", "BBBB");
+    VERIFY( res.size == 8 );
+    VERIFY( os.str() == "AA" );
+  }
+
+  {
+    std::ostringstream os;
+    auto res = std::format_to_n(std::ostreambuf_iterator<char>(os),
+                               8, "{}{}", "AAAA", "BBBB");
+    VERIFY( res.size == 8 );
+    VERIFY( os.str() == "AAAABBBB" );
+  }
+}
+
+void
+test_format_to_n_padding()
+{
+  {
+    std::ostringstream os;
+    auto res = std::format_to_n(std::ostreambuf_iterator<char>(os),
+                               3, "{:>10}", "hello");
+    VERIFY( res.size == 10 );
+    VERIFY( os.str().size() == 3 );
+  }
+
+  {
+    std::ostringstream os;
+    auto res = std::format_to_n(std::ostreambuf_iterator<char>(os),
+                               3, "{:.5}", std::string(100, 'z'));
+    VERIFY( res.size == 5 );
+    VERIFY( os.str() == std::string(3, 'z') );
+  }
+}
+
+void
+test_format_to_n_prefilled()
+{
+  // Pre-filled ostringstream: after seekp(0) the streambuf has a put
+  // area pointing into existing data.  This exercises the
+  // _M_use_put_area code path combined with format_to_n truncation.
+  {
+    std::string init(100, 'Z');
+    std::ostringstream os(init);
+    os.seekp(0);
+    auto res = std::format_to_n(std::ostreambuf_iterator<char>(os),
+                               5, "{}", "1234567890");
+    VERIFY( res.size == 10 );
+    VERIFY( os.str() == "12345ZZZZZ" + std::string(90, 'Z') );
+  }
+}
+
+void
+test_write_failure()
+{
+  const std::string payload(20, 'Z');
+
+  {
+    ShortWriteStreambuf<20> buf;
+    auto it = std::ostreambuf_iterator<char>(&buf);
+    auto res = std::format_to(it, "{}", payload);
+    VERIFY( !res.failed() );
+    VERIFY( buf.data == std::string(20, 'Z') );
+  }
+
+  {
+    ShortWriteStreambuf<19> buf;
+    auto it = std::ostreambuf_iterator<char>(&buf);
+    auto res = std::format_to(it, "{}", payload);
+    VERIFY( res.failed() );
+    VERIFY( buf.data == std::string(19, 'Z') );
+  }
+
+  {
+    ShortWriteStreambuf<10> buf;
+    auto it = std::ostreambuf_iterator<char>(&buf);
+    auto res = std::format_to_n(it, 10, "{}", payload);
+    VERIFY( !res.out.failed() );
+    VERIFY( res.size == 20 );
+    VERIFY( buf.data == std::string(10, 'Z') );
+  }
+
+  {
+    ShortWriteStreambuf<10> buf;
+    auto it = std::ostreambuf_iterator<char>(&buf);
+    auto res = std::format_to_n(it, 11, "{}", payload);
+    VERIFY( res.out.failed() );
+    VERIFY( res.size == 20 );
+    VERIFY( buf.data == std::string(10, 'Z') );
+  }
+
+  {
+    ShortWriteStreambuf<100> buf;
+    auto it = std::ostreambuf_iterator<char>(&buf);
+    auto res = std::format_to(it, "{}", std::string(300, 'Y'));
+    VERIFY( res.failed() );
+    VERIFY( buf.data.size() <= 100 );
+  }
+  // format_to_n after write failure: size must still be the total
+  // number of characters that would have been produced.
+  {
+    ShortWriteStreambuf<50> buf;
+    auto it = std::ostreambuf_iterator<char>(&buf);
+    auto res = std::format_to_n(it, 100,
+                               "{}{}{}",
+                               std::string(20, 'A'),
+                               std::string(30, 'B'),
+                               std::string(40, 'C'));
+    // The first two arguments fill the buffer (20 + 30 == 50).
+    // The third argument (40 C's) triggers write failure and the
+    // remaining chars must still be counted in res.size.
+    VERIFY( res.out.failed() );
+    VERIFY( res.size == 90 );
+  }
+}
+
+int main()
+{
+  test_format_to();
+  test_format_to_n();
+  test_format_to_n_padding();
+  test_format_to_n_prefilled();
+  test_write_failure();
+}

base-commit: 5be7b70e769e6e992dbf631cd37e24bd9da9e05e
-- 
2.34.1

Reply via email to