Commit 33b0914b authored by Dominik Charousset's avatar Dominik Charousset

Fix error handling

parent cc6e1751
...@@ -112,10 +112,11 @@ struct base_state { ...@@ -112,10 +112,11 @@ struct base_state {
return aout(self) << color << name << " (id = " << self->id() << "): "; return aout(self) << color << name << " (id = " << self->id() << "): ";
} }
virtual void init(std::string m_name, std::string m_color) { virtual bool init(std::string m_name, std::string m_color) {
name = std::move(m_name); name = std::move(m_name);
color = std::move(m_color); color = std::move(m_color);
print() << "started" << color::reset_endl; print() << "started" << color::reset_endl;
return true;
} }
virtual ~base_state() { virtual ~base_state() {
...@@ -129,7 +130,8 @@ struct base_state { ...@@ -129,7 +130,8 @@ struct base_state {
// encapsulates an HTTP request // encapsulates an HTTP request
behavior client_job(stateful_actor<base_state>* self, actor parent) { behavior client_job(stateful_actor<base_state>* self, actor parent) {
self->state.init("client-job", color::blue); if (!self->state.init("client-job", color::blue))
return {}; // returning an empty behavior terminates the actor
self->send(parent, read_atom::value, self->send(parent, read_atom::value,
"http://www.example.com/index.html", "http://www.example.com/index.html",
uint64_t{0}, uint64_t{4095}); uint64_t{0}, uint64_t{4095});
...@@ -165,7 +167,8 @@ struct client_state : base_state { ...@@ -165,7 +167,8 @@ struct client_state : base_state {
behavior client(stateful_actor<client_state>* self, actor parent) { behavior client(stateful_actor<client_state>* self, actor parent) {
using std::chrono::milliseconds; using std::chrono::milliseconds;
self->link_to(parent); self->link_to(parent);
self->state.init("client", color::green); if (!self->state.init("client", color::green))
return {}; // returning an empty behavior terminates the actor
self->send(self, next_atom::value); self->send(self, next_atom::value);
return { return {
[=](next_atom) { [=](next_atom) {
...@@ -201,13 +204,13 @@ struct curl_state : base_state { ...@@ -201,13 +204,13 @@ struct curl_state : base_state {
return size; return size;
} }
void init(std::string m_name, std::string m_color) override { bool init(std::string m_name, std::string m_color) override {
curl = curl_easy_init(); curl = curl_easy_init();
if (!curl) if (!curl)
throw std::runtime_error("Unable initialize CURL."); return false;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_state::callback); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_state::callback);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
base_state::init(std::move(m_name), std::move(m_color)); return base_state::init(std::move(m_name), std::move(m_color));
} }
CURL* curl = nullptr; CURL* curl = nullptr;
...@@ -216,7 +219,8 @@ struct curl_state : base_state { ...@@ -216,7 +219,8 @@ struct curl_state : base_state {
// manages a CURL session // manages a CURL session
behavior curl_worker(stateful_actor<curl_state>* self, actor parent) { behavior curl_worker(stateful_actor<curl_state>* self, actor parent) {
self->state.init("curl-worker", color::yellow); if (!self->state.init("curl-worker", color::yellow))
return {}; // returning an empty behavior terminates the actor
return { return {
[=](read_atom, const std::string& fname, uint64_t offset, uint64_t range) [=](read_atom, const std::string& fname, uint64_t offset, uint64_t range)
-> message { -> message {
...@@ -282,7 +286,8 @@ struct master_state : base_state { ...@@ -282,7 +286,8 @@ struct master_state : base_state {
}; };
behavior curl_master(stateful_actor<master_state>* self) { behavior curl_master(stateful_actor<master_state>* self) {
self->state.init("curl-master", color::magenta); if (!self->state.init("curl-master", color::magenta))
return {}; // returning an empty behavior terminates the actor
// spawn workers // spawn workers
for(size_t i = 0; i < num_curl_workers; ++i) for(size_t i = 0; i < num_curl_workers; ++i)
self->state.idle.push_back(self->spawn<detached+linked>(curl_worker, self)); self->state.idle.push_back(self->spawn<detached+linked>(curl_worker, self));
......
...@@ -117,7 +117,6 @@ void tester(scoped_actor& self, const Handle& hdl, int x, int y, Ts&&... xs) { ...@@ -117,7 +117,6 @@ void tester(scoped_actor& self, const Handle& hdl, int x, int y, Ts&&... xs) {
auto handle_err = [&](const error& err) { auto handle_err = [&](const error& err) {
aout(self) << "AUT (actor under test) failed: " aout(self) << "AUT (actor under test) failed: "
<< self->system().render(err) << endl; << self->system().render(err) << endl;
throw std::runtime_error("AUT responded with an error");
}; };
// first test: x + y = z // first test: x + y = z
self->request(hdl, infinite, add_atom::value, x, y).receive( self->request(hdl, infinite, add_atom::value, x, y).receive(
......
#include <cassert>
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
#include "caf/all.hpp" #include "caf/all.hpp"
...@@ -58,8 +59,7 @@ public: ...@@ -58,8 +59,7 @@ public:
} }
behavior make_behavior() override { behavior make_behavior() override {
if (size_ < 2) assert(size_ < 2);
throw std::runtime_error("size < 2 is not supported for fixed_stack");
return empty_; return empty_;
} }
......
...@@ -262,21 +262,17 @@ void client_repl(actor_system& system, const config& cfg) { ...@@ -262,21 +262,17 @@ void client_repl(actor_system& system, const config& cfg) {
}, },
[&](string& arg0, string& arg1, string& arg2) { [&](string& arg0, string& arg1, string& arg2) {
if (arg0 == "connect") { if (arg0 == "connect") {
try { char* end = nullptr;
auto lport = std::stoul(arg2); auto lport = strtoul(arg2.c_str(), &end, 10);
if (lport < std::numeric_limits<uint16_t>::max()) { if (end != arg2.c_str() + arg2.size())
cout << "\"" << arg2 << "\" is not an unsigned integer" << endl;
else if (lport > std::numeric_limits<uint16_t>::max())
cout << "\"" << arg2 << "\" > "
<< std::numeric_limits<uint16_t>::max() << endl;
else
anon_send(client, connect_atom::value, move(arg1), anon_send(client, connect_atom::value, move(arg1),
static_cast<uint16_t>(lport)); static_cast<uint16_t>(lport));
} }
else {
cout << lport << " is not a valid port" << endl;
}
}
catch (std::exception&) {
cout << "\"" << arg2 << "\" is not an unsigned integer"
<< endl;
}
}
else { else {
auto x = toint(arg0); auto x = toint(arg0);
auto op = plus_or_minus(arg1); auto op = plus_or_minus(arg1);
......
...@@ -39,18 +39,18 @@ using calculator = typed_actor<replies_to<add_atom, int, int>::with<int>, ...@@ -39,18 +39,18 @@ using calculator = typed_actor<replies_to<add_atom, int, int>::with<int>,
calculator::behavior_type calculator_fun(calculator::pointer self) { calculator::behavior_type calculator_fun(calculator::pointer self) {
return { return {
[=](add_atom, int a, int b) -> int { [=](add_atom, int a, int b) -> int {
aout(self) << "received task from a remote node" << std::endl; aout(self) << "received task from a remote node" << endl;
return a + b; return a + b;
}, },
[=](sub_atom, int a, int b) -> int { [=](sub_atom, int a, int b) -> int {
aout(self) << "received task from a remote node" << std::endl; aout(self) << "received task from a remote node" << endl;
return a - b; return a - b;
} }
}; };
} }
// removes leading and trailing whitespaces // removes leading and trailing whitespaces
string trim(std::string s) { string trim(string s) {
auto not_space = [](char c) { return !isspace(c); }; auto not_space = [](char c) { return !isspace(c); };
// trim left // trim left
s.erase(s.begin(), find_if(s.begin(), s.end(), not_space)); s.erase(s.begin(), find_if(s.begin(), s.end(), not_space));
...@@ -65,15 +65,13 @@ void client_repl(function_view<calculator> f) { ...@@ -65,15 +65,13 @@ void client_repl(function_view<calculator> f) {
cout << "Usage:" << endl cout << "Usage:" << endl
<< " quit : terminate program" << endl << " quit : terminate program" << endl
<< " <x> + <y> : adds two integers" << endl << " <x> + <y> : adds two integers" << endl
<< " <x> - <y> : subtracts two integers" << endl << " <x> - <y> : subtracts two integers" << endl << endl;
<< endl;
}; };
usage(); usage();
// read next line, split it, and evaluate user input // read next line, split it, and evaluate user input
string line; string line;
while (std::getline(std::cin, line)) { while (std::getline(std::cin, line)) {
line = trim(std::move(line)); // ignore leading and trailing whitespaces if ((line = trim(std::move(line))) == "quit")
if (line == "quit")
return; return;
std::vector<string> words; std::vector<string> words;
split(words, line, is_any_of(" "), token_compress_on); split(words, line, is_any_of(" "), token_compress_on);
...@@ -81,18 +79,20 @@ void client_repl(function_view<calculator> f) { ...@@ -81,18 +79,20 @@ void client_repl(function_view<calculator> f) {
usage(); usage();
continue; continue;
} }
try { auto to_int = [](const string& str) -> optional<int> {
auto x = stoi(words[0]); char* end = nullptr;
auto y = stoi(words[2]); auto res = strtol(str.c_str(), &end, 10);
if (words[1] == "+") if (end == str.c_str() + str.size())
std::cout << "= " << f(add_atom::value, x, y) << std::endl; return static_cast<int>(res);
else if (words[1] == "-") return none;
std::cout << "= " << f(sub_atom::value, x, y) << std::endl; };
else auto x = to_int(words[0]);
usage(); auto y = to_int(words[2]);
} catch(...) { if (!x || !y || (words[1] != "+" && words[1] != "-"))
usage(); usage();
} else
cout << " = " << (words[1] == "+" ? f(add_atom::value, *x, *y)
: f(sub_atom::value, *x, *y)) << "\n";
} }
} }
...@@ -105,28 +105,28 @@ struct config : actor_system_config { ...@@ -105,28 +105,28 @@ struct config : actor_system_config {
.add(server_mode, "server-mode,s", "enable server mode"); .add(server_mode, "server-mode,s", "enable server mode");
} }
uint16_t port = 0; uint16_t port = 0;
std::string host = "localhost"; string host = "localhost";
bool server_mode = false; bool server_mode = false;
}; };
void server(actor_system& system, const config& cfg) { void server(actor_system& system, const config& cfg) {
auto res = system.middleman().open(cfg.port); auto res = system.middleman().open(cfg.port);
if (!res) { if (!res) {
std::cerr << "*** cannot open port: " cerr << "*** cannot open port: "
<< system.render(res.error()) << std::endl; << system.render(res.error()) << endl;
return; return;
} }
std::cout << "*** running on port: " cout << "*** running on port: "
<< *res << std::endl << *res << endl
<< "*** press <enter> to shutdown server" << std::endl; << "*** press <enter> to shutdown server" << endl;
getchar(); getchar();
} }
void client(actor_system& system, const config& cfg) { void client(actor_system& system, const config& cfg) {
auto node = system.middleman().connect(cfg.host, cfg.port); auto node = system.middleman().connect(cfg.host, cfg.port);
if (!node) { if (!node) {
std::cerr << "*** connect failed: " cerr << "*** connect failed: "
<< system.render(node.error()) << std::endl; << system.render(node.error()) << endl;
return; return;
} }
auto type = "calculator"; // type of the actor we wish to spawn auto type = "calculator"; // type of the actor we wish to spawn
...@@ -135,8 +135,8 @@ void client(actor_system& system, const config& cfg) { ...@@ -135,8 +135,8 @@ void client(actor_system& system, const config& cfg) {
auto worker = system.middleman().remote_spawn<calculator>(*node, type, auto worker = system.middleman().remote_spawn<calculator>(*node, type,
args, tout); args, tout);
if (!worker) { if (!worker) {
std::cerr << "*** remote spawn failed: " cerr << "*** remote spawn failed: "
<< system.render(worker.error()) << std::endl; << system.render(worker.error()) << endl;
return; return;
} }
// start using worker in main loop // start using worker in main loop
......
...@@ -86,6 +86,7 @@ void blocking_actor::launch(execution_unit*, bool, bool hide) { ...@@ -86,6 +86,7 @@ void blocking_actor::launch(execution_unit*, bool, bool hide) {
CAF_ASSERT(dynamic_cast<blocking_actor*>(this_ptr) != 0); CAF_ASSERT(dynamic_cast<blocking_actor*>(this_ptr) != 0);
auto self = static_cast<blocking_actor*>(this_ptr); auto self = static_cast<blocking_actor*>(this_ptr);
error rsn; error rsn;
# ifndef CAF_NO_EXCEPTIONS
try { try {
self->act(); self->act();
rsn = self->fail_state_; rsn = self->fail_state_;
...@@ -99,6 +100,11 @@ void blocking_actor::launch(execution_unit*, bool, bool hide) { ...@@ -99,6 +100,11 @@ void blocking_actor::launch(execution_unit*, bool, bool hide) {
catch (...) { catch (...) {
// simply ignore exception // simply ignore exception
} }
# else
self->act();
rsn = self->fail_state_;
self->on_exit();
# endif
self->cleanup(std::move(rsn), self->context()); self->cleanup(std::move(rsn), self->context());
ptr->home_system->dec_detached_threads(); ptr->home_system->dec_detached_threads();
}, ctrl()).detach(); }, ctrl()).detach();
......
...@@ -79,7 +79,7 @@ struct fixture { ...@@ -79,7 +79,7 @@ struct fixture {
}; };
void handle_err(const error& err) { void handle_err(const error& err) {
throw std::runtime_error("AUT responded with an error: " + to_string(err)); CAF_FAIL("AUT responded with an error: " + to_string(err));
} }
} // namespace <anonymous> } // namespace <anonymous>
......
...@@ -419,8 +419,7 @@ CAF_TEST(event_testee_series) { ...@@ -419,8 +419,7 @@ CAF_TEST(event_testee_series) {
result = str; result = str;
}, },
after(chrono::minutes(1)) >> [&] { after(chrono::minutes(1)) >> [&] {
CAF_ERROR("event_testee does not reply"); CAF_FAIL("event_testee does not reply");
throw runtime_error("event_testee does not reply");
} }
); );
CAF_CHECK_EQUAL(result, "wait4int"); CAF_CHECK_EQUAL(result, "wait4int");
...@@ -448,7 +447,7 @@ CAF_TEST(maybe_string_delegator_chain) { ...@@ -448,7 +447,7 @@ CAF_TEST(maybe_string_delegator_chain) {
CAF_MESSAGE("send empty string, expect error"); CAF_MESSAGE("send empty string, expect error");
self->request(aut, infinite, "").receive( self->request(aut, infinite, "").receive(
[](ok_atom, const string&) { [](ok_atom, const string&) {
throw std::logic_error("unexpected result!"); CAF_FAIL("unexpected string response");
}, },
[](const error& err) { [](const error& err) {
CAF_CHECK_EQUAL(err.category(), atom("mock")); CAF_CHECK_EQUAL(err.category(), atom("mock"));
......
...@@ -120,7 +120,7 @@ public: ...@@ -120,7 +120,7 @@ public:
bool has_pending_scribe(std::string host, uint16_t port); bool has_pending_scribe(std::string host, uint16_t port);
/// Accepts a pending connect on `hdl`. /// Accepts a pending connect on `hdl`.
void accept_connection(accept_handle hdl); bool accept_connection(accept_handle hdl);
/// Reads data from the external input buffer until /// Reads data from the external input buffer until
/// the configured read policy no longer allows receiving. /// the configured read policy no longer allows receiving.
......
...@@ -608,30 +608,22 @@ behavior basp_broker::make_behavior() { ...@@ -608,30 +608,22 @@ behavior basp_broker::make_behavior() {
CAF_LOG_WARNING("invalid handle"); CAF_LOG_WARNING("invalid handle");
return; return;
} }
try { auto res = assign_tcp_doorman(hdl);
assign_tcp_doorman(hdl); if (res) {
}
catch (...) {
CAF_LOG_DEBUG("failed to assign doorman from handle");
return;
}
if (whom) if (whom)
system().registry().put(whom->id(), whom); system().registry().put(whom->id(), whom);
state.instance.add_published_actor(port, whom, std::move(sigs)); state.instance.add_published_actor(port, whom, std::move(sigs));
} else {
CAF_LOG_DEBUG("failed to assign doorman from handle"
<< CAF_ARG(whom));
}
}, },
// received from middleman actor (delegated) // received from middleman actor (delegated)
[=](connect_atom, connection_handle hdl, uint16_t port) { [=](connect_atom, connection_handle hdl, uint16_t port) {
CAF_LOG_TRACE(CAF_ARG(hdl.id())); CAF_LOG_TRACE(CAF_ARG(hdl.id()));
auto rp = make_response_promise(); auto rp = make_response_promise();
try { auto res = assign_tcp_scribe(hdl);
assign_tcp_scribe(hdl); if (res) {
}
catch (std::exception& e) {
CAF_LOG_DEBUG("failed to assign scribe from handle: " << e.what());
CAF_IGNORE_UNUSED(e);
rp.deliver(sec::failed_to_assign_scribe_from_handle);
return;
}
auto& ctx = state.ctx[hdl]; auto& ctx = state.ctx[hdl];
ctx.hdl = hdl; ctx.hdl = hdl;
ctx.remote_port = port; ctx.remote_port = port;
...@@ -639,6 +631,10 @@ behavior basp_broker::make_behavior() { ...@@ -639,6 +631,10 @@ behavior basp_broker::make_behavior() {
ctx.callback = rp; ctx.callback = rp;
// await server handshake // await server handshake
configure_read(hdl, receive_policy::exactly(basp::header_size)); configure_read(hdl, receive_policy::exactly(basp::header_size));
} else {
CAF_LOG_DEBUG("failed to assign scribe from handle" << CAF_ARG(res));
rp.deliver(std::move(res.error()));
}
}, },
[=](delete_atom, const node_id& nid, actor_id aid) { [=](delete_atom, const node_id& nid, actor_id aid) {
CAF_LOG_TRACE(CAF_ARG(nid) << ", " << CAF_ARG(aid)); CAF_LOG_TRACE(CAF_ARG(nid) << ", " << CAF_ARG(aid));
...@@ -660,13 +656,10 @@ behavior basp_broker::make_behavior() { ...@@ -660,13 +656,10 @@ behavior basp_broker::make_behavior() {
// it is well-defined behavior to not have an actor published here, // it is well-defined behavior to not have an actor published here,
// hence the result can be ignored safely // hence the result can be ignored safely
state.instance.remove_published_actor(port, nullptr); state.instance.remove_published_actor(port, nullptr);
try { auto res = close(hdl_by_port(port));
close(hdl_by_port(port)); if (res)
}
catch (std::exception&) {
return sec::cannot_close_invalid_port;
}
return unit; return unit;
return sec::cannot_close_invalid_port;
}, },
[=](get_atom, const node_id& x) [=](get_atom, const node_id& x)
-> std::tuple<node_id, std::string, uint16_t> { -> std::tuple<node_id, std::string, uint16_t> {
......
...@@ -236,7 +236,6 @@ strong_actor_ptr middleman::remote_lookup(atom_value name, const node_id& nid) { ...@@ -236,7 +236,6 @@ strong_actor_ptr middleman::remote_lookup(atom_value name, const node_id& nid) {
auto basp = named_broker<basp_broker>(atom("BASP")); auto basp = named_broker<basp_broker>(atom("BASP"));
strong_actor_ptr result; strong_actor_ptr result;
scoped_actor self{system(), true}; scoped_actor self{system(), true};
try {
self->send(basp, forward_atom::value, nid, atom("ConfigServ"), self->send(basp, forward_atom::value, nid, atom("ConfigServ"),
make_message(get_atom::value, name)); make_message(get_atom::value, name));
self->receive( self->receive(
...@@ -247,9 +246,6 @@ strong_actor_ptr middleman::remote_lookup(atom_value name, const node_id& nid) { ...@@ -247,9 +246,6 @@ strong_actor_ptr middleman::remote_lookup(atom_value name, const node_id& nid) {
// nop // nop
} }
); );
} catch (std::exception&) {
// nop
}
return result; return result;
} }
......
...@@ -300,15 +300,15 @@ bool test_multiplexer::has_pending_scribe(std::string x, uint16_t y) { ...@@ -300,15 +300,15 @@ bool test_multiplexer::has_pending_scribe(std::string x, uint16_t y) {
return scribes_.count(std::make_pair(std::move(x), y)) > 0; return scribes_.count(std::make_pair(std::move(x), y)) > 0;
} }
void test_multiplexer::accept_connection(accept_handle hdl) { bool test_multiplexer::accept_connection(accept_handle hdl) {
if (passive_mode(hdl)) if (passive_mode(hdl))
return; return false;
auto& dd = doorman_data_[hdl]; auto& dd = doorman_data_[hdl];
if (!dd.ptr) if (!dd.ptr)
throw std::logic_error("accept_connection: this doorman was not " return false;
"assigned to a broker yet");
if (!dd.ptr->new_connection()) if (!dd.ptr->new_connection())
passive_mode(hdl) = true; passive_mode(hdl) = true;
return true;
} }
void test_multiplexer::read_data(connection_handle hdl) { void test_multiplexer::read_data(connection_handle hdl) {
......
...@@ -282,7 +282,7 @@ public: ...@@ -282,7 +282,7 @@ public:
auto hdl = n.connection; auto hdl = n.connection;
mpx_->add_pending_connect(src, hdl); mpx_->add_pending_connect(src, hdl);
mpx_->assign_tcp_scribe(aut(), hdl); mpx_->assign_tcp_scribe(aut(), hdl);
mpx_->accept_connection(src); CAF_REQUIRE(mpx_->accept_connection(src));
// technically, the server handshake arrives // technically, the server handshake arrives
// before we send the client handshake // before we send the client handshake
mock(hdl, mock(hdl,
......
...@@ -182,7 +182,7 @@ public: ...@@ -182,7 +182,7 @@ public:
// "open" a new connection to our server // "open" a new connection to our server
mpx_->add_pending_connect(acceptor_, connection_); mpx_->add_pending_connect(acceptor_, connection_);
mpx_->assign_tcp_scribe(aut_ptr_, connection_); mpx_->assign_tcp_scribe(aut_ptr_, connection_);
mpx_->accept_connection(acceptor_); CAF_REQUIRE(mpx_->accept_connection(acceptor_));
} }
~fixture() { ~fixture() {
......
...@@ -120,14 +120,7 @@ public: ...@@ -120,14 +120,7 @@ public:
namespace detail { namespace detail {
// thrown when a required check fails [[noreturn]] void requirement_failed(const std::string& msg);
class require_error : std::logic_error {
public:
require_error(const std::string& msg);
require_error(const require_error&) = default;
require_error(require_error&&) = default;
~require_error() noexcept;
};
// constructs spacing given a line number. // constructs spacing given a line number.
const char* fill(size_t line); const char* fill(size_t line);
...@@ -501,53 +494,49 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture; ...@@ -501,53 +494,49 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
auto CAF_UNIQUE(__str) = CAF_TEST_PRINT_ERROR(msg).str(); \ auto CAF_UNIQUE(__str) = CAF_TEST_PRINT_ERROR(msg).str(); \
::caf::test::detail::remove_trailing_spaces(CAF_UNIQUE(__str)); \ ::caf::test::detail::remove_trailing_spaces(CAF_UNIQUE(__str)); \
::caf::test::engine::current_test()->fail(CAF_UNIQUE(__str), false); \ ::caf::test::engine::current_test()->fail(CAF_UNIQUE(__str), false); \
throw ::caf::test::detail::require_error{"test failure"}; \ ::caf::test::detail::requirement_failed("test failure"); \
} while(false) } while (false)
#define CAF_REQUIRE(...) \ #define CAF_REQUIRE(...) \
do { \ do { \
auto CAF_UNIQUE(__result) = \ auto CAF_UNIQUE(__result) = ::caf::test::detail::check( \
::caf::test::detail::check(::caf::test::engine::current_test(), \ ::caf::test::engine::current_test(), __FILE__, __LINE__, #__VA_ARGS__, \
__FILE__, __LINE__, #__VA_ARGS__, false, \ false, static_cast<bool>(__VA_ARGS__)); \
static_cast<bool>(__VA_ARGS__)); \ if (!CAF_UNIQUE(__result)) \
if (! CAF_UNIQUE(__result)) { \ ::caf::test::detail::requirement_failed(#__VA_ARGS__); \
throw ::caf::test::detail::require_error{#__VA_ARGS__}; \
} \
::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_REQUIRE_PRED(pred, x_expr, y_expr) \ #define CAF_REQUIRE_PRED(pred, x_expr, y_expr) \
do { \ do { \
const auto& x_val___ = x_expr; \ const auto& x_val___ = x_expr; \
const auto& y_val___ = y_expr; \ const auto& y_val___ = y_expr; \
auto CAF_UNIQUE(__result) = \ auto CAF_UNIQUE(__result) = ::caf::test::detail::check( \
::caf::test::detail::check(::caf::test::engine::current_test(), \ ::caf::test::engine::current_test(), __FILE__, __LINE__, \
__FILE__, __LINE__, CAF_PRED_EXPR(pred, x_expr, y_expr), false, \ CAF_PRED_EXPR(pred, x_expr, y_expr), false, x_val___ pred y_val___, \
x_val___ pred y_val___, x_val___, y_val___); \ x_val___, y_val___); \
if (! CAF_UNIQUE(__result)) { \ if (!CAF_UNIQUE(__result)) \
throw ::caf::test::detail::require_error{ \ ::caf::test::detail::requirement_failed( \
CAF_PRED_EXPR(pred, x_expr, y_expr)}; \ CAF_PRED_EXPR(pred, x_expr, y_expr)); \
} \
::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_REQUIRE_FUNC(func, x_expr, y_expr) \ #define CAF_REQUIRE_FUNC(func, x_expr, y_expr) \
do { \ do { \
const auto& x_val___ = x_expr; \ const auto& x_val___ = x_expr; \
const auto& y_val___ = y_expr; \ const auto& y_val___ = y_expr; \
auto CAF_UNIQUE(__result) = \ auto CAF_UNIQUE(__result) = ::caf::test::detail::check( \
::caf::test::detail::check(::caf::test::engine::current_test(), \ ::caf::test::engine::current_test(), __FILE__, __LINE__, \
__FILE__, __LINE__, CAF_FUNC_EXPR(func, x_expr, y_expr), false, \ CAF_FUNC_EXPR(func, x_expr, y_expr), false, func(x_val___, y_val___), \
func(x_val___, y_val___), x_val___, y_val___); \ x_val___, y_val___); \
if (! CAF_UNIQUE(__result)) { \ if (!CAF_UNIQUE(__result)) \
throw ::caf::test::detail::require_error{ \ ::caf::test::detail::requirement_failed( \
CAF_FUNC_EXPR(func, x_expr, y_expr)}; \ CAF_FUNC_EXPR(func, x_expr, y_expr)); \
} \
::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_TEST(name) \ #define CAF_TEST(name) \
namespace { \ namespace { \
......
...@@ -29,6 +29,12 @@ ...@@ -29,6 +29,12 @@
#include <algorithm> #include <algorithm>
#include <condition_variable> #include <condition_variable>
#include "caf/config.hpp"
#ifndef CAF_WINDOWS
#include <unistd.h>
#endif
#include "caf/message_builder.hpp" #include "caf/message_builder.hpp"
#include "caf/message_handler.hpp" #include "caf/message_handler.hpp"
#include "caf/string_algorithms.hpp" #include "caf/string_algorithms.hpp"
...@@ -121,24 +127,27 @@ const std::string& test::name() const { ...@@ -121,24 +127,27 @@ const std::string& test::name() const {
namespace detail { namespace detail {
require_error::require_error(const std::string& msg) : std::logic_error(msg) { [[noreturn]] void requirement_failed(const std::string& msg) {
// nop auto& log = logger::instance();
} log.error() << engine::color(red) << " REQUIRED: " << msg
<< engine::color(reset) << '\n'
require_error::~require_error() noexcept { << " " << engine::color(blue) << engine::last_check_file()
// nop << engine::color(yellow) << ":" << engine::color(cyan)
<< engine::last_check_line() << engine::color(reset)
<< detail::fill(engine::last_check_line())
<< "had last successful check" << '\n';
abort();
} }
const char* fill(size_t line) { const char* fill(size_t line) {
if (line < 10) { if (line < 10)
return " "; return " ";
} else if (line < 100) { else if (line < 100)
return " "; return " ";
} else if (line < 1000) { else if (line < 1000)
return " "; return " ";
} else { else
return " "; return " ";
}
} }
void remove_trailing_spaces(std::string& x) { void remove_trailing_spaces(std::string& x) {
...@@ -325,9 +334,8 @@ bool engine::run(bool colorize, ...@@ -325,9 +334,8 @@ bool engine::run(bool colorize,
size_t total_bad = 0; size_t total_bad = 0;
size_t total_bad_expected = 0; size_t total_bad_expected = 0;
auto bar = '+' + std::string(70, '-') + '+'; auto bar = '+' + std::string(70, '-') + '+';
auto failed_require = false; #if (!defined(__clang__) && defined(__GNUC__) && __GNUC__ == 4 \
# if (! defined(__clang__) && defined(__GNUC__) \ && __GNUC_MINOR__ < 9) \
&& __GNUC__ == 4 && __GNUC_MINOR__ < 9) \
|| (defined(__clang__) && !defined(_LIBCPP_VERSION)) || (defined(__clang__) && !defined(_LIBCPP_VERSION))
// regex implementation is broken prior to 4.9 // regex implementation is broken prior to 4.9
using strvec = std::vector<std::string>; using strvec = std::vector<std::string>;
...@@ -358,12 +366,10 @@ bool engine::run(bool colorize, ...@@ -358,12 +366,10 @@ bool engine::run(bool colorize,
std::regex not_suites; std::regex not_suites;
std::regex not_tests; std::regex not_tests;
// a default constructored regex matches is not equal to an "empty" regex // a default constructored regex matches is not equal to an "empty" regex
if (!not_suites_str.empty()) { if (!not_suites_str.empty())
not_suites.assign(not_suites_str); not_suites.assign(not_suites_str);
} if (!not_tests_str.empty())
if (!not_tests_str.empty()) {
not_tests.assign(not_tests_str); not_tests.assign(not_tests_str);
}
auto enabled = [](const std::regex& whitelist, auto enabled = [](const std::regex& whitelist,
const std::regex& blacklist, const std::regex& blacklist,
const std::string& x) { const std::string& x) {
...@@ -394,11 +400,7 @@ bool engine::run(bool colorize, ...@@ -394,11 +400,7 @@ bool engine::run(bool colorize,
<< '\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());
try {
t->run(); t->run();
} catch (const detail::require_error&) {
failed_require = true;
}
watchdog::stop(); watchdog::stop();
auto stop = std::chrono::high_resolution_clock::now(); auto stop = std::chrono::high_resolution_clock::now();
auto elapsed = auto elapsed =
...@@ -407,13 +409,6 @@ bool engine::run(bool colorize, ...@@ -407,13 +409,6 @@ bool engine::run(bool colorize,
++total_tests; ++total_tests;
size_t good = t->good(); size_t good = t->good();
size_t bad = t->bad(); size_t bad = t->bad();
if (failed_require) {
log.error() << color(red) << " REQUIRED" << color(reset) << '\n'
<< " " << color(blue) << last_check_file()
<< color(yellow) << ":" << color(cyan) << last_check_line()
<< color(reset) << detail::fill(last_check_line())
<< "had last successful check" << '\n';
}
total_good += good; total_good += good;
total_bad += bad; total_bad += bad;
total_bad_expected += t->expected_failures(); total_bad_expected += t->expected_failures();
...@@ -431,21 +426,14 @@ bool engine::run(bool colorize, ...@@ -431,21 +426,14 @@ bool engine::run(bool colorize,
} else { } else {
log.verbose() << '\n'; log.verbose() << '\n';
} }
if (failed_require) {
break;
}
} }
// only count suites which have executed one or more tests // only count suites which have executed one or more tests
if (tests_ran > 0) { if (tests_ran > 0) {
++total_suites; ++total_suites;
} }
if (displayed_header) { if (displayed_header)
log.verbose() << '\n'; log.verbose() << '\n';
} }
if (failed_require) {
break;
}
}
unsigned percent_good = 100; unsigned percent_good = 100;
if (total_bad > 0) { if (total_bad > 0) {
auto tmp = (100000.0 * static_cast<double>(total_good)) auto tmp = (100000.0 * static_cast<double>(total_good))
...@@ -602,10 +590,19 @@ int main(int argc, char** argv) { ...@@ -602,10 +590,19 @@ int main(int argc, char** argv) {
<< res.helptext << std::endl; << res.helptext << std::endl;
return 1; return 1;
} }
# ifndef CAF_WINDOWS # ifdef CAF_WINDOWS
auto colorize = res.opts.count("no-colors") == 0; constexpr bool colorize = false;
# else # else
auto colorize = false; auto color_support = []() -> bool {
if (!isatty(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 # endif
std::vector<char*> args; std::vector<char*> args;
if (divider < argc) { if (divider < argc) {
...@@ -630,14 +627,8 @@ int main(int argc, char** argv) { ...@@ -630,14 +627,8 @@ int main(int argc, char** argv) {
#ifndef CAF_TEST_NO_MAIN #ifndef CAF_TEST_NO_MAIN
int main(int argc, char** argv) { int main(int argc, char** argv) {
try {
return caf::test::main(argc, argv); return caf::test::main(argc, argv);
} catch (std::exception& e) {
std::cerr << "exception " << typeid(e).name() << ": "
<< e.what() << std::endl;
}
return 1;
} }
#endif #endif // CAF_TEST_UNIT_TEST_IMPL_HPP
#endif // CAF_TEST_UNIT_TEST_IMPL_HPP #endif // CAF_TEST_UNIT_TEST_IMPL_HPP
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