Improvements to beast::unit_test framework:

* Some runner member functions are now thread-safe.
* De-inline and tidy up declarations and definitions.
* arg() interface allows command lines to be passed to suites.
This commit is contained in:
Vinnie Falco
2014-10-28 06:56:19 -07:00
committed by Nik Bougalis
parent 9ab4f7bcc6
commit f5941041d4
7 changed files with 229 additions and 153 deletions

View File

@@ -31,28 +31,36 @@ namespace unit_test {
class amount class amount
{ {
private: private:
std::size_t n; std::size_t n_;
std::string const& what; std::string const& what_;
public: public:
amount (amount const&) = default; amount (amount const&) = default;
amount& operator= (amount const&) = delete; amount& operator= (amount const&) = delete;
amount (std::size_t n_, std::string const& what_) template <class = void>
: n (n_) amount (std::size_t n, std::string const& what);
, what (what_)
{
}
friend friend
std::ostream& std::ostream&
operator<< (std::ostream& s, amount const& t) operator<< (std::ostream& s, amount const& t);
{
s << t.n << " " << t.what << ((t.n != 1) ? "s" : "");
return s;
}
}; };
template <class>
amount::amount (std::size_t n, std::string const& what)
: n_ (n)
, what_ (what)
{
}
inline
std::ostream&
operator<< (std::ostream& s, amount const& t)
{
s << t.n_ << " " << t.what_ << ((t.n_ != 1) ? "s" : "");
return s;
}
} // unit_test } // unit_test
} // beast } // beast

View File

@@ -20,58 +20,79 @@
#include <beast/unit_test/amount.h> #include <beast/unit_test/amount.h>
#include <beast/unit_test/global_suites.h> #include <beast/unit_test/global_suites.h>
#include <beast/unit_test/suite.h> #include <beast/unit_test/suite.h>
#include <string>
// Include this .cpp in your project to gain access to the printing suite // Include this .cpp in your project to gain access to the printing suite
namespace beast { namespace beast {
namespace unit_test { namespace unit_test {
namespace detail { namespace detail {
/** A suite that prints the list of globally defined suites. */ /** A suite that prints the list of globally defined suites. */
class print_test : public suite class print_test : public suite
{ {
private:
template <class = void>
void
do_run();
public: public:
template <class = void>
static static
std::string std::string
prefix (suite_info const& s) prefix (suite_info const& s);
{
if (s.manual())
return "|M| ";
return " ";
}
template <class = void>
void void
print (suite_list &c) print (suite_list &c);
{
std::size_t manual (0);
for (auto const& s : c)
{
log <<
prefix (s) <<
s.full_name();
if (s.manual())
++manual;
}
log <<
amount (c.size(), "suite") << " total, " <<
amount (manual, "manual suite")
;
}
void void
run() run()
{ {
log << "------------------------------------------"; do_run();
print (global_suites());
log << "------------------------------------------";
pass();
} }
}; };
template <class>
void
print_test::do_run()
{
log << "------------------------------------------";
print (global_suites());
log << "------------------------------------------";
pass();
}
template <class>
std::string
print_test::prefix (suite_info const& s)
{
if (s.manual())
return "|M| ";
return " ";
}
template <class>
void
print_test::print (suite_list &c)
{
std::size_t manual (0);
for (auto const& s : c)
{
log <<
prefix (s) <<
s.full_name();
if (s.manual())
++manual;
}
log <<
amount (c.size(), "suite") << " total, " <<
amount (manual, "manual suite")
;
}
BEAST_DEFINE_TESTSUITE_MANUAL(print,unit_test,beast); BEAST_DEFINE_TESTSUITE_MANUAL(print,unit_test,beast);
} }
} }
} }

View File

@@ -27,8 +27,7 @@ namespace unit_test {
namespace detail { namespace detail {
// Non const container is a detail, users are not allowed to modify! template <class = void>
inline
suite_list& suite_list&
global_suites() global_suites()
{ {
@@ -36,18 +35,23 @@ global_suites()
return s; return s;
} }
// Used to insert suites during static initialization
template <class Suite> template <class Suite>
struct global_suite_instance struct insert_suite
{ {
global_suite_instance (char const* name, char const* module, template <class = void>
char const* library, bool manual) insert_suite (char const* name, char const* module,
{ char const* library, bool manual);
global_suites().insert <Suite> (
name, module, library, manual);
}
}; };
template <class Suite>
template <class>
insert_suite<Suite>::insert_suite (char const* name,
char const* module, char const* library, bool manual)
{
global_suites().insert <Suite> (
name, module, library, manual);
}
} // detail } // detail
/** Holds suites registered during static initialization. */ /** Holds suites registered during static initialization. */
@@ -58,7 +62,7 @@ global_suites()
return detail::global_suites(); return detail::global_suites();
} }
} // unit_test }
} // beast }
#endif #endif

View File

@@ -56,69 +56,80 @@ private:
std::string m_library; std::string m_library;
public: public:
template <class = void>
explicit explicit
selector (mode_t mode, std::string const& pattern = "") selector (mode_t mode, std::string const& pattern = "");
: m_mode (mode)
, m_pat (pattern)
{
if (m_mode == automatch && pattern.empty())
m_mode = all;
}
template <class = void>
bool bool
operator() (suite_info const& s) operator() (suite_info const& s);
{
switch (m_mode)
{
case automatch:
// check suite
if (m_pat == s.name())
{
m_mode = none;
return true;
}
// check module
if (m_pat == s.module())
{
m_mode = module;
m_library = s.library();
return ! s.manual();
}
// check library
if (m_pat == s.library())
{
m_mode = library;
return ! s.manual();
}
return false;
case suite:
return m_pat == s.name();
case module:
return m_pat == s.module() && ! s.manual();
case library:
return m_pat == s.library() && ! s.manual();
case none:
return false;
case all:
default:
// fall through
break;
};
return ! s.manual();
}
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template <class>
selector::selector (mode_t mode, std::string const& pattern)
: m_mode (mode)
, m_pat (pattern)
{
if (m_mode == automatch && pattern.empty())
m_mode = all;
}
template <class>
bool
selector::operator() (suite_info const& s)
{
switch (m_mode)
{
case automatch:
// check suite
if (m_pat == s.name())
{
m_mode = none;
return true;
}
// check module
if (m_pat == s.module())
{
m_mode = module;
m_library = s.library();
return ! s.manual();
}
// check library
if (m_pat == s.library())
{
m_mode = library;
return ! s.manual();
}
return false;
case suite:
return m_pat == s.name();
case module:
return m_pat == s.module() && ! s.manual();
case library:
return m_pat == s.library() && ! s.manual();
case none:
return false;
case all:
default:
// fall through
break;
};
return ! s.manual();
}
//------------------------------------------------------------------------------
// Utility functions for producing predicates to select suites. // Utility functions for producing predicates to select suites.
/** Returns a predicate that implements a smart matching rule. /** Returns a predicate that implements a smart matching rule.

View File

@@ -55,11 +55,11 @@ private:
: public const_container <std::vector <test>> : public const_container <std::vector <test>>
{ {
private: private:
std::size_t m_failed; std::size_t failed_;
public: public:
tests_t () tests_t ()
: m_failed (0) : failed_ (0)
{ {
} }
@@ -74,7 +74,7 @@ private:
std::size_t std::size_t
failed() const failed() const
{ {
return m_failed; return failed_;
} }
/** Register a successful test condition. */ /** Register a successful test condition. */
@@ -88,7 +88,7 @@ private:
void void
fail (std::string const& reason = "") fail (std::string const& reason = "")
{ {
++m_failed; ++failed_;
cont().emplace_back (false, reason); cont().emplace_back (false, reason);
} }
}; };
@@ -105,11 +105,11 @@ private:
} }
}; };
std::string m_name; std::string name_;
public: public:
explicit case_results (std::string const& name = "") explicit case_results (std::string const& name = "")
: m_name (name) : name_ (name)
{ {
} }
@@ -117,7 +117,7 @@ public:
std::string const& std::string const&
name() const name() const
{ {
return m_name; return name_;
} }
/** Memberspace for a container of test condition outcomes. */ /** Memberspace for a container of test condition outcomes. */
@@ -134,15 +134,13 @@ class suite_results
: public const_container <std::vector <case_results>> : public const_container <std::vector <case_results>>
{ {
private: private:
std::string m_name; std::string name_;
std::size_t m_total; std::size_t total_ = 0;
std::size_t m_failed; std::size_t failed_ = 0;
public: public:
explicit suite_results (std::string const& name = "") explicit suite_results (std::string const& name = "")
: m_name (name) : name_ (name)
, m_total (0)
, m_failed (0)
{ {
} }
@@ -150,21 +148,21 @@ public:
std::string const& std::string const&
name() const name() const
{ {
return m_name; return name_;
} }
/** Returns the total number of test conditions. */ /** Returns the total number of test conditions. */
std::size_t std::size_t
total() const total() const
{ {
return m_total; return total_;
} }
/** Returns the number of failures. */ /** Returns the number of failures. */
std::size_t std::size_t
failed() const failed() const
{ {
return m_failed; return failed_;
} }
/** Insert a set of testcase results. */ /** Insert a set of testcase results. */
@@ -173,16 +171,16 @@ public:
insert (case_results&& r) insert (case_results&& r)
{ {
cont().emplace_back (std::move (r)); cont().emplace_back (std::move (r));
m_total += r.tests.total(); total_ += r.tests.total();
m_failed += r.tests.failed(); failed_ += r.tests.failed();
} }
void void
insert (case_results const& r) insert (case_results const& r)
{ {
cont().push_back (r); cont().push_back (r);
m_total += r.tests.total(); total_ += r.tests.total();
m_failed += r.tests.failed(); failed_ += r.tests.failed();
} }
/** @} */ /** @} */
}; };
@@ -196,14 +194,14 @@ class results
{ {
private: private:
std::size_t m_cases; std::size_t m_cases;
std::size_t m_total; std::size_t total_;
std::size_t m_failed; std::size_t failed_;
public: public:
results() results()
: m_cases (0) : m_cases (0)
, m_total (0) , total_ (0)
, m_failed (0) , failed_ (0)
{ {
} }
@@ -218,14 +216,14 @@ public:
std::size_t std::size_t
total() const total() const
{ {
return m_total; return total_;
} }
/** Returns the number of failures. */ /** Returns the number of failures. */
std::size_t std::size_t
failed() const failed() const
{ {
return m_failed; return failed_;
} }
/** Insert a set of suite results. */ /** Insert a set of suite results. */
@@ -234,8 +232,8 @@ public:
insert (suite_results&& r) insert (suite_results&& r)
{ {
m_cases += r.size(); m_cases += r.size();
m_total += r.total(); total_ += r.total();
m_failed += r.failed(); failed_ += r.failed();
cont().emplace_back (std::move (r)); cont().emplace_back (std::move (r));
} }
@@ -243,8 +241,8 @@ public:
insert (suite_results const& r) insert (suite_results const& r)
{ {
m_cases += r.size(); m_cases += r.size();
m_total += r.total(); total_ += r.total();
m_failed += r.failed(); failed_ += r.failed();
cont().push_back (r); cont().push_back (r);
} }
/** @} */ /** @} */

View File

@@ -21,10 +21,9 @@
#define BEAST_UNIT_TEST_RUNNER_H_INCLUDED #define BEAST_UNIT_TEST_RUNNER_H_INCLUDED
#include <beast/unit_test/suite_info.h> #include <beast/unit_test/suite_info.h>
#include <beast/streams/abstract_ostream.h> #include <beast/streams/abstract_ostream.h>
#include <cassert> #include <cassert>
#include <mutex>
#include <string> #include <string>
namespace beast { namespace beast {
@@ -47,22 +46,22 @@ private:
stream_t() = delete; stream_t() = delete;
stream_t& operator= (stream_t const&) = delete; stream_t& operator= (stream_t const&) = delete;
stream_t (runner& owner) template <class = void>
: owner_ (owner) stream_t (runner& owner);
{
}
void void
write (string_type const& s) write (string_type const& s) override
{ {
owner_.log (s); owner_.log (s);
} }
}; };
stream_t stream_; stream_t stream_;
bool default_; std::string arg_;
bool failed_; bool default_ = false;
bool cond_; bool failed_ = false;
bool cond_ = false;
std::recursive_mutex mutex_;
public: public:
virtual ~runner() = default; virtual ~runner() = default;
@@ -72,6 +71,25 @@ public:
template <class = void> template <class = void>
runner(); runner();
/** Set the argument string.
The argument string is available to suites and
allows for customization of the test. Each suite
defines its own syntax for the argumnet string.
The same argument is passed to all suites.
*/
void
arg (std::string const& s)
{
arg_ = s;
}
/** Returns the argument string. */
std::string const&
arg() const
{
return arg_;
}
/** Run the specified suite. /** Run the specified suite.
@return `true` if any conditions failed. @return `true` if any conditions failed.
*/ */
@@ -201,12 +219,17 @@ private:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template <class>
runner::stream_t::stream_t (runner& owner)
: owner_ (owner)
{
}
//------------------------------------------------------------------------------
template <class> template <class>
runner::runner() runner::runner()
: stream_ (*this) : stream_ (*this)
, default_ (false)
, failed_ (false)
, cond_ (false)
{ {
} }
@@ -272,6 +295,7 @@ template <class>
void void
runner::testcase (std::string const& name) runner::testcase (std::string const& name)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex_);
// Name may not be empty // Name may not be empty
assert (default_ || ! name.empty()); assert (default_ || ! name.empty());
// Forgot to call pass or fail // Forgot to call pass or fail
@@ -287,6 +311,7 @@ template <class>
void void
runner::pass() runner::pass()
{ {
std::lock_guard<std::recursive_mutex> lock(mutex_);
if (default_) if (default_)
testcase (""); testcase ("");
on_pass(); on_pass();
@@ -297,6 +322,7 @@ template <class>
void void
runner::fail (std::string const& reason) runner::fail (std::string const& reason)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex_);
if (default_) if (default_)
testcase (""); testcase ("");
on_fail (reason); on_fail (reason);
@@ -308,6 +334,7 @@ template <class>
void void
runner::log (std::string const& s) runner::log (std::string const& s)
{ {
std::lock_guard<std::recursive_mutex> lock(mutex_);
if (default_) if (default_)
testcase (""); testcase ("");
on_log (s); on_log (s);

View File

@@ -166,6 +166,13 @@ public:
bool bool
expect (Condition shouldBeTrue, std::string const& reason = ""); expect (Condition shouldBeTrue, std::string const& reason = "");
/** Return the argument associated with the runner. */
std::string const&
arg() const
{
return runner_->arg();
}
// DEPRECATED // DEPRECATED
// @return `true` if the test condition indicates success (a false value) // @return `true` if the test condition indicates success (a false value)
template <class Condition> template <class Condition>
@@ -398,8 +405,8 @@ suite::fail (std::string const& reason)
// detail: // detail:
// This inserts the suite with the given manual flag // This inserts the suite with the given manual flag
#define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual) \ #define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual) \
static beast::unit_test::detail::global_suite_instance <Class##_test> \ static beast::unit_test::detail::insert_suite <Class##_test> \
Library ## Module ## Class ## _test_instance ( \ Library ## Module ## Class ## _test_instance ( \
#Class, #Module, #Library, manual); #Class, #Module, #Library, manual);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------