From fc9fcaa1ba83bc20a3391e359dae8651616bea24 Mon Sep 17 00:00:00 2001 From: jgabry Date: Fri, 28 Aug 2026 11:08:37 -0600 Subject: [PATCH 1/3] json_writer.hpp: flush when the top-level closes --- src/stan/callbacks/json_writer.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stan/callbacks/json_writer.hpp b/src/stan/callbacks/json_writer.hpp index d791b8a7371..869bbeee46d 100644 --- a/src/stan/callbacks/json_writer.hpp +++ b/src/stan/callbacks/json_writer.hpp @@ -24,7 +24,7 @@ namespace callbacks { * The writer doesn't try to validate the object's internal structure * or object completeness, only syntactic correctness. * - * @tparam Stream A type with with a valid `operator<<(std::string)` + * @tparam Stream A type with a valid `operator<<(std::string)` and `flush()` * @tparam Deleter A class with a valid `operator()` method for deleting the * output stream */ @@ -227,6 +227,7 @@ class json_writer final : public structured_writer { record_element_needs_comma_ = true; } else { *output_ << "\n"; + output_->flush(); } } From 75d9cd09010d2f42e75b427677ad9a9a37a1a8c6 Mon Sep 17 00:00:00 2001 From: jgabry Date: Fri, 28 Aug 2026 11:11:08 -0600 Subject: [PATCH 2/3] tests for json flushing closing top level should flush once, don't flush when closing a nested object --- src/test/unit/callbacks/json_writer_test.cpp | 41 ++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/test/unit/callbacks/json_writer_test.cpp b/src/test/unit/callbacks/json_writer_test.cpp index 3b0f2613e47..6af6317ca18 100644 --- a/src/test/unit/callbacks/json_writer_test.cpp +++ b/src/test/unit/callbacks/json_writer_test.cpp @@ -1,12 +1,24 @@ #include #include #include +#include #include struct deleter_noop { template constexpr void operator()(T* arg) const {} }; + +class counting_streambuf : public std::stringbuf { + public: + int sync() override { + ++sync_count; + return std::stringbuf::sync(); + } + + int sync_count = 0; +}; + class StanInterfaceCallbacksJsonWriter : public ::testing::Test { public: StanInterfaceCallbacksJsonWriter() @@ -92,6 +104,35 @@ TEST_F(StanInterfaceCallbacksJsonWriter, begin_end_record_nested) { ASSERT_TRUE(stan::test::is_valid_JSON(json)); } +TEST_F(StanInterfaceCallbacksJsonWriter, flushes_completed_top_level_record) { + counting_streambuf buffer; + std::ostream output(&buffer); + stan::callbacks::json_writer writer{ + std::unique_ptr(&output)}; + + writer.begin_record(); + writer.begin_record("nested"); + writer.write("value", 1); + writer.end_record(); + EXPECT_EQ(0, buffer.sync_count); + + writer.end_record(); + EXPECT_EQ(1, buffer.sync_count); + const char* expected = R"json( +{ + "nested" : { + "value" : 1 + } +} +)json"; + auto json = buffer.str(); + EXPECT_EQ(expected, json); + EXPECT_TRUE(stan::test::is_valid_JSON(json)); + + stan::callbacks::json_writer no_op_writer; + EXPECT_NO_THROW(no_op_writer.end_record()); +} + TEST_F(StanInterfaceCallbacksJsonWriter, write_double_vector) { std::string key("key"); const int N = 5; From 52dd7b3960e3ca706828b481b0427acce03e4bde Mon Sep 17 00:00:00 2001 From: jgabry Date: Fri, 28 Aug 2026 11:34:19 -0600 Subject: [PATCH 3/3] Revert "tests for json flushing" This reverts commit 75d9cd09010d2f42e75b427677ad9a9a37a1a8c6. --- src/test/unit/callbacks/json_writer_test.cpp | 41 -------------------- 1 file changed, 41 deletions(-) diff --git a/src/test/unit/callbacks/json_writer_test.cpp b/src/test/unit/callbacks/json_writer_test.cpp index 6af6317ca18..3b0f2613e47 100644 --- a/src/test/unit/callbacks/json_writer_test.cpp +++ b/src/test/unit/callbacks/json_writer_test.cpp @@ -1,24 +1,12 @@ #include #include #include -#include #include struct deleter_noop { template constexpr void operator()(T* arg) const {} }; - -class counting_streambuf : public std::stringbuf { - public: - int sync() override { - ++sync_count; - return std::stringbuf::sync(); - } - - int sync_count = 0; -}; - class StanInterfaceCallbacksJsonWriter : public ::testing::Test { public: StanInterfaceCallbacksJsonWriter() @@ -104,35 +92,6 @@ TEST_F(StanInterfaceCallbacksJsonWriter, begin_end_record_nested) { ASSERT_TRUE(stan::test::is_valid_JSON(json)); } -TEST_F(StanInterfaceCallbacksJsonWriter, flushes_completed_top_level_record) { - counting_streambuf buffer; - std::ostream output(&buffer); - stan::callbacks::json_writer writer{ - std::unique_ptr(&output)}; - - writer.begin_record(); - writer.begin_record("nested"); - writer.write("value", 1); - writer.end_record(); - EXPECT_EQ(0, buffer.sync_count); - - writer.end_record(); - EXPECT_EQ(1, buffer.sync_count); - const char* expected = R"json( -{ - "nested" : { - "value" : 1 - } -} -)json"; - auto json = buffer.str(); - EXPECT_EQ(expected, json); - EXPECT_TRUE(stan::test::is_valid_JSON(json)); - - stan::callbacks::json_writer no_op_writer; - EXPECT_NO_THROW(no_op_writer.end_record()); -} - TEST_F(StanInterfaceCallbacksJsonWriter, write_double_vector) { std::string key("key"); const int N = 5;