diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index 5a494f404..41e7f2c63 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -4813,6 +4813,10 @@
True
True
+
+ True
+ True
+
True
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index 02ec7491a..2c9ad27fc 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -5724,6 +5724,9 @@
test\basics
+
+ test\basics
+
test\basics
diff --git a/src/ripple/app/main/Main.cpp b/src/ripple/app/main/Main.cpp
index 8e0034ca5..d41326942 100644
--- a/src/ripple/app/main/Main.cpp
+++ b/src/ripple/app/main/Main.cpp
@@ -229,9 +229,17 @@ static int runUnitTests(
int bad_child_exits = 0;
for(auto& c : children)
{
- c.wait();
- if (c.exit_code())
+ try
+ {
+ c.wait();
+ if (c.exit_code())
+ ++bad_child_exits;
+ }
+ catch (...)
+ {
+ // wait throws if process was terminated with a signal
++bad_child_exits;
+ }
}
if (parent_runner.any_failed() || bad_child_exits)
diff --git a/src/test/basics/DetectCrash_test.cpp b/src/test/basics/DetectCrash_test.cpp
new file mode 100644
index 000000000..27213db8c
--- /dev/null
+++ b/src/test/basics/DetectCrash_test.cpp
@@ -0,0 +1,46 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include
+#include
+
+#include
+
+namespace ripple {
+namespace test {
+
+struct DetectCrash_test : public beast::unit_test::suite
+{
+ void testDetectCrash ()
+ {
+ testcase ("Detect Crash");
+ // Kill the process. This is used to test that the multi-process
+ // unit test will correctly report the crash.
+ std::terminate();
+ }
+ void run() override
+ {
+ testDetectCrash();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE_MANUAL(DetectCrash,unit_test,beast);
+
+} // test
+} // ripple
diff --git a/src/test/unit_test/multi_runner.cpp b/src/test/unit_test/multi_runner.cpp
index 58569bfae..c26f0e333 100644
--- a/src/test/unit_test/multi_runner.cpp
+++ b/src/test/unit_test/multi_runner.cpp
@@ -338,14 +338,11 @@ multi_runner_base::print_results(S& s)
template
void
-multi_runner_base::message_queue_send(std::string const& s)
+multi_runner_base::message_queue_send(MessageType mt, std::string const& s)
{
- // Even though the message queue does _not_ live in shared memory, child
- // processes (the only ones using "send" need to protect access with a mutex
- // on the OSX platform (access does not appear to need to be protection on
- // linux or windows). This is likely due to the different back end implementation
- // of message queue in boost, though that has not been confirmed.
+ // must use a mutex since the two "sends" must happen in order
std::lock_guard l{inner_->m_};
+ message_queue_->send(&mt, sizeof(mt), /*priority*/ 0);
message_queue_->send(s.c_str(), s.size(), /*priority*/ 0);
}
@@ -381,18 +378,44 @@ multi_runner_parent::multi_runner_parent()
{
std::size_t recvd_size = 0;
unsigned int priority = 0;
+ this->message_queue_->receive(
+ buf.data(), buf.size(), recvd_size, priority);
+ if (!recvd_size)
+ continue;
+ assert (recvd_size == 1);
+ MessageType mt{*reinterpret_cast(buf.data())};
+
this->message_queue_->receive(
buf.data(), buf.size(), recvd_size, priority);
if (recvd_size)
{
std::string s{buf.data(), recvd_size};
- this->os_ << s;
- this->os_.flush();
+ switch (mt)
+ {
+ case MessageType::log:
+ this->os_ << s;
+ this->os_.flush();
+ break;
+ case MessageType::test_start:
+ running_suites_.insert(std::move(s));
+ break;
+ case MessageType::test_end:
+ running_suites_.erase(s);
+ break;
+ default:
+ assert(0); // unknown message type
+ }
}
}
+ catch (std::exception const& e)
+ {
+ std::cerr << "Error: " << e.what()
+ << " reading unit test message queue.\n";
+ return;
+ }
catch (...)
{
- std::cerr << "Error reading unit test message queue.\n";
+ std::cerr << "Unknown error reading unit test message queue.\n";
return;
}
}
@@ -407,6 +430,12 @@ multi_runner_parent::~multi_runner_parent()
message_queue_thread_.join();
print_results(os_);
+
+ for (auto const& s : running_suites_)
+ {
+ os_ << "\nSuite: " << s
+ << " failed to complete. The child process may have crashed.\n";
+ }
}
bool
@@ -475,12 +504,14 @@ void
multi_runner_child::on_suite_begin(beast::unit_test::suite_info const& info)
{
suite_results_ = detail::suite_results{info.full_name()};
+ message_queue_send(MessageType::test_start, suite_results_.name);
}
void
multi_runner_child::on_suite_end()
{
results_.add(suite_results_);
+ message_queue_send(MessageType::test_end, suite_results_.name);
}
void
@@ -496,7 +527,7 @@ multi_runner_child::on_case_begin(std::string const& name)
s << job_index_ << "> ";
s << suite_results_.name
<< (case_results_.name.empty() ? "" : (" " + case_results_.name)) << '\n';
- message_queue_send(s.str());
+ message_queue_send(MessageType::log, s.str());
}
void
@@ -521,7 +552,7 @@ multi_runner_child::on_fail(std::string const& reason)
s << job_index_ << "> ";
s << "#" << case_results_.total << " failed" << (reason.empty() ? "" : ": ")
<< reason << '\n';
- message_queue_send(s.str());
+ message_queue_send(MessageType::log, s.str());
}
void
@@ -534,7 +565,7 @@ multi_runner_child::on_log(std::string const& msg)
if (num_jobs_ > 1)
s << job_index_ << "> ";
s << msg;
- message_queue_send(s.str());
+ message_queue_send(MessageType::log, s.str());
}
namespace detail {
diff --git a/src/test/unit_test/multi_runner.h b/src/test/unit_test/multi_runner.h
index dc21e0e1b..79143eaf4 100644
--- a/src/test/unit_test/multi_runner.h
+++ b/src/test/unit_test/multi_runner.h
@@ -163,7 +163,8 @@ class multi_runner_base
protected:
std::unique_ptr message_queue_;
- void message_queue_send(std::string const& s);
+ enum class MessageType : std::uint8_t {test_start, test_end, log};
+ void message_queue_send(MessageType mt, std::string const& s);
public:
multi_runner_base();
@@ -208,7 +209,8 @@ private:
std::ostream& os_;
std::atomic continue_message_queue_{true};
std::thread message_queue_thread_;
-
+ // track running suites so if a child crashes the culprit can be flagged
+ std::set running_suites_;
public:
multi_runner_parent(multi_runner_parent const&) = delete;
multi_runner_parent&
@@ -335,7 +337,7 @@ multi_runner_child::run_multi(Pred pred)
// inform the parent
std::stringstream s;
s << job_index_ << "> failed Unhandled exception in test.\n";
- message_queue_send(s.str());
+ message_queue_send(MessageType::log, s.str());
failed = true;
}
}
diff --git a/src/test/unity/basics_test_unity.cpp b/src/test/unity/basics_test_unity.cpp
index fe4357c09..569e479f5 100644
--- a/src/test/unity/basics_test_unity.cpp
+++ b/src/test/unity/basics_test_unity.cpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
#include