From 357f18a9735786e4d49dd436084ccf2a8768604f Mon Sep 17 00:00:00 2001 From: HarshPopat23 Date: Fri, 11 Sep 2026 19:29:06 +0530 Subject: [PATCH] feat(terminal): add unconditional ANSI styling policy Add TerminalColorPolicy::Always to enable unconditional ANSI styling regardless of destination interactivity. This allows callers to explicitly request color output when streams are captured through pipes, such as in automated CLI test runners, while keeping terminal detection truthful and preserving WhenInteractive and Disabled semantics. Provide terminal_reset_color_policy() to restore stream coloring to the default policy, and exercise CLI color output across non-interactive executions using .clitest suites with sourcemeta::core::options parsing. Signed-off-by: HarshPopat23 --- .../include/sourcemeta/core/terminal.h | 51 ++- src/core/terminal/terminal.cc | 22 +- test/terminal/CMakeLists.txt | 17 + test/terminal/color_always.clitest | 6 + test/terminal/color_auto.clitest | 6 + test/terminal/color_never.clitest | 6 + test/terminal/terminal_child_main.cc | 69 ++++ test/terminal/terminal_test.cc | 296 +++++++++++++++++- 8 files changed, 467 insertions(+), 6 deletions(-) create mode 100644 test/terminal/color_always.clitest create mode 100644 test/terminal/color_auto.clitest create mode 100644 test/terminal/color_never.clitest create mode 100644 test/terminal/terminal_child_main.cc diff --git a/src/core/terminal/include/sourcemeta/core/terminal.h b/src/core/terminal/include/sourcemeta/core/terminal.h index fbbe84a7d1..527d28c0bc 100644 --- a/src/core/terminal/include/sourcemeta/core/terminal.h +++ b/src/core/terminal/include/sourcemeta/core/terminal.h @@ -76,7 +76,10 @@ enum class TerminalColorPolicy : std::uint8_t { /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/isatty.html WhenInteractive, /// Styling is unconditionally suppressed. - Disabled + Disabled, + /// Styling is unconditionally enabled regardless of destination + /// interactivity. + Always }; /// @ingroup terminal @@ -328,6 +331,49 @@ SOURCEMETA_CORE_TERMINAL_EXPORT auto terminal_set_color_policy(TerminalStream stream, TerminalColorPolicy policy) noexcept -> void; +/// @ingroup terminal +/// +/// Reset the color policy across all streams to default +/// (`TerminalColorPolicy::WhenInteractive`). +/// +/// For example: +/// +/// ```cpp +/// #include +/// #include +/// +/// sourcemeta::core::terminal_set_color_policy( +/// sourcemeta::core::TerminalColorPolicy::Disabled); +/// sourcemeta::core::terminal_reset_color_policy(); +/// assert(sourcemeta::core::terminal_color_policy() == +/// sourcemeta::core::TerminalColorPolicy::WhenInteractive); +/// ``` +SOURCEMETA_CORE_TERMINAL_EXPORT +auto terminal_reset_color_policy() noexcept -> void; + +/// @ingroup terminal +/// +/// Reset the color policy for a specific stream to default +/// (`TerminalColorPolicy::WhenInteractive`). +/// +/// For example: +/// +/// ```cpp +/// #include +/// #include +/// +/// sourcemeta::core::terminal_set_color_policy( +/// sourcemeta::core::TerminalStream::Stderr, +/// sourcemeta::core::TerminalColorPolicy::Disabled); +/// sourcemeta::core::terminal_reset_color_policy( +/// sourcemeta::core::TerminalStream::Stderr); +/// assert(sourcemeta::core::terminal_color_policy( +/// sourcemeta::core::TerminalStream::Stderr) == +/// sourcemeta::core::TerminalColorPolicy::WhenInteractive); +/// ``` +SOURCEMETA_CORE_TERMINAL_EXPORT +auto terminal_reset_color_policy(TerminalStream stream) noexcept -> void; + /// @ingroup terminal /// /// Retrieve the current color policy for the specified stream. @@ -340,7 +386,8 @@ auto terminal_set_color_policy(TerminalStream stream, /// /// const auto policy{sourcemeta::core::terminal_color_policy()}; /// assert(policy == sourcemeta::core::TerminalColorPolicy::WhenInteractive || -/// policy == sourcemeta::core::TerminalColorPolicy::Disabled); +/// policy == sourcemeta::core::TerminalColorPolicy::Disabled || +/// policy == sourcemeta::core::TerminalColorPolicy::Always); /// ``` SOURCEMETA_CORE_TERMINAL_EXPORT auto terminal_color_policy( diff --git a/src/core/terminal/terminal.cc b/src/core/terminal/terminal.cc index 82992cedb3..5c29d71719 100644 --- a/src/core/terminal/terminal.cc +++ b/src/core/terminal/terminal.cc @@ -10,7 +10,7 @@ #include // std::ostream #include // std::string #include // std::string_view -#include // std::to_underlying +#include // std::to_underlying, std::unreachable namespace { @@ -49,14 +49,30 @@ auto terminal_set_color_policy(TerminalStream stream, std::memory_order_relaxed); } +auto terminal_reset_color_policy() noexcept -> void { + terminal_set_color_policy(TerminalColorPolicy::WhenInteractive); +} + +auto terminal_reset_color_policy(TerminalStream stream) noexcept -> void { + terminal_set_color_policy(stream, TerminalColorPolicy::WhenInteractive); +} + auto terminal_color_policy(TerminalStream stream) noexcept -> TerminalColorPolicy { return stream_policies[stream_index(stream)].load(std::memory_order_relaxed); } auto terminal_color_enabled(TerminalStream stream) noexcept -> bool { - return terminal_color_policy(stream) != TerminalColorPolicy::Disabled && - terminal_is_interactive(stream); + switch (terminal_color_policy(stream)) { + case TerminalColorPolicy::WhenInteractive: + return terminal_is_interactive(stream); + case TerminalColorPolicy::Always: + return true; + case TerminalColorPolicy::Disabled: + return false; + default: + std::unreachable(); + } } auto terminal_sgr_reset() noexcept -> std::string_view { diff --git a/test/terminal/CMakeLists.txt b/test/terminal/CMakeLists.txt index d5ab8a74c0..bb17cc58ea 100644 --- a/test/terminal/CMakeLists.txt +++ b/test/terminal/CMakeLists.txt @@ -3,3 +3,20 @@ sourcemeta_test(NAMESPACE sourcemeta PROJECT core NAME terminal target_link_libraries(sourcemeta_core_terminal_unit PRIVATE sourcemeta::core::terminal) + +sourcemeta_executable(NAMESPACE sourcemeta PROJECT core NAME terminal + VARIANT test_helper SOURCES terminal_child_main.cc) +target_link_libraries(sourcemeta_core_terminal_test_helper + PRIVATE sourcemeta::core::terminal + PRIVATE sourcemeta::core::options) + +macro(add_terminal_test name) + add_test(NAME core.terminal.${name} COMMAND + "$" + "${CMAKE_CURRENT_SOURCE_DIR}/${name}.clitest" + --binary "$") +endmacro() + +add_terminal_test(color_auto) +add_terminal_test(color_always) +add_terminal_test(color_never) diff --git a/test/terminal/color_always.clitest b/test/terminal/color_always.clitest new file mode 100644 index 0000000000..773f19b133 --- /dev/null +++ b/test/terminal/color_always.clitest @@ -0,0 +1,6 @@ +RUN --color always STDIN /dev/null IN . INTO result.txt EXPECTING 0 +WRITE expected.txt UNTIL END +1> streamed +1> painted +END +COMPARE result.txt AGAINST expected.txt diff --git a/test/terminal/color_auto.clitest b/test/terminal/color_auto.clitest new file mode 100644 index 0000000000..9f4051bc3c --- /dev/null +++ b/test/terminal/color_auto.clitest @@ -0,0 +1,6 @@ +RUN --color auto STDIN /dev/null IN . INTO result.txt EXPECTING 0 +WRITE expected.txt UNTIL END +1> streamed +1> painted +END +COMPARE result.txt AGAINST expected.txt diff --git a/test/terminal/color_never.clitest b/test/terminal/color_never.clitest new file mode 100644 index 0000000000..9cf99e24f0 --- /dev/null +++ b/test/terminal/color_never.clitest @@ -0,0 +1,6 @@ +RUN --color never STDIN /dev/null IN . INTO result.txt EXPECTING 0 +WRITE expected.txt UNTIL END +1> streamed +1> painted +END +COMPARE result.txt AGAINST expected.txt diff --git a/test/terminal/terminal_child_main.cc b/test/terminal/terminal_child_main.cc new file mode 100644 index 0000000000..fc471918b7 --- /dev/null +++ b/test/terminal/terminal_child_main.cc @@ -0,0 +1,69 @@ +#include +#include + +#include +#include +#include +#include + +#if defined(_WIN32) +#include // _O_BINARY +#include // _setmode, _fileno +#endif + +namespace { + +auto run_mode(sourcemeta::core::TerminalColorPolicy policy) -> int { + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalStream::Stdout, policy); + assert(!sourcemeta::core::terminal_is_interactive( + sourcemeta::core::TerminalStream::Stdout)); + sourcemeta::core::terminal_paint( + std::cout, sourcemeta::core::TerminalStream::Stdout, "streamed", + sourcemeta::core::TerminalStyle::Red); + std::cout << "\n" + << sourcemeta::core::terminal_paint( + sourcemeta::core::TerminalStream::Stdout, "painted", + sourcemeta::core::TerminalStyle::Bold | + sourcemeta::core::TerminalStyle::Green) + << "\n" + << std::flush; + return EXIT_SUCCESS; +} + +} // namespace + +auto main(int argc, char **argv) -> int { +#if defined(_WIN32) + // Ensure standard output is binary on Windows so newlines are not converted + // to CRLF (\r\n), which would alter captured byte stream comparisons. + _setmode(_fileno(stdout), _O_BINARY); +#endif + + sourcemeta::core::Options application; + application.option("color", {}); + application.parse(argc, argv); + + auto policy{sourcemeta::core::TerminalColorPolicy::WhenInteractive}; + if (application.contains("color")) { + const auto &values{application.at("color")}; + if (values.empty()) { + std::cerr << "missing value for --color\n"; + return EXIT_FAILURE; + } + + const auto value{values.front()}; + if (value == "always") { + policy = sourcemeta::core::TerminalColorPolicy::Always; + } else if (value == "auto" || value == "when-interactive") { + policy = sourcemeta::core::TerminalColorPolicy::WhenInteractive; + } else if (value == "never" || value == "disabled") { + policy = sourcemeta::core::TerminalColorPolicy::Disabled; + } else { + std::cerr << "unknown --color option: " << value << "\n"; + return EXIT_FAILURE; + } + } + + return run_mode(policy); +} diff --git a/test/terminal/terminal_test.cc b/test/terminal/terminal_test.cc index ee7ecf8203..bac7dc76e0 100644 --- a/test/terminal/terminal_test.cc +++ b/test/terminal/terminal_test.cc @@ -211,6 +211,8 @@ TEST(policy_default_argument) { } TEST(policy_lifecycle_and_isolation) { + sourcemeta::core::terminal_reset_color_policy(); + // Global configuration affects all streams sourcemeta::core::terminal_set_color_policy( sourcemeta::core::TerminalColorPolicy::WhenInteractive); @@ -251,11 +253,46 @@ TEST(policy_lifecycle_and_isolation) { EXPECT_EQ(stderr_stream.str(), "error_stream"); // Restore default policy across all streams + sourcemeta::core::terminal_reset_color_policy(); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stderr), + sourcemeta::core::TerminalColorPolicy::WhenInteractive); +} + +TEST(policy_reset_global) { sourcemeta::core::terminal_set_color_policy( - sourcemeta::core::TerminalColorPolicy::WhenInteractive); + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::Always); + sourcemeta::core::terminal_reset_color_policy(); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::WhenInteractive); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stderr), + sourcemeta::core::TerminalColorPolicy::WhenInteractive); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdin), + sourcemeta::core::TerminalColorPolicy::WhenInteractive); +} + +TEST(policy_reset_per_stream) { + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalStream::Stderr, + sourcemeta::core::TerminalColorPolicy::Disabled); + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalStream::Stdout, + sourcemeta::core::TerminalColorPolicy::Always); + sourcemeta::core::terminal_reset_color_policy( + sourcemeta::core::TerminalStream::Stderr); EXPECT_EQ(sourcemeta::core::terminal_color_policy( sourcemeta::core::TerminalStream::Stderr), sourcemeta::core::TerminalColorPolicy::WhenInteractive); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::Always); + sourcemeta::core::terminal_reset_color_policy(); } TEST(stream_detection_runs_safely) { @@ -311,3 +348,260 @@ TEST(composition_with_formatting) { const std::string text_disabled{"Status: " + plain + " [exit code: 1]"}; EXPECT_EQ(text_disabled, "Status: FAILED [exit code: 1]"); } + +TEST(policy_always_enables_styling) { + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stderr), + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdin), + sourcemeta::core::TerminalColorPolicy::Always); + + EXPECT_TRUE(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stdout)); + EXPECT_TRUE(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stderr)); + EXPECT_TRUE(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stdin)); + sourcemeta::core::terminal_reset_color_policy(); +} + +TEST(policy_always_preserves_terminal_interactivity) { + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_FALSE(sourcemeta::core::terminal_is_interactive(-1)); + + std::array pipe_descriptors{{-1, -1}}; +#if defined(_WIN32) + const int pipe_status{_pipe(pipe_descriptors.data(), 256, _O_BINARY)}; +#else + const int pipe_status{::pipe(pipe_descriptors.data())}; +#endif + if (pipe_status == 0) { + EXPECT_FALSE( + sourcemeta::core::terminal_is_interactive(pipe_descriptors[0])); + EXPECT_FALSE( + sourcemeta::core::terminal_is_interactive(pipe_descriptors[1])); +#if defined(_WIN32) + _close(pipe_descriptors[0]); + _close(pipe_descriptors[1]); +#else + ::close(pipe_descriptors[0]); + ::close(pipe_descriptors[1]); +#endif + } + sourcemeta::core::terminal_reset_color_policy(); +} + +TEST(policy_disabled_suppresses_styling) { + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalColorPolicy::Disabled); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::Disabled); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stderr), + sourcemeta::core::TerminalColorPolicy::Disabled); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdin), + sourcemeta::core::TerminalColorPolicy::Disabled); + + EXPECT_FALSE(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stdout)); + EXPECT_FALSE(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stderr)); + EXPECT_FALSE(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stdin)); + sourcemeta::core::terminal_reset_color_policy(); +} + +TEST(policy_when_interactive_retains_detection) { + sourcemeta::core::terminal_reset_color_policy(); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::WhenInteractive); + + EXPECT_EQ(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::terminal_is_interactive( + sourcemeta::core::TerminalStream::Stdout)); + EXPECT_EQ(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stderr), + sourcemeta::core::terminal_is_interactive( + sourcemeta::core::TerminalStream::Stderr)); + EXPECT_EQ(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stdin), + sourcemeta::core::terminal_is_interactive( + sourcemeta::core::TerminalStream::Stdin)); +} + +TEST(policy_switching_takes_effect) { + sourcemeta::core::terminal_reset_color_policy(); + + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalStream::Stdout, + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_TRUE(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stdout)); + + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalStream::Stdout, + sourcemeta::core::TerminalColorPolicy::Disabled); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::Disabled); + EXPECT_FALSE(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stdout)); + + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalStream::Stdout, + sourcemeta::core::TerminalColorPolicy::WhenInteractive); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::WhenInteractive); + EXPECT_EQ(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::terminal_is_interactive( + sourcemeta::core::TerminalStream::Stdout)); + + sourcemeta::core::terminal_reset_color_policy(); +} + +TEST(policy_per_stream_isolation) { + sourcemeta::core::terminal_reset_color_policy(); + + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalStream::Stdout, + sourcemeta::core::TerminalColorPolicy::Always); + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalStream::Stderr, + sourcemeta::core::TerminalColorPolicy::Disabled); + + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stderr), + sourcemeta::core::TerminalColorPolicy::Disabled); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdin), + sourcemeta::core::TerminalColorPolicy::WhenInteractive); + + EXPECT_TRUE(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stdout)); + EXPECT_FALSE(sourcemeta::core::terminal_color_enabled( + sourcemeta::core::TerminalStream::Stderr)); + + sourcemeta::core::terminal_reset_color_policy(); +} + +TEST(policy_global_setter_across_streams) { + sourcemeta::core::terminal_reset_color_policy(); + + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stderr), + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdin), + sourcemeta::core::TerminalColorPolicy::Always); + + // Subsequent per-stream setter is not blocked by earlier global setter + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalStream::Stderr, + sourcemeta::core::TerminalColorPolicy::Disabled); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stdout), + sourcemeta::core::TerminalColorPolicy::Always); + EXPECT_EQ(sourcemeta::core::terminal_color_policy( + sourcemeta::core::TerminalStream::Stderr), + sourcemeta::core::TerminalColorPolicy::Disabled); + + sourcemeta::core::terminal_reset_color_policy(); +} + +TEST(paint_destination_aware_always) { + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalStream::Stdout, + sourcemeta::core::TerminalColorPolicy::Always); + + // Basic styling + EXPECT_EQ(sourcemeta::core::terminal_paint( + sourcemeta::core::TerminalStream::Stdout, "Hello", + sourcemeta::core::TerminalStyle::Red), + "\033[31mHello\033[0m"); + + // Representative combined style (bold + foreground color) + const auto bold_green{sourcemeta::core::TerminalStyle::Bold | + sourcemeta::core::TerminalStyle::Green}; + EXPECT_EQ( + sourcemeta::core::terminal_paint(sourcemeta::core::TerminalStream::Stdout, + "Success", bold_green), + "\033[1;32mSuccess\033[0m"); + + // Stream output overload + std::ostringstream stream_output; + sourcemeta::core::terminal_paint(stream_output, + sourcemeta::core::TerminalStream::Stdout, + "Streamed", bold_green); + EXPECT_EQ(stream_output.str(), "\033[1;32mStreamed\033[0m"); + + // Empty text produces no escape sequences + EXPECT_EQ(sourcemeta::core::terminal_paint( + sourcemeta::core::TerminalStream::Stdout, "", bold_green), + ""); + std::ostringstream empty_stream; + sourcemeta::core::terminal_paint( + empty_stream, sourcemeta::core::TerminalStream::Stdout, "", bold_green); + EXPECT_EQ(empty_stream.str(), ""); + + // Style None produces plain text without escape sequences + EXPECT_EQ(sourcemeta::core::terminal_paint( + sourcemeta::core::TerminalStream::Stdout, "Plain", + sourcemeta::core::TerminalStyle::None), + "Plain"); + std::ostringstream none_stream; + sourcemeta::core::terminal_paint( + none_stream, sourcemeta::core::TerminalStream::Stdout, "Plain", + sourcemeta::core::TerminalStyle::None); + EXPECT_EQ(none_stream.str(), "Plain"); + + sourcemeta::core::terminal_reset_color_policy(); +} + +TEST(paint_destination_aware_disabled) { + sourcemeta::core::terminal_set_color_policy( + sourcemeta::core::TerminalStream::Stdout, + sourcemeta::core::TerminalColorPolicy::Disabled); + + EXPECT_EQ(sourcemeta::core::terminal_paint( + sourcemeta::core::TerminalStream::Stdout, "Hello", + sourcemeta::core::TerminalStyle::Red), + "Hello"); + + const auto bold_blue{sourcemeta::core::TerminalStyle::Bold | + sourcemeta::core::TerminalStyle::Blue}; + EXPECT_EQ(sourcemeta::core::terminal_paint( + sourcemeta::core::TerminalStream::Stdout, "Info", bold_blue), + "Info"); + + std::ostringstream stream_output; + sourcemeta::core::terminal_paint(stream_output, + sourcemeta::core::TerminalStream::Stdout, + "Streamed", bold_blue); + EXPECT_EQ(stream_output.str(), "Streamed"); + + sourcemeta::core::terminal_reset_color_policy(); +}