Commit eb34eb97 authored by Dominik Charousset's avatar Dominik Charousset Committed by Marian Triebe

Redesign terminal color API to support Windows

parent 42ab04ea
...@@ -34,7 +34,6 @@ set (LIBCAF_CORE_SRCS ...@@ -34,7 +34,6 @@ set (LIBCAF_CORE_SRCS
src/behavior_impl.cpp src/behavior_impl.cpp
src/blocking_actor.cpp src/blocking_actor.cpp
src/blocking_behavior.cpp src/blocking_behavior.cpp
src/color.cpp
src/concatenated_tuple.cpp src/concatenated_tuple.cpp
src/config_option.cpp src/config_option.cpp
src/continue_helper.cpp src/continue_helper.cpp
...@@ -89,6 +88,7 @@ set (LIBCAF_CORE_SRCS ...@@ -89,6 +88,7 @@ set (LIBCAF_CORE_SRCS
src/sync_request_bouncer.cpp src/sync_request_bouncer.cpp
src/stringification_inspector.cpp src/stringification_inspector.cpp
src/test_coordinator.cpp src/test_coordinator.cpp
src/term.cpp
src/timestamp.cpp src/timestamp.cpp
src/try_match.cpp src/try_match.cpp
src/type_erased_value.cpp src/type_erased_value.cpp
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "caf/send.hpp" #include "caf/send.hpp"
#include "caf/skip.hpp" #include "caf/skip.hpp"
#include "caf/unit.hpp" #include "caf/unit.hpp"
#include "caf/term.hpp"
#include "caf/actor.hpp" #include "caf/actor.hpp"
#include "caf/after.hpp" #include "caf/after.hpp"
#include "caf/error.hpp" #include "caf/error.hpp"
......
...@@ -17,30 +17,55 @@ ...@@ -17,30 +17,55 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#ifndef CAF_COLOR_HPP #ifndef CAF_TERM_HPP
#define CAF_COLOR_HPP #define CAF_TERM_HPP
namespace caf { #include <iosfwd>
enum color_face { namespace caf {
normal,
bold
};
enum color_value { /// Terminal color and font face options.
enum class term {
/// Resets the color to the default color and the font weight to normal.
reset, reset,
/// Like `reset` but also prints a newline.
reset_endl,
/// Sets the terminal color to black.
black, black,
/// Sets the terminal color to red.
red, red,
/// Sets the terminal color to green.
green, green,
/// Sets the terminal color to yellow.
yellow, yellow,
/// Sets the terminal color to blue.
blue, blue,
/// Sets the terminal color to magenta.
magenta, magenta,
/// Sets the terminal color to cyan.
cyan, cyan,
white /// Sets the terminal color to white.
white,
/// Sets the terminal color to black and the font weight to bold.
bold_black,
/// Sets the terminal color to red and the font weight to bold.
bold_red,
/// Sets the terminal color to green and the font weight to bold.
bold_green,
/// Sets the terminal color to yellow and the font weight to bold.
bold_yellow,
/// Sets the terminal color to blue and the font weight to bold.
bold_blue,
/// Sets the terminal color to magenta and the font weight to bold.
bold_magenta,
/// Sets the terminal color to cyan and the font weight to bold.
bold_cyan,
/// Sets the terminal color to white and the font weight to bold.
bold_white
}; };
const char* color(color_value value, color_face face = normal); std::ostream& operator<<(std::ostream& out, term x);
} // namespace caf } // namespace caf
#endif // CAF_COLOR_HPP #endif // CAF_TERM_HPP
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
#include "caf/string_algorithms.hpp" #include "caf/string_algorithms.hpp"
#include "caf/color.hpp" #include "caf/term.hpp"
#include "caf/locks.hpp" #include "caf/locks.hpp"
#include "caf/timestamp.hpp" #include "caf/timestamp.hpp"
#include "caf/actor_proxy.hpp" #include "caf/actor_proxy.hpp"
...@@ -424,22 +424,22 @@ void logger::run() { ...@@ -424,22 +424,22 @@ void logger::run() {
default: default:
break; break;
case CAF_LOG_LEVEL_ERROR: case CAF_LOG_LEVEL_ERROR:
std::clog << color(red); std::clog << term::red;;
break; break;
case CAF_LOG_LEVEL_WARNING: case CAF_LOG_LEVEL_WARNING:
std::clog << color(yellow); std::clog << term::yellow;
break; break;
case CAF_LOG_LEVEL_INFO: case CAF_LOG_LEVEL_INFO:
std::clog << color(green); std::clog << term::green;
break; break;
case CAF_LOG_LEVEL_DEBUG: case CAF_LOG_LEVEL_DEBUG:
std::clog << color(cyan); std::clog << term::cyan;
break; break;
case CAF_LOG_LEVEL_TRACE: case CAF_LOG_LEVEL_TRACE:
std::clog << color(blue); std::clog << term::blue;
break; break;
} }
std::clog << ptr->msg << color(reset) << std::endl; std::clog << ptr->msg << term::reset << std::endl;
} }
} }
if (file) { if (file) {
......
...@@ -17,29 +17,126 @@ ...@@ -17,29 +17,126 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include "caf/color.hpp" #include "caf/term.hpp"
#include <iostream>
#include "caf/config.hpp" #include "caf/config.hpp"
#ifdef CAF_WINDOWS
#include <io.h>
#include <windows.h>
#else
#include <stdio.h>
#include <unistd.h>
#endif
namespace caf { namespace caf {
const char* color(color_value value, color_face face) { namespace {
#ifdef CAF_MSVC
return ""; #ifdef CAF_WINDOWS
#else
const char* colors[9][2] = { // windows terminals do no support bold fonts
{"\033[0m", "\033[0m"}, // reset
{"\033[30m", "\033[1m\033[30m"}, // black constexpr int win_black = 0;
{"\033[31m", "\033[1m\033[31m"}, // red constexpr int win_red = FOREGROUND_INTENSITY | FOREGROUND_RED;
{"\033[32m", "\033[1m\033[32m"}, // green constexpr int win_green = FOREGROUND_INTENSITY | FOREGROUND_GREEN;
{"\033[33m", "\033[1m\033[33m"}, // yellow constexpr int win_blue = FOREGROUND_INTENSITY | FOREGROUND_BLUE;
{"\033[34m", "\033[1m\033[34m"}, // blue constexpr int win_yellow = win_red | win_green;
{"\033[35m", "\033[1m\033[35m"}, // magenta constexpr int win_magenta = win_red | win_blue;
{"\033[36m", "\033[1m\033[36m"}, // cyan constexpr int win_cyan = win_green | win_blue;
{"\033[37m", "\033[1m\033[37m"} // white constexpr int win_white = win_red | win_cyan;
};
return colors[static_cast<size_t>(value)][static_cast<size_t>(face)]; int tty_codes[] = {
-1, // reset
-2, // reset_endl
win_black, // black
win_red, // red
win_green, // green
win_yellow, // yellow
win_blue, // blue
win_magenta, // magenta
win_cyan, // cyan
win_white, // white
win_black, // bold black
win_red, // bold red
win_green, // bold green
win_yellow, // bold yellow
win_blue, // bold blue
win_magenta, // bold magenta
win_cyan, // bold cyan
win_white // bold white
};
void set_term_color(std::ostream& out, int c) {
static WORD default_color = 0xFFFF;
auto hdl = GetStdHandle(&out == &std::cout ? STD_OUTPUT_HANDLE
: STD_ERROR_HANDLE);
if (default_color == 0xFFFF) {
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo(hdl, &info))
return;
default_color = info.wAttributes;
}
// always pick background from the default color
auto x = c < 0 ? default_color
: static_cast<WORD>((0xF0 & default_color) | (0x0F & c));
SetConsoleTextAttribute(hdl, x);
if (c == -2)
out << '\n';
}
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#define ISATTY_FUN ::_isatty
#else // POSIX-compatible terminals
const char* tty_codes[] = {
"\033[0m", // reset
"\033[0m\n", // reset_endl
"\033[30m", // black
"\033[31m", // red
"\033[32m", // green
"\033[33m", // yellow
"\033[34m", // blue
"\033[35m", // magenta
"\033[36m", // cyan
"\033[37m", // white
"\033[1m\033[30m", // bold_black
"\033[1m\033[31m", // bold_red
"\033[1m\033[32m", // bold_green
"\033[1m\033[33m", // bold_yellow
"\033[1m\033[34m", // bold_blue
"\033[1m\033[35m", // bold_magenta
"\033[1m\033[36m", // bold_cyan
"\033[1m\033[37m" // bold_white
};
void set_term_color(std::ostream& out, const char* x) {
out << x;
}
#define ISATTY_FUN ::isatty
#endif #endif
bool is_tty(const std::ostream& out) {
if (&out == &std::cout)
return ISATTY_FUN(STDOUT_FILENO) != 0;
if (&out == &std::cerr || &out == &std::clog)
return ISATTY_FUN(STDERR_FILENO) != 0;
return false;
}
} // namespace <anonymous>
std::ostream& operator<<(std::ostream& out, term x) {
if (is_tty(out))
set_term_color(out, tty_codes[static_cast<size_t>(x)]);
return out;
} }
} // namespace caf } // namespace caf
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include <iostream> #include <iostream>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/color.hpp" #include "caf/term.hpp"
#include "caf/optional.hpp" #include "caf/optional.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
...@@ -81,9 +81,9 @@ public: ...@@ -81,9 +81,9 @@ public:
size_t expected_failures() const; size_t expected_failures() const;
void pass(const std::string& msg); void pass();
void fail(const std::string& msg, bool expected); void fail(bool expected);
const std::string& name() const; const std::string& name() const;
...@@ -141,20 +141,30 @@ public: ...@@ -141,20 +141,30 @@ public:
massive = 4 massive = 4
}; };
static bool init(int lvl_cons, int lvl_file, const std::string& logfile);
static logger& instance();
template <class T>
void log(level lvl, const T& x) {
if (lvl <= level_console_) {
*console_ << x;
}
if (lvl <= level_file_) {
file_ << x;
}
}
/// Output stream for logging purposes. /// Output stream for logging purposes.
class stream { class stream {
public: public:
stream(logger& l, level lvl); stream(logger& parent, level lvl);
stream(stream&&); stream(const stream&) = default;
template <class T> template <class T>
typename std::enable_if< stream& operator<<(const T& x) {
!std::is_same<T, char*>::value, parent_.log(lvl_, x);
stream&
>::type
operator<<(const T& x) {
buf_ << x;
return *this; return *this;
} }
...@@ -165,53 +175,26 @@ public: ...@@ -165,53 +175,26 @@ public:
return *this << *x; return *this << *x;
} }
stream& operator<<(const char& c);
stream& operator<<(const char* cstr);
stream& operator<<(const std::string& x);
std::string str() const;
private: private:
void flush(); logger& parent_;
level lvl_;
logger& logger_;
level level_;
std::ostringstream buf_;
std::string str_;
}; };
static bool init(int lvl_cons, int lvl_file, const std::string& logfile);
static logger& instance();
template <class T>
void log(level lvl, const T& x) {
if (lvl <= level_console_) {
std::lock_guard<std::mutex> io_guard{console_mtx_};
console_ << x;
}
if (lvl <= level_file_) {
std::lock_guard<std::mutex> io_guard{file_mtx_};
file_ << x;
}
}
stream error(); stream error();
stream info(); stream info();
stream verbose(); stream verbose();
stream massive(); stream massive();
void disable_colors();
private: private:
logger(); logger();
level level_console_; level level_console_;
level level_file_; level level_file_;
std::ostream& console_; std::ostream* console_;
std::ofstream file_; std::ofstream file_;
std::mutex console_mtx_; std::ostringstream dummy_;
std::mutex file_mtx_;
}; };
/// Drives unit test execution. /// Drives unit test execution.
...@@ -270,10 +253,6 @@ public: ...@@ -270,10 +253,6 @@ public:
const std::string& tests_str, const std::string& tests_str,
const std::string& not_tests_str); const std::string& not_tests_str);
/// Retrieves a UNIX terminal color code or an empty string based on the
/// color configuration of the engine.
static const char* color(color_value v, color_face t = normal);
static const char* last_check_file(); static const char* last_check_file();
static void last_check_file(const char* file); static void last_check_file(const char* file);
...@@ -327,7 +306,7 @@ template <class T> ...@@ -327,7 +306,7 @@ template <class T>
std::ostream& operator<<(std::ostream& out, const showable_base<T>& x) { std::ostream& operator<<(std::ostream& out, const showable_base<T>& x) {
auto str = caf::deep_to_string(x.value); auto str = caf::deep_to_string(x.value);
if (str == "<unprintable>") if (str == "<unprintable>")
out << engine::color(blue) << "<unprintable>" << engine::color(reset); out << term::blue << "<unprintable>" << term::reset;
else else
out << str; out << str;
return out; return out;
...@@ -360,22 +339,22 @@ template <class T, class U> ...@@ -360,22 +339,22 @@ template <class T, class U>
bool check(test* parent, const char *file, size_t line, bool check(test* parent, const char *file, size_t line,
const char *expr, bool should_fail, bool result, const char *expr, bool should_fail, bool result,
const T& x, const U& y) { const T& x, const U& y) {
std::stringstream ss; auto out = logger::instance().massive();
if (result) { if (result) {
ss << engine::color(green) << "** " out << term::green << "** "
<< engine::color(blue) << file << engine::color(yellow) << ":" << term::blue << file << term::yellow << ":"
<< engine::color(blue) << line << fill(line) << engine::color(reset) << term::blue << line << fill(line) << term::reset
<< expr; << expr << '\n';
parent->pass(ss.str()); parent->pass();
} else { } else {
ss << engine::color(red) << "!!" out << term::red << "!! "
<< engine::color(blue) << file << engine::color(yellow) << ":" << term::blue << file << term::yellow << ":"
<< engine::color(blue) << line << fill(line) << engine::color(reset) << term::blue << line << fill(line) << term::reset
<< expr << engine::color(magenta) << " (" << expr << term::magenta << " ("
<< engine::color(red) << show(x) << engine::color(magenta) << term::red << show(x) << term::magenta
<< " !! " << engine::color(red) << show(y) << engine::color(magenta) << " !! " << term::red << show(y) << term::magenta
<< ')' << engine::color(reset); << ')' << term::reset_endl;
parent->fail(ss.str(), should_fail); parent->fail(should_fail);
} }
return result; return result;
} }
...@@ -388,9 +367,8 @@ bool check(test* parent, const char *file, size_t line, ...@@ -388,9 +367,8 @@ bool check(test* parent, const char *file, size_t line,
using caf_test_case_auto_fixture = caf::test::dummy_fixture; using caf_test_case_auto_fixture = caf::test::dummy_fixture;
#define CAF_TEST_PRINT(level, msg, colorcode) \ #define CAF_TEST_PRINT(level, msg, colorcode) \
(::caf::test::logger::instance(). level () \ (::caf::test::logger::instance().level() \
<< ::caf::test::engine::color(::caf:: colorcode ) \ << ::caf::term:: colorcode << " -> " << ::caf::term::reset << msg \
<< " -> " << ::caf::test::engine::color(::caf::reset) << msg \
<< " [line " << __LINE__ << "]\n") << " [line " << __LINE__ << "]\n")
#define CAF_TEST_PRINT_ERROR(msg) CAF_TEST_PRINT(info, msg, red) #define CAF_TEST_PRINT_ERROR(msg) CAF_TEST_PRINT(info, msg, red)
...@@ -416,12 +394,11 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture; ...@@ -416,12 +394,11 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
#define CAF_ERROR(msg) \ #define CAF_ERROR(msg) \
do { \ do { \
auto CAF_UNIQUE(__str) = CAF_TEST_PRINT_ERROR(msg).str(); \ CAF_TEST_PRINT_ERROR(msg); \
::caf::test::detail::remove_trailing_spaces(CAF_UNIQUE(__str)); \ ::caf::test::engine::current_test()->fail(false); \
::caf::test::engine::current_test()->fail(CAF_UNIQUE(__str), false); \
::caf::test::engine::last_check_file(__FILE__); \ ::caf::test::engine::last_check_file(__FILE__); \
::caf::test::engine::last_check_line(__LINE__); \ ::caf::test::engine::last_check_line(__LINE__); \
} while(false) } while (false)
#define CAF_CHECK(...) \ #define CAF_CHECK(...) \
do { \ do { \
...@@ -467,9 +444,8 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture; ...@@ -467,9 +444,8 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
#define CAF_FAIL(msg) \ #define CAF_FAIL(msg) \
do { \ do { \
auto CAF_UNIQUE(__str) = CAF_TEST_PRINT_ERROR(msg).str(); \ CAF_TEST_PRINT_ERROR(msg); \
::caf::test::detail::remove_trailing_spaces(CAF_UNIQUE(__str)); \ ::caf::test::engine::current_test()->fail(false); \
::caf::test::engine::current_test()->fail(CAF_UNIQUE(__str), false); \
::caf::test::detail::requirement_failed("test failure"); \ ::caf::test::detail::requirement_failed("test failure"); \
} while (false) } while (false)
......
...@@ -108,17 +108,14 @@ size_t test::expected_failures() const { ...@@ -108,17 +108,14 @@ size_t test::expected_failures() const {
return expected_failures_; return expected_failures_;
} }
void test::pass(const std::string& msg) { void test::pass() {
++good_; ++good_;
::caf::test::logger::instance().massive() << " " << msg << '\n';
} }
void test::fail(const std::string& msg, bool expected) { void test::fail(bool expected) {
++bad_; ++bad_;
::caf::test::logger::instance().massive() << " " << msg << '\n'; if (expected)
if (expected) {
++expected_failures_; ++expected_failures_;
}
} }
const std::string& test::name() const { const std::string& test::name() const {
...@@ -129,11 +126,11 @@ namespace detail { ...@@ -129,11 +126,11 @@ namespace detail {
[[noreturn]] void requirement_failed(const std::string& msg) { [[noreturn]] void requirement_failed(const std::string& msg) {
auto& log = logger::instance(); auto& log = logger::instance();
log.error() << engine::color(red) << " REQUIRED: " << msg log.error() << term::red << " REQUIRED: " << msg
<< engine::color(reset) << '\n' << term::reset << '\n'
<< " " << engine::color(blue) << engine::last_check_file() << " " << term::blue << engine::last_check_file()
<< engine::color(yellow) << ":" << engine::color(cyan) << term::yellow << ":" << term::cyan
<< engine::last_check_line() << engine::color(reset) << engine::last_check_line() << term::reset
<< detail::fill(engine::last_check_line()) << detail::fill(engine::last_check_line())
<< "had last successful check" << '\n'; << "had last successful check" << '\n';
abort(); abort();
...@@ -155,75 +152,31 @@ void remove_trailing_spaces(std::string& x) { ...@@ -155,75 +152,31 @@ void remove_trailing_spaces(std::string& x) {
bool check(test* parent, const char *file, size_t line, bool check(test* parent, const char *file, size_t line,
const char *expr, bool should_fail, bool result) { const char *expr, bool should_fail, bool result) {
std::stringstream ss; auto out = logger::instance().massive();
if (result) { if (result) {
ss << engine::color(green) << "** " out << term::green << "** "
<< engine::color(blue) << file << engine::color(yellow) << ":" << term::blue << file << term::yellow << ":"
<< engine::color(blue) << line << fill(line) << engine::color(reset) << term::blue << line << fill(line) << term::reset
<< expr; << expr;
parent->pass(ss.str()); parent->pass();
} else { } else {
ss << engine::color(red) << "!!" out << term::red << "!! "
<< engine::color(blue) << file << engine::color(yellow) << ":" << term::blue << file << term::yellow << ":"
<< engine::color(blue) << line << fill(line) << engine::color(reset) << term::blue << line << fill(line) << term::reset
<< expr; << expr;
parent->fail(ss.str(), should_fail); parent->fail(should_fail);
} }
return result; return result;
} }
} // namespace detail } // namespace detail
logger::stream::stream(logger& l, level lvl) : logger_(l), level_(lvl) { logger::stream::stream(logger& parent, logger::level lvl)
: parent_(parent),
lvl_(lvl) {
// nop // nop
} }
logger::stream::stream(stream&& other)
: logger_(other.logger_)
, level_(other.level_) {
// ostringstream does have swap since C++11... but not in GCC 4.8.4
buf_.str(other.buf_.str());
}
logger::stream& logger::stream::operator<<(const char& c) {
buf_ << c;
if (c == '\n') {
flush();
}
return *this;
}
logger::stream& logger::stream::operator<<(const char* cstr) {
if (*cstr == '\0') {
return *this;
}
buf_ << cstr;
if (cstr[std::strlen(cstr) - 1] == '\n') {
flush();
}
return *this;
}
logger::stream& logger::stream::operator<<(const std::string& x) {
if (x.empty())
return *this;
buf_ << x;
if (x.back() == '\n')
flush();
return *this;
}
std::string logger::stream::str() const {
return str_;
}
void logger::stream::flush() {
auto x = buf_.str();
buf_.str("");
logger_.log(level_, x);
str_ += x;
}
bool logger::init(int lvl_cons, int lvl_file, const std::string& logfile) { bool logger::init(int lvl_cons, int lvl_file, const std::string& logfile) {
instance().level_console_ = static_cast<level>(lvl_cons); instance().level_console_ = static_cast<level>(lvl_cons);
instance().level_file_ = static_cast<level>(lvl_file); instance().level_file_ = static_cast<level>(lvl_file);
...@@ -255,10 +208,19 @@ logger::stream logger::massive() { ...@@ -255,10 +208,19 @@ logger::stream logger::massive() {
return stream{*this, level::massive}; return stream{*this, level::massive};
} }
void logger::disable_colors() {
// Disable colors by piping all writes through dummy_.
// Since dummy_ is not a TTY, colors are turned off implicitly.
dummy_.copyfmt(std::cerr);
dummy_.clear(std::cerr.rdstate());
dummy_.basic_ios<char>::rdbuf(std::cerr.rdbuf());
console_ = &dummy_;
}
logger::logger() logger::logger()
: level_console_(level::error), : level_console_(level::error),
level_file_(level::error), level_file_(level::error),
console_(std::cerr) { console_(&std::cerr) {
// nop // nop
} }
...@@ -320,6 +282,8 @@ bool engine::run(bool colorize, ...@@ -320,6 +282,8 @@ bool engine::run(bool colorize,
return false; return false;
} }
auto& log = logger::instance(); auto& log = logger::instance();
if (!colorize)
log.disable_colors();
std::chrono::microseconds runtime{0}; std::chrono::microseconds runtime{0};
size_t total_suites = 0; size_t total_suites = 0;
size_t total_tests = 0; size_t total_tests = 0;
...@@ -385,11 +349,11 @@ bool engine::run(bool colorize, ...@@ -385,11 +349,11 @@ bool engine::run(bool colorize,
instance().current_test_ = t.get(); instance().current_test_ = t.get();
++tests_ran; ++tests_ran;
if (!displayed_header) { if (!displayed_header) {
log.verbose() << color(yellow) << bar << '\n' << pad << suite_name log.verbose() << term::yellow << bar << '\n' << pad << suite_name
<< '\n' << bar << color(reset) << "\n\n"; << '\n' << bar << term::reset << "\n\n";
displayed_header = true; displayed_header = true;
} }
log.verbose() << color(yellow) << "- " << color(reset) << t->name() log.verbose() << term::yellow << "- " << term::reset << t->name()
<< '\n'; << '\n';
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
watchdog::start(max_runtime()); watchdog::start(max_runtime());
...@@ -405,17 +369,17 @@ bool engine::run(bool colorize, ...@@ -405,17 +369,17 @@ bool engine::run(bool colorize,
total_good += good; total_good += good;
total_bad += bad; total_bad += bad;
total_bad_expected += t->expected_failures(); total_bad_expected += t->expected_failures();
log.verbose() << color(yellow) << " -> " << color(cyan) << good + bad log.verbose() << term::yellow << " -> " << term::cyan << good + bad
<< color(reset) << " check" << (good + bad > 1 ? "s " : " ") << term::reset << " check" << (good + bad > 1 ? "s " : " ")
<< "took " << color(cyan) << render(elapsed) << "took " << term::cyan << render(elapsed)
<< color(reset) << '\n'; << term::reset << '\n';
if (bad > 0) { if (bad > 0) {
// concat suite name + test name // concat suite name + test name
failed_tests.emplace_back(p.first); failed_tests.emplace_back(p.first);
failed_tests.back() += ":"; failed_tests.back() += ":";
failed_tests.back() += t->name(); failed_tests.back() += t->name();
log.verbose() << " (" << color(green) << good << color(reset) << '/' log.verbose() << " (" << term::green << good << term::reset << '/'
<< color(red) << bad << color(reset) << ")" << '\n'; << term::red << bad << term::reset << ")" << '\n';
} else { } else {
log.verbose() << '\n'; log.verbose() << '\n';
} }
...@@ -437,40 +401,36 @@ bool engine::run(bool colorize, ...@@ -437,40 +401,36 @@ bool engine::run(bool colorize,
auto title = std::string{"summary"}; auto title = std::string{"summary"};
auto pad = std::string((bar.size() - title.size()) / 2, ' '); auto pad = std::string((bar.size() - title.size()) / 2, ' ');
auto indent = std::string(24, ' '); auto indent = std::string(24, ' ');
log.info() << color(cyan) << bar << '\n' << pad << title << '\n' << bar log.info() << term::cyan << bar << '\n' << pad << title << '\n' << bar
<< color(reset) << "\n\n" << indent << "suites: " << color(yellow) << term::reset << "\n\n" << indent << "suites: " << term::yellow
<< total_suites << color(reset) << '\n' << indent << total_suites << term::reset << '\n' << indent
<< "tests: " << color(yellow) << total_tests << color(reset) << "tests: " << term::yellow << total_tests << term::reset
<< '\n' << indent << "checks: " << color(yellow) << '\n' << indent << "checks: " << term::yellow
<< total_good + total_bad << color(reset); << total_good + total_bad << term::reset;
if (total_bad > 0) { if (total_bad > 0) {
log.info() << " (" << color(green) << total_good << color(reset) << '/' log.info() << " (" << term::green << total_good << term::reset << '/'
<< color(red) << total_bad << color(reset) << ")"; << term::red << total_bad << term::reset << ")";
if (total_bad_expected > 0) { if (total_bad_expected > 0) {
log.info() log.info()
<< ' ' << color(cyan) << total_bad_expected << color(reset) << ' ' << term::cyan << total_bad_expected << term::reset
<< " failures expected"; << " failures expected";
} }
} }
log.info() << '\n' << indent << "time: " << color(yellow) log.info() << '\n' << indent << "time: " << term::yellow
<< render(runtime) << '\n' << color(reset) << indent << render(runtime) << '\n' << term::reset << indent
<< "success: " << "success: "
<< (total_bad == total_bad_expected ? color(green) : color(red)) << (total_bad == total_bad_expected ? term::green : term::red)
<< percent_good << "%" << color(reset) << "\n\n"; << percent_good << "%" << term::reset << "\n\n";
if (!failed_tests.empty()) { if (!failed_tests.empty()) {
log.info() << indent << "failed tests:" << '\n'; log.info() << indent << "failed tests:" << '\n';
for (auto& name : failed_tests) for (auto& name : failed_tests)
log.info() << indent << "- " << name << '\n'; log.info() << indent << "- " << name << '\n';
log.info() << '\n'; log.info() << '\n';
} }
log.info() << color(cyan) << bar << color(reset) << '\n'; log.info() << term::cyan << bar << term::reset << '\n';
return total_bad == total_bad_expected; return total_bad == total_bad_expected;
} }
const char* engine::color(color_value v, color_face t) {
return instance().colorize_ ? caf::color(v, t) : "";
}
const char* engine::last_check_file() { const char* engine::last_check_file() {
return instance().check_file_; return instance().check_file_;
} }
...@@ -583,20 +543,7 @@ int main(int argc, char** argv) { ...@@ -583,20 +543,7 @@ int main(int argc, char** argv) {
<< res.helptext << std::endl; << res.helptext << std::endl;
return 1; return 1;
} }
# ifdef CAF_WINDOWS auto colorize = res.opts.count("no-colors") == 0;
constexpr bool colorize = false;
# else
auto color_support = []() -> bool {
if (isatty(0) == 0)
return false;
char ttybuf[50];
if (ttyname_r(0, ttybuf, sizeof(ttybuf)) != 0)
return false;
char prefix[] = "/dev/tty";
return strncmp(prefix, ttybuf, sizeof(prefix) - 1) == 0;
};
auto colorize = color_support();
# endif
std::vector<char*> args; std::vector<char*> args;
if (divider < argc) { if (divider < argc) {
// make a new args vector that contains argv[0] and all remaining args // make a new args vector that contains argv[0] and all remaining args
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment