Skip to content

Commit

Permalink
Add test for buffering behavior of iostreams
Browse files Browse the repository at this point in the history
Closes #186
  • Loading branch information
Flamefire committed Oct 17, 2024
1 parent 84972e2 commit 7a975e0
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 19 deletions.
7 changes: 6 additions & 1 deletion include/boost/nowide/iostream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ namespace nowide {
class BOOST_NOWIDE_DECL winconsole_ostream : public std::ostream
{
public:
winconsole_ostream(bool isBuffered, winconsole_ostream* tieStream);
enum class target_stream
{
output,
error
};
winconsole_ostream(target_stream target, bool isBuffered, winconsole_ostream* tieStream);
~winconsole_ostream();

private:
Expand Down
26 changes: 13 additions & 13 deletions src/iostream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,27 +82,26 @@ namespace nowide {
}
};

winconsole_ostream::winconsole_ostream(const bool isBuffered, winconsole_ostream* tieStream) : std::ostream(0)
winconsole_ostream::winconsole_ostream(const target_stream target,
const bool isBuffered,
winconsole_ostream* tieStream) :
std::ostream(0)
{
HANDLE h;
if(isBuffered)
h = GetStdHandle(STD_OUTPUT_HANDLE);
else
h = GetStdHandle(STD_ERROR_HANDLE);
const HANDLE h = GetStdHandle(target == target_stream::output ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);

if(is_atty_handle(h))
{
d.reset(new console_output_buffer(h));
std::ostream::rdbuf(d.get());
} else
{
std::ostream::rdbuf(isBuffered ? std::cout.rdbuf() : std::cerr.rdbuf());
std::ostream::rdbuf(target == target_stream::output ? std::cout.rdbuf() : std::cerr.rdbuf());
assert(rdbuf());
}
if(tieStream)
{
tie(tieStream);
setf(ios_base::unitbuf); // If tieStream is set, this is cerr -> set unbuffered
}
if(!isBuffered)
setf(ios_base::unitbuf);
}
winconsole_ostream::~winconsole_ostream()
{
Expand Down Expand Up @@ -144,10 +143,11 @@ namespace nowide {
#else
#define BOOST_NOWIDE_INIT_PRIORITY
#endif
detail::winconsole_ostream cout BOOST_NOWIDE_INIT_PRIORITY(true, nullptr);
using target_stream = detail::winconsole_ostream::target_stream;
detail::winconsole_ostream cout BOOST_NOWIDE_INIT_PRIORITY(target_stream::output, true, nullptr);
detail::winconsole_istream cin BOOST_NOWIDE_INIT_PRIORITY(&cout);
detail::winconsole_ostream cerr BOOST_NOWIDE_INIT_PRIORITY(false, &cout);
detail::winconsole_ostream clog BOOST_NOWIDE_INIT_PRIORITY(false, nullptr);
detail::winconsole_ostream cerr BOOST_NOWIDE_INIT_PRIORITY(target_stream::error, false, &cout);
detail::winconsole_ostream clog BOOST_NOWIDE_INIT_PRIORITY(target_stream::error, true, nullptr);
} // namespace nowide
} // namespace boost

Expand Down
32 changes: 27 additions & 5 deletions test/test_iostream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ const std::string outputString =
const bool usesNowideRdBufIn = nw::cin.rdbuf() != std::cin.rdbuf();
const bool usesNowideRdBufOut = nw::cout.rdbuf() != std::cout.rdbuf();

bool is_buffered(const std::ostream& os)
{
return (os.flags() & std::ios_base::unitbuf) == 0;
}

#ifndef BOOST_NOWIDE_TEST_INTERACTIVE
class mock_output_buffer final : public nw::detail::console_output_buffer_base
{
Expand Down Expand Up @@ -134,12 +139,14 @@ void test_is_valid_UTF8()
TEST(!is_valid_UTF8(invalid_utf8_tests[0].utf8)); // Detect invalid
}

void test_tie()
void test_tie_and_buffered()
{
TEST(nw::cin.tie() == &nw::cout);
TEST(nw::cerr.tie() == &nw::cout);
TEST((nw::cerr.flags() & std::ios_base::unitbuf) != 0);
TEST(nw::clog.tie() == nullptr);
TEST(is_buffered(nw::cout));
TEST(!is_buffered(nw::cerr));
TEST(is_buffered(nw::clog));
}

void test_putback_and_get()
Expand Down Expand Up @@ -233,6 +240,18 @@ void test_cerr()
TEST_MOCKED(mock_buf.output == nw::widen("aHello World"));
}

void test_clog()
{
if(usesNowideRdBufOut)
{
TEST(nw::clog.rdbuf() != std::clog.rdbuf());
TEST(nw::clog.rdbuf() != std::cout.rdbuf());
TEST(nw::clog.rdbuf() != nw::cin.rdbuf());
TEST(nw::clog.rdbuf() != nw::cout.rdbuf());
TEST(nw::clog.rdbuf() != nw::cerr.rdbuf());

Check warning on line 251 in test/test_iostream.cpp

View check run for this annotation

Codecov / codecov/patch

test/test_iostream.cpp#L247-L251

Added lines #L247 - L251 were not covered by tests
}
}

void test_cerr_single_char()
{
INSTALL_MOCK_BUF(cerr, mock_output_buffer);
Expand Down Expand Up @@ -494,7 +513,8 @@ void test_console()
std::cout << "Test cout console" << std::endl;
{
RedirectStdio stdoutHandle(STD_OUTPUT_HANDLE);
decltype(nw::cout) cout(true, nullptr);
using cout_t = decltype(nw::cout);
cout_t cout(cout_t::target_stream::output, true, nullptr);
TEST(cout.rdbuf() != std::cout.rdbuf());

const std::string testString = "Hello std out\n\xc3\xa4-\xc3\xb6-\xc3\xbc\n";
Expand All @@ -507,7 +527,8 @@ void test_console()
{
RedirectStdio stderrHandle(STD_ERROR_HANDLE);

decltype(nw::cerr) cerr(false, nullptr);
using cerr_t = decltype(nw::cerr);
cerr_t cerr(cerr_t::target_stream::error, false, nullptr);
TEST(cerr.rdbuf() != std::cerr.rdbuf());

const std::string testString = "Hello std err\n\xc3\xa4-\xc3\xb6-\xc3\xbc\n";
Expand Down Expand Up @@ -573,12 +594,13 @@ void test_main(int argc, char** argv, char**)
test_ctrl_z_is_eof(); // LCOV_EXCL_LINE
#else
test_is_valid_UTF8();
test_tie();
test_tie_and_buffered();
test_putback_and_get();
test_cout();
test_cout_single_char();
test_cerr();
test_cerr_single_char();
test_clog();
test_cin();
test_cin_getline();
test_ctrl_z_is_eof();
Expand Down

0 comments on commit 7a975e0

Please sign in to comment.