mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
Improved support for exceptions in threads spawned by unit tests:
Unit tests that wish to spawn threads for testing concurrency may now do so by using unit_test::thread as a replacement for std::thread. These threads propagate unhandled exceptions to the unit test, and work with the abort on failure feature.
This commit is contained in:
committed by
Edward Hennis
parent
adc69e72df
commit
faf91d6697
@@ -29,6 +29,8 @@
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace unit_test {
|
namespace unit_test {
|
||||||
|
|
||||||
|
class thread;
|
||||||
|
|
||||||
/** A testsuite class.
|
/** A testsuite class.
|
||||||
Derived classes execute a series of testcases, where each testcase is
|
Derived classes execute a series of testcases, where each testcase is
|
||||||
a series of pass/fail tests. To provide a unit test using this class,
|
a series of pass/fail tests. To provide a unit test using this class,
|
||||||
@@ -46,9 +48,19 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool abort_ = false;
|
bool abort_ = false;
|
||||||
|
bool aborted_ = false;
|
||||||
runner* runner_ = nullptr;
|
runner* runner_ = nullptr;
|
||||||
|
|
||||||
struct abort_exception;
|
// This exception is thrown internally to stop the current suite
|
||||||
|
// in the event of a failure, if the option to stop is set.
|
||||||
|
struct abort_exception : public std::exception
|
||||||
|
{
|
||||||
|
char const*
|
||||||
|
what() const noexcept override
|
||||||
|
{
|
||||||
|
return "suite aborted";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Memberspace
|
// Memberspace
|
||||||
class log_t
|
class log_t
|
||||||
@@ -105,16 +117,6 @@ private:
|
|||||||
/** @} */
|
/** @} */
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hacks to make this header-only
|
|
||||||
|
|
||||||
template <class = void>
|
|
||||||
void
|
|
||||||
run (runner& r);
|
|
||||||
|
|
||||||
template <class = void>
|
|
||||||
void
|
|
||||||
do_fail (std::string const& reason);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Type for scoped stream logging.
|
/** Type for scoped stream logging.
|
||||||
To use this type, declare a local variable of the type
|
To use this type, declare a local variable of the type
|
||||||
@@ -180,42 +182,34 @@ public:
|
|||||||
unexpected (Condition shouldBeFalse, std::string const& reason = "");
|
unexpected (Condition shouldBeFalse, std::string const& reason = "");
|
||||||
|
|
||||||
/** Record a successful test condition. */
|
/** Record a successful test condition. */
|
||||||
|
template <class = void>
|
||||||
void
|
void
|
||||||
pass()
|
pass();
|
||||||
{
|
|
||||||
runner_->pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Record a failure. */
|
/** Record a failure. */
|
||||||
|
template <class = void>
|
||||||
void
|
void
|
||||||
fail (std::string const& reason = "");
|
fail (std::string const& reason = "");
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class thread;
|
||||||
|
|
||||||
/** Runs the suite. */
|
/** Runs the suite. */
|
||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
run() = 0;
|
run() = 0;
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
void
|
||||||
|
propagate_abort();
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
void
|
||||||
|
run (runner& r);
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
// This exception is thrown internally to stop the current suite
|
|
||||||
// in the event of a failure, if the option to stop is set.
|
|
||||||
struct suite::abort_exception : public std::exception
|
|
||||||
{
|
|
||||||
char const*
|
|
||||||
what() const noexcept override;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline
|
|
||||||
char const*
|
|
||||||
suite::abort_exception::what() const noexcept
|
|
||||||
{
|
|
||||||
return "suite aborted on failed condition";
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
inline
|
inline
|
||||||
abstract_ostream::scoped_stream_type
|
abstract_ostream::scoped_stream_type
|
||||||
@@ -323,6 +317,66 @@ suite::testcase_t::operator<< (T const& t)
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
suite::operator() (runner& r)
|
||||||
|
{
|
||||||
|
run (r);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Condition>
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
suite::expect (Condition shouldBeTrue, std::string const& reason)
|
||||||
|
{
|
||||||
|
if (shouldBeTrue)
|
||||||
|
pass();
|
||||||
|
else
|
||||||
|
fail (reason);
|
||||||
|
return shouldBeTrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Condition>
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
suite::unexpected (Condition shouldBeFalse, std::string const& reason)
|
||||||
|
{
|
||||||
|
if (! shouldBeFalse)
|
||||||
|
pass();
|
||||||
|
else
|
||||||
|
fail (reason);
|
||||||
|
return ! shouldBeFalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
void
|
||||||
|
suite::pass()
|
||||||
|
{
|
||||||
|
propagate_abort();
|
||||||
|
runner_->pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
void
|
||||||
|
suite::fail (std::string const& reason)
|
||||||
|
{
|
||||||
|
propagate_abort();
|
||||||
|
runner_->fail (reason);
|
||||||
|
if (abort_)
|
||||||
|
{
|
||||||
|
aborted_ = true;
|
||||||
|
throw abort_exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
void
|
||||||
|
suite::propagate_abort()
|
||||||
|
{
|
||||||
|
if (abort_ && aborted_)
|
||||||
|
throw abort_exception();
|
||||||
|
}
|
||||||
|
|
||||||
template <class>
|
template <class>
|
||||||
void
|
void
|
||||||
suite::run (runner& r)
|
suite::run (runner& r)
|
||||||
@@ -341,62 +395,15 @@ suite::run (runner& r)
|
|||||||
}
|
}
|
||||||
catch (std::exception const& e)
|
catch (std::exception const& e)
|
||||||
{
|
{
|
||||||
fail (std::string ("unhandled exception: ") +
|
runner_->fail ("unhandled exception: " +
|
||||||
std::string (e.what()));
|
std::string (e.what()));
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
fail ("unhandled exception");
|
runner_->fail ("unhandled exception");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
suite::operator() (runner& r)
|
|
||||||
{
|
|
||||||
run (r);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Condition>
|
|
||||||
inline
|
|
||||||
bool
|
|
||||||
suite::expect (Condition shouldBeTrue, std::string const& reason)
|
|
||||||
{
|
|
||||||
if (shouldBeTrue)
|
|
||||||
pass();
|
|
||||||
else
|
|
||||||
do_fail (reason);
|
|
||||||
return shouldBeTrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Condition>
|
|
||||||
inline
|
|
||||||
bool
|
|
||||||
suite::unexpected (Condition shouldBeFalse, std::string const& reason)
|
|
||||||
{
|
|
||||||
if (! shouldBeFalse)
|
|
||||||
pass();
|
|
||||||
else
|
|
||||||
do_fail (reason);
|
|
||||||
return ! shouldBeFalse;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
void
|
|
||||||
suite::do_fail (std::string const& reason)
|
|
||||||
{
|
|
||||||
runner_->fail (reason);
|
|
||||||
if (abort_)
|
|
||||||
throw abort_exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
suite::fail (std::string const& reason)
|
|
||||||
{
|
|
||||||
do_fail (reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // unit_test
|
} // unit_test
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
|||||||
122
beast/unit_test/thread.h
Normal file
122
beast/unit_test/thread.h
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_UNIT_TEST_THREAD_H_INCLUDED
|
||||||
|
#define BEAST_UNIT_TEST_THREAD_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/utility/noexcept.h>
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace unit_test {
|
||||||
|
|
||||||
|
/** Replacement for std::thread that handles exceptions in unit tests. */
|
||||||
|
class thread
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
suite* s_ = nullptr;
|
||||||
|
std::thread t_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using id = std::thread::id;
|
||||||
|
using native_handle_type = std::thread::native_handle_type;
|
||||||
|
|
||||||
|
thread() = default;
|
||||||
|
thread (thread const&) = delete;
|
||||||
|
thread& operator= (thread const&) = delete;
|
||||||
|
|
||||||
|
thread (thread&& other)
|
||||||
|
: s_ (other.s_)
|
||||||
|
, t_ (std::move(other.t_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
thread& operator= (thread&& other)
|
||||||
|
{
|
||||||
|
s_ = other.s_;
|
||||||
|
t_ = std::move(other.t_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class F, class... Args>
|
||||||
|
explicit
|
||||||
|
thread (suite& s, F&& f, Args&&... args)
|
||||||
|
: s_ (&s)
|
||||||
|
{
|
||||||
|
std::function<void(void)> b =
|
||||||
|
std::bind(std::forward<F>(f),
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
t_ = std::thread (&thread::run, this,
|
||||||
|
std::move(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
joinable() const
|
||||||
|
{
|
||||||
|
return t_.joinable();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::id
|
||||||
|
get_id() const
|
||||||
|
{
|
||||||
|
return t_.get_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
unsigned
|
||||||
|
hardware_concurrency() noexcept
|
||||||
|
{
|
||||||
|
return std::thread::hardware_concurrency();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
join()
|
||||||
|
{
|
||||||
|
t_.join();
|
||||||
|
s_->propagate_abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
swap (thread& other)
|
||||||
|
{
|
||||||
|
std::swap(s_, other.s_);
|
||||||
|
std::swap(t_, other.t_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
run (std::function <void(void)> f)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
catch (suite::abort_exception const&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // unit_test
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user