Refactor unit_test logging:

The log member is changed to derive from std::ostream. A new
class dstream is derived from std::ostream to support redirection
to the Visual Studio Output Window if a debugger is attached.

Obsolete classes abstract_ostream and its derived variants are
removed.
This commit is contained in:
Vinnie Falco
2016-05-10 07:09:57 -04:00
parent 908794bab2
commit e47811bef2
15 changed files with 316 additions and 844 deletions

View File

@@ -1,20 +0,0 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_ABSTRACT_OSTREAM_HPP
#define BEAST_UNIT_TEST_ABSTRACT_OSTREAM_HPP
#include <beast/unit_test/basic_abstract_ostream.hpp>
namespace beast {
/** An abstract ostream for `char`. */
using abstract_ostream = basic_abstract_ostream <char>;
} // beast
#endif

View File

@@ -1,85 +0,0 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_BASIC_ABSTRACT_OSTREAM_HPP
#define BEAST_UNIT_TEST_BASIC_ABSTRACT_OSTREAM_HPP
#include <beast/unit_test/basic_scoped_ostream.hpp>
#include <functional>
#include <memory>
#include <sstream>
namespace beast {
/** Abstraction for an output stream similar to std::basic_ostream. */
template <
class CharT,
class Traits = std::char_traits <CharT>
>
class basic_abstract_ostream
{
public:
using string_type = std::basic_string <CharT, Traits>;
using scoped_stream_type = basic_scoped_ostream <CharT, Traits>;
basic_abstract_ostream() = default;
virtual
~basic_abstract_ostream() = default;
basic_abstract_ostream (basic_abstract_ostream const&) = default;
basic_abstract_ostream& operator= (
basic_abstract_ostream const&) = default;
/** Returns `true` if the stream is active.
Inactive streams do not produce output.
*/
/** @{ */
virtual
bool
active() const
{
return true;
}
explicit
operator bool() const
{
return active();
}
/** @} */
/** Called to output each string. */
virtual
void
write (string_type const& s) = 0;
scoped_stream_type
operator<< (std::ostream& manip (std::ostream&))
{
return scoped_stream_type (manip,
[this](string_type const& s)
{
this->write (s);
});
}
template <class T>
scoped_stream_type
operator<< (T const& t)
{
return scoped_stream_type (t,
[this](string_type const& s)
{
this->write (s);
});
}
};
} // beast
#endif

View File

@@ -1,136 +0,0 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_BASIC_SCOPED_OSTREAM_HPP
#define BEAST_UNIT_TEST_BASIC_SCOPED_OSTREAM_HPP
#include <functional>
#include <memory>
#include <sstream>
// gcc libstd++ doesn't have move constructors for basic_ostringstream
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316
//
#ifndef BEAST_NO_STDLIB_STREAM_MOVE
# ifdef __GLIBCXX__
# define BEAST_NO_STDLIB_STREAM_MOVE 1
# else
# define BEAST_NO_STDLIB_STREAM_MOVE 0
# endif
#endif
namespace beast {
template <
class CharT,
class Traits
>
class basic_abstract_ostream;
/** Scoped output stream that forwards to a functor upon destruction. */
template <
class CharT,
class Traits = std::char_traits <CharT>,
class Allocator = std::allocator <CharT>
>
class basic_scoped_ostream
{
private:
using handler_t = std::function <void (
std::basic_string <CharT, Traits, Allocator> const&)>;
using stream_type = std::basic_ostringstream <
CharT, Traits, Allocator>;
handler_t m_handler;
#if BEAST_NO_STDLIB_STREAM_MOVE
std::unique_ptr <stream_type> m_ss;
stream_type& stream()
{
return *m_ss;
}
#else
stream_type m_ss;
stream_type& stream()
{
return m_ss;
}
#endif
public:
using string_type = std::basic_string <CharT, Traits>;
// Disallow copy since that would duplicate the output
basic_scoped_ostream (basic_scoped_ostream const&) = delete;
basic_scoped_ostream& operator= (basic_scoped_ostream const) = delete;
template <class Handler>
explicit basic_scoped_ostream (Handler&& handler)
: m_handler (std::forward <Handler> (handler))
#if BEAST_NO_STDLIB_STREAM_MOVE
, m_ss (new stream_type())
#endif
{
}
template <class T, class Handler>
basic_scoped_ostream (T const& t, Handler&& handler)
: m_handler (std::forward <Handler> (handler))
#if BEAST_NO_STDLIB_STREAM_MOVE
, m_ss (new stream_type())
#endif
{
stream() << t;
}
basic_scoped_ostream (basic_abstract_ostream <
CharT, Traits>& ostream)
: m_handler (
[&](string_type const& s)
{
ostream.write (s);
})
{
}
basic_scoped_ostream (basic_scoped_ostream&& other)
: m_handler (std::move (other.m_handler))
, m_ss (std::move (other.m_ss))
{
}
~basic_scoped_ostream()
{
auto const& s (stream().str());
if (! s.empty())
m_handler (s);
}
basic_scoped_ostream&
operator<< (std::ostream& manip (std::ostream&))
{
stream() << manip;
return *this;
}
template <class T>
basic_scoped_ostream&
operator<< (T const& t)
{
stream() << t;
return *this;
}
};
} // beast
#endif

View File

@@ -1,60 +0,0 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_BASIC_STD_OSTREAM_HPP
#define BEAST_UNIT_TEST_BASIC_STD_OSTREAM_HPP
#include <beast/unit_test/basic_abstract_ostream.hpp>
#include <ostream>
namespace beast {
/** Wraps an existing std::basic_ostream as an abstract_ostream. */
template <
class CharT,
class Traits = std::char_traits <CharT>
>
class basic_std_ostream
: public basic_abstract_ostream <CharT, Traits>
{
private:
using typename basic_abstract_ostream <CharT, Traits>::string_type;
std::reference_wrapper <std::ostream> m_stream;
public:
explicit basic_std_ostream (
std::basic_ostream <CharT, Traits>& stream)
: m_stream (stream)
{
}
void
write (string_type const& s) override
{
m_stream.get() << s << std::endl;
}
};
using std_ostream = basic_std_ostream <char>;
//------------------------------------------------------------------------------
/** Returns a basic_std_ostream using template argument deduction. */
template <
class CharT,
class Traits = std::char_traits <CharT>
>
basic_std_ostream <CharT, Traits>
make_std_ostream (std::basic_ostream <CharT, Traits>& stream)
{
return basic_std_ostream <CharT, Traits> (stream);
}
} // beast
#endif

View File

@@ -1,78 +0,0 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_DEBUG_OSTREAM_HPP
#define BEAST_UNIT_TEST_DEBUG_OSTREAM_HPP
#include <beast/unit_test/abstract_ostream.hpp>
#include <iostream>
#ifdef _MSC_VER
# ifndef WIN32_LEAN_AND_MEAN // VC_EXTRALEAN
# define WIN32_LEAN_AND_MEAN
#include <windows.h>
# undef WIN32_LEAN_AND_MEAN
# else
#include <windows.h>
# endif
# ifdef min
# undef min
# endif
# ifdef max
# undef max
# endif
#endif
namespace beast {
#ifdef _MSC_VER
/** A basic_abstract_ostream that redirects output to an attached debugger. */
class debug_ostream
: public abstract_ostream
{
private:
bool m_debugger;
public:
debug_ostream()
: m_debugger (IsDebuggerPresent() != FALSE)
{
// Note that the check for an attached debugger is made only
// during construction time, for efficiency. A stream created before
// the debugger is attached will not have output redirected.
}
void
write (string_type const& s) override
{
if (m_debugger)
{
OutputDebugStringA ((s + "\n").c_str());
return;
}
std::cout << s << std::endl;
}
};
#else
class debug_ostream
: public abstract_ostream
{
public:
void
write (string_type const& s) override
{
std::cout << s << std::endl;
}
};
#endif
} // beast
#endif

View File

@@ -73,8 +73,8 @@ public:
int
sync() override
{
write(str().c_str());
str("");
write(this->str().c_str());
this->str("");
return 0;
}
};
@@ -107,8 +107,8 @@ public:
int
sync() override
{
write(str().c_str());
str("");
write(this->str().c_str());
this->str("");
return 0;
}
};
@@ -130,7 +130,7 @@ class basic_dstream
{
public:
basic_dstream()
: std::basic_ostream<CharT, Traits>(&member)
: std::basic_ostream<CharT, Traits>(&this->member)
{
}
};

View File

@@ -15,7 +15,8 @@ namespace unit_test {
namespace detail {
template <class = void>
/// Holds test suites registered during static initialization.
inline
suite_list&
global_suites()
{
@@ -26,23 +27,17 @@ global_suites()
template<class Suite>
struct insert_suite
{
template <class = void>
insert_suite(char const* name, char const* module,
char const* library, bool manual);
};
template <class Suite>
template <class>
insert_suite<Suite>::insert_suite (char const* name,
char const* module, char const* library, bool manual)
char const* library, bool manual)
{
global_suites().insert<Suite>(
name, module, library, manual);
}
};
} // detail
/** Holds suites registered during static initialization. */
/// Holds test suites registered during static initialization.
inline
suite_list const&
global_suites()

View File

@@ -6,12 +6,13 @@
//
#include <beast/unit_test/amount.hpp>
#include <beast/unit_test/dstream.hpp>
#include <beast/unit_test/global_suites.hpp>
#include <beast/unit_test/match.hpp>
#include <beast/unit_test/reporter.hpp>
#include <beast/unit_test/suite.hpp>
#include <beast/unit_test/debug_ostream.hpp>
#include <boost/program_options.hpp>
#include <cstdlib>
#include <iostream>
#include <vector>
@@ -25,11 +26,10 @@
# endif
#endif
#include <cstdlib>
namespace beast {
namespace unit_test {
static
std::string
prefix(suite_info const& s)
{
@@ -38,32 +38,34 @@ prefix(suite_info const& s)
return " ";
}
template<class Log>
static
void
print(Log& log, suite_list const& c)
print(std::ostream& os, suite_list const& c)
{
std::size_t manual = 0;
for(auto const& s : c)
{
log <<
prefix (s) <<
s.full_name();
os << prefix (s) << s.full_name() << '\n';
if(s.manual())
++manual;
}
log <<
os <<
amount(c.size(), "suite") << " total, " <<
amount(manual, "manual suite")
amount(manual, "manual suite") <<
'\n'
;
}
template<class Log>
// Print the list of suites
// Used with the --print command line option
static
void
print(Log& log)
print(std::ostream& os)
{
log << "------------------------------------------";
print(log, global_suites());
log << "------------------------------------------";
os << "------------------------------------------\n";
print(os, global_suites());
os << "------------------------------------------" <<
std::endl;
}
} // unit_test
@@ -97,11 +99,11 @@ int main(int ac, char const* av[])
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
beast::debug_ostream log;
dstream log;
if(vm.count("help"))
{
log << desc;
log << desc << std::endl;
}
else if(vm.count("print"))
{

View File

@@ -1,67 +0,0 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_UNIT_TEST_PRINT_H_INCLUDED
#define BEAST_UNIT_TEST_PRINT_H_INCLUDED
#include <beast/unit_test/amount.hpp>
#include <beast/unit_test/results.hpp>
#include <beast/unit_test/abstract_ostream.hpp>
#include <beast/unit_test/basic_std_ostream.hpp>
#include <iostream>
#include <string>
namespace beast {
namespace unit_test {
/** Write test results to the specified output stream. */
/** @{ */
template <class = void>
void
print (results const& r, abstract_ostream& stream)
{
for (auto const& s : r)
{
for (auto const& c : s)
{
stream <<
s.name() <<
(c.name().empty() ? "" : ("." + c.name()));
std::size_t i (1);
for (auto const& t : c.tests)
{
if (! t.pass)
stream <<
"#" << i <<
" failed: " << t.reason;
++i;
}
}
}
stream <<
amount (r.size(), "suite") << ", " <<
amount (r.cases(), "case") << ", " <<
amount (r.total(), "test") << " total, " <<
amount (r.failed(), "failure")
;
}
template <class = void>
void
print (results const& r, std::ostream& stream = std::cout)
{
auto s (make_std_ostream (stream));
print (r, s);
}
} // unit_test
} // beast
#endif

View File

@@ -10,8 +10,6 @@
#include <beast/unit_test/amount.hpp>
#include <beast/unit_test/recorder.hpp>
#include <beast/unit_test/abstract_ostream.hpp>
#include <beast/unit_test/basic_std_ostream.hpp>
#include <boost/optional.hpp>
#include <algorithm>
#include <chrono>
@@ -42,7 +40,11 @@ private:
std::size_t total = 0;
std::size_t failed = 0;
case_results (std::string const& name_ = "");
explicit
case_results(std::string name_ = "")
: name(std::move(name_))
{
}
};
struct suite_results
@@ -51,11 +53,13 @@ private:
std::size_t cases = 0;
std::size_t total = 0;
std::size_t failed = 0;
typename clock_type::time_point start =
clock_type::now();
typename clock_type::time_point start = clock_type::now();
explicit
suite_results (std::string const& name_ = "");
suite_results(std::string const& name_ = "")
: name(std::move(name_))
{
}
void
add(case_results const& r);
@@ -76,15 +80,13 @@ private:
std::size_t total = 0;
std::size_t failed = 0;
std::vector<run_time> top;
typename clock_type::time_point start =
clock_type::now();
typename clock_type::time_point start = clock_type::now();
void
add (suite_results const& r);
};
boost::optional <std_ostream> std_ostream_;
std::reference_wrapper <beast::abstract_ostream> stream_;
std::ostream& os_;
results results_;
suite_results suite_results_;
case_results case_results_;
@@ -96,10 +98,7 @@ public:
~reporter();
explicit
reporter (std::ostream& stream = std::cout);
explicit
reporter (beast::abstract_ostream& stream);
reporter(std::ostream& os = std::cout);
private:
static
@@ -137,23 +136,10 @@ private:
//------------------------------------------------------------------------------
template <class _>
reporter<_>::case_results::case_results (
std::string const& name_)
: name (name_)
{
}
template <class _>
reporter<_>::suite_results::suite_results (
std::string const& name_)
: name (name_)
{
}
template<class _>
void
reporter<_>::suite_results::add (case_results const& r)
reporter<_>::
suite_results::add(case_results const& r)
{
++cases;
total += r.total;
@@ -162,17 +148,15 @@ reporter<_>::suite_results::add (case_results const& r)
template<class _>
void
reporter<_>::results::add (
suite_results const& r)
reporter<_>::
results::add(suite_results const& r)
{
++suites;
total += r.total;
cases += r.cases;
failed += r.failed;
auto const elapsed =
clock_type::now() - r.start;
if (elapsed >= std::chrono::seconds(1))
auto const elapsed = clock_type::now() - r.start;
if (elapsed >= std::chrono::seconds{1})
{
auto const iter = std::lower_bound(top.begin(),
top.end(), elapsed,
@@ -197,10 +181,9 @@ reporter<_>::results::add (
//------------------------------------------------------------------------------
template<class _>
reporter<_>::reporter (
std::ostream& stream)
: std_ostream_ (std::ref (stream))
, stream_ (*std_ostream_)
reporter<_>::
reporter(std::ostream& os)
: os_(os)
{
}
@@ -209,37 +192,28 @@ reporter<_>::~reporter()
{
if(results_.top.size() > 0)
{
stream_.get() << "Longest suite times:";
os_ << "Longest suite times:\n";
for(auto const& i : results_.top)
stream_.get() << std::setw(8) <<
fmtdur(i.second) << " " << i.first;
os_ << std::setw(8) <<
fmtdur(i.second) << " " << i.first << '\n';
}
auto const elapsed =
clock_type::now() - results_.start;
stream_.get() <<
auto const elapsed = clock_type::now() - results_.start;
os_ <<
fmtdur(elapsed) << ", " <<
amount (results_.suites, "suite") << ", " <<
amount (results_.cases, "case") << ", " <<
amount (results_.total, "test") << " total, " <<
amount (results_.failed, "failure");
}
template <class _>
reporter<_>::reporter (
abstract_ostream& stream)
: stream_ (stream)
{
amount{results_.suites, "suite"} << ", " <<
amount{results_.cases, "case"} << ", " <<
amount{results_.total, "test"} << " total, " <<
amount{results_.failed, "failure"} <<
std::endl;
}
template<class _>
std::string
reporter<_>::fmtdur (
typename clock_type::duration const& d)
reporter<_>::fmtdur(typename clock_type::duration const& d)
{
using namespace std::chrono;
auto const ms =
duration_cast<milliseconds>(d);
if (ms < seconds(1))
auto const ms = duration_cast<milliseconds>(d);
if (ms < seconds{1})
return std::to_string(ms.count()) + "ms";
std::stringstream ss;
ss << std::fixed << std::setprecision(1) <<
@@ -249,10 +223,10 @@ reporter<_>::fmtdur (
template<class _>
void
reporter<_>::on_suite_begin (
suite_info const& info)
reporter<_>::
on_suite_begin(suite_info const& info)
{
suite_results_ = suite_results (info.full_name());
suite_results_ = suite_results{info.full_name()};
}
template<class _>
@@ -264,50 +238,50 @@ reporter<_>::on_suite_end()
template<class _>
void
reporter<_>::on_case_begin (
std::string const& name)
reporter<_>::
on_case_begin(std::string const& name)
{
case_results_ = case_results (name);
stream_.get() <<
os_ <<
suite_results_.name <<
(case_results_.name.empty() ?
"" : (" " + case_results_.name));
"" : (" " + case_results_.name)) << std::endl;
}
template<class _>
void
reporter<_>::on_case_end()
reporter<_>::
on_case_end()
{
suite_results_.add(case_results_);
}
template<class _>
void
reporter<_>::on_pass()
reporter<_>::
on_pass()
{
++case_results_.total;
}
template<class _>
void
reporter<_>::on_fail (
std::string const& reason)
reporter<_>::
on_fail(std::string const& reason)
{
++case_results_.failed;
++case_results_.total;
stream_.get() <<
"#" << case_results_.total <<
" failed" <<
(reason.empty() ? "" : ": ") << reason;
os_ <<
"#" << case_results_.total << " failed" <<
(reason.empty() ? "" : ": ") << reason << std::endl;
}
template<class _>
void
reporter<_>::on_log (
std::string const& s)
reporter<_>::
on_log(std::string const& s)
{
stream_.get() << s;
os_ << s;
}
} // detail

View File

@@ -9,9 +9,9 @@
#define BEAST_UNIT_TEST_RUNNER_H_INCLUDED
#include <beast/unit_test/suite_info.hpp>
#include <beast/unit_test/abstract_ostream.hpp>
#include <cassert>
#include <mutex>
#include <ostream>
#include <string>
namespace beast {
@@ -23,28 +23,6 @@ namespace unit_test {
*/
class runner
{
private:
// Reroutes log output to the runner
class stream_t : public abstract_ostream
{
private:
runner& owner_;
public:
stream_t() = delete;
stream_t& operator= (stream_t const&) = delete;
template <class = void>
stream_t (runner& owner);
void
write (string_type const& s) override
{
owner_.log (s);
}
};
stream_t stream_;
std::string arg_;
bool default_ = false;
bool failed_ = false;
@@ -52,14 +30,13 @@ private:
std::recursive_mutex mutex_;
public:
runner() = default;
virtual ~runner() = default;
runner (runner const&) = default;
runner& operator= (runner const&) = default;
template <class = void>
runner();
runner(runner const&) = delete;
runner& operator=(runner const&) = delete;
/** 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.
@@ -124,54 +101,54 @@ public:
bool
run_each_if (SequenceContainer const& c, Pred pred = Pred{});
private:
protected:
//
// Overrides
//
/** Called when a new suite starts. */
/// Called when a new suite starts.
virtual
void
on_suite_begin(suite_info const&)
{
}
/** Called when a suite ends. */
/// Called when a suite ends.
virtual
void
on_suite_end()
{
}
/** Called when a new case starts. */
/// Called when a new case starts.
virtual
void
on_case_begin(std::string const&)
{
}
/** Called when a new case ends. */
/// Called when a new case ends.
virtual
void
on_case_end()
{
}
/** Called for each passing condition. */
/// Called for each passing condition.
virtual
void
on_pass()
{
}
/** Called for each failing condition. */
/// Called for each failing condition.
virtual
void
on_fail(std::string const&)
{
}
/** Called when a test logs output. */
/// Called when a test logs output.
virtual
void
on_log(std::string const&)
@@ -181,12 +158,6 @@ private:
private:
friend class suite;
abstract_ostream&
stream()
{
return stream_;
}
// Start a new testcase.
template <class = void>
void
@@ -207,20 +178,6 @@ private:
//------------------------------------------------------------------------------
template <class>
runner::stream_t::stream_t (runner& owner)
: owner_ (owner)
{
}
//------------------------------------------------------------------------------
template <class>
runner::runner()
: stream_ (*this)
{
}
template <class>
bool
runner::run (suite_info const& s)

View File

@@ -9,15 +9,23 @@
#define BEAST_UNIT_TEST_SUITE_HPP
#include <beast/unit_test/runner.hpp>
#include <string>
#include <ostream>
#include <sstream>
#include <string>
namespace beast {
namespace unit_test {
class thread;
enum abort_t
{
no_abort_on_fail,
abort_on_fail
};
/** A testsuite class.
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,
derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a
@@ -25,13 +33,6 @@ class thread;
*/
class suite
{
public:
enum abort_t
{
no_abort_on_fail,
abort_on_fail
};
private:
bool abort_ = false;
bool aborted_ = false;
@@ -44,95 +45,100 @@ private:
char const*
what() const noexcept override
{
return "suite aborted";
return "test suite aborted";
}
};
public:
// Memberspace
class log_t
template<class CharT, class Traits, class Allocator>
class log_buf
: public std::basic_stringbuf<CharT, Traits, Allocator>
{
private:
friend class suite;
suite* suite_ = nullptr;
suite& suite_;
public:
log_t () = default;
explicit
log_buf(suite& self)
: suite_(self)
{
}
template <class T>
abstract_ostream::scoped_stream_type
operator<< (T const& t);
~log_buf()
{
sync();
}
/** Returns the raw stream used for output. */
abstract_ostream&
stream();
int
sync() override
{
auto const& s = this->str();
if(s.size() > 0)
suite_.runner_->on_log(s);
this->str("");
return 0;
}
};
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
>
class log_os : public std::basic_ostream<CharT, Traits>
{
log_buf<CharT, Traits, Allocator> buf_;
public:
explicit
log_os(suite& self)
: std::basic_ostream<CharT, Traits>(&buf_)
, buf_(self)
{
}
};
private:
class scoped_testcase;
// Memberspace
class testcase_t
{
private:
friend class suite;
suite* suite_ = nullptr;
suite& suite_;
std::stringstream ss_;
public:
testcase_t() = default;
explicit
testcase_t(suite& self)
: suite_(self)
{
}
/** Open a new testcase.
A testcase is a series of evaluated test conditions. A test suite
may have multiple test cases. A test is associated with the last
opened testcase. When the test first runs, a default unnamed
case is opened. Tests with only one case may omit the call
to testcase.
@param abort If `true`, the suite will be stopped on first failure.
A testcase is a series of evaluated test conditions. A test
suite may have multiple test cases. A test is associated with
the last opened testcase. When the test first runs, a default
unnamed case is opened. Tests with only one case may omit the
call to testcase.
@param abort Determines if suite continues running after a failure.
*/
void
operator()(std::string const& name,
abort_t abort = no_abort_on_fail);
/** Stream style composition of testcase names. */
/** @{ */
scoped_testcase
operator()(abort_t abort);
template <class T>
scoped_testcase
operator<<(T const& t);
/** @} */
};
public:
/** Type for scoped stream logging.
To use this type, declare a local variable of the type
on the stack in derived class member function and construct
it from log.stream();
/** Logging output stream.
@code
scoped_stream ss (log.stream();
ss << "Hello" << std::endl;
ss << "world" << std::endl;
@endcode
Streams constructed in this fashion will not have the line
ending automatically appended.
Thread safety:
The scoped_stream may only be used by one thread.
Multiline output sent to the stream will be atomically
written to the underlying abstract_Ostream
Text sent to the log output stream will be forwarded to
the output stream associated with the runner.
*/
using scoped_stream = abstract_ostream::scoped_stream_type;
/** Memberspace for logging. */
log_t log;
log_os<char> log;
/** Memberspace for declaring test cases. */
testcase_t testcase;
@@ -145,6 +151,12 @@ public:
return *p_this_suite();
}
suite()
: log(*this)
, testcase(*this)
{
}
/** Invokes the test using the specified runner.
Data members are set up here instead of the constructor as a
convenience to writing the derived class to avoid repetition of
@@ -272,83 +284,50 @@ private:
//------------------------------------------------------------------------------
template <class T>
inline
abstract_ostream::scoped_stream_type
suite::log_t::operator<< (T const& t)
{
return suite_->runner_->stream() << t;
}
/** Returns the raw stream used for output. */
inline
abstract_ostream&
suite::log_t::stream()
{
return suite_->runner_->stream();
}
//------------------------------------------------------------------------------
// Helper for streaming testcase names
class suite::scoped_testcase
{
private:
suite* suite_;
std::stringstream* ss_;
suite& suite_;
std::stringstream& ss_;
public:
~scoped_testcase();
scoped_testcase& operator=(scoped_testcase const&) = delete;
scoped_testcase (suite* s, std::stringstream* ss);
~scoped_testcase()
{
auto const& name = ss_.str();
if(! name.empty())
suite_.runner_->testcase (name);
}
scoped_testcase(suite& self, std::stringstream& ss)
: suite_(self)
, ss_(ss)
{
ss_.clear();
ss_.str({});
}
template<class T>
scoped_testcase (suite* s, std::stringstream* ss, T const& t);
scoped_testcase& operator= (scoped_testcase const&) = delete;
scoped_testcase(suite& self,
std::stringstream& ss, T const& t)
: suite_(self)
, ss_(ss)
{
ss_.clear();
ss_.str({});
ss_ << t;
}
template<class T>
scoped_testcase&
operator<< (T const& t);
};
inline
suite::scoped_testcase::~scoped_testcase()
operator<<(T const& t)
{
auto const& name (ss_->str());
if (! name.empty())
suite_->runner_->testcase (name);
}
inline
suite::scoped_testcase::scoped_testcase (suite* s, std::stringstream* ss)
: suite_ (s)
, ss_ (ss)
{
ss_->clear();
ss_->str({});
}
template <class T>
inline
suite::scoped_testcase::scoped_testcase (suite* s, std::stringstream* ss, T const& t)
: suite_ (s)
, ss_ (ss)
{
ss_->clear();
ss_->str({});
*ss_ << t;
}
template <class T>
inline
suite::scoped_testcase&
suite::scoped_testcase::operator<< (T const& t)
{
*ss_ << t;
ss_ << t;
return *this;
}
};
//------------------------------------------------------------------------------
@@ -357,16 +336,16 @@ void
suite::testcase_t::operator() (std::string const& name,
abort_t abort)
{
suite_->abort_ = abort == abort_on_fail;
suite_->runner_->testcase (name);
suite_.abort_ = abort == abort_on_fail;
suite_.runner_->testcase (name);
}
inline
suite::scoped_testcase
suite::testcase_t::operator() (abort_t abort)
{
suite_->abort_ = abort == abort_on_fail;
return { suite_, &ss_ };
suite_.abort_ = abort == abort_on_fail;
return { suite_, ss_ };
}
template<class T>
@@ -374,7 +353,7 @@ inline
suite::scoped_testcase
suite::testcase_t::operator<< (T const& t)
{
return { suite_, &ss_, t };
return { suite_, ss_, t };
}
//------------------------------------------------------------------------------
@@ -511,8 +490,6 @@ void
suite::run (runner& r)
{
runner_ = &r;
log.suite_ = this;
testcase.suite_ = this;
try
{

View File

@@ -8,6 +8,7 @@
#ifndef BEAST_UNIT_TEST_SUITE_INFO_HPP
#define BEAST_UNIT_TEST_SUITE_INFO_HPP
#include <cstring>
#include <functional>
#include <string>
#include <utility>
@@ -20,19 +21,28 @@ class runner;
/** Associates a unit test type with metadata. */
class suite_info
{
private:
using run_type = std::function<void(runner&)>;
std::string name_;
std::string module_;
std::string library_;
bool m_manual;
run_type m_run;
bool manual_;
run_type run_;
public:
template <class = void>
suite_info (std::string const& name, std::string const& module,
std::string const& library, bool manual, run_type run);
suite_info(
std::string name,
std::string module,
std::string library,
bool manual,
run_type run)
: name_(std::move(name))
, module_(std::move(module))
, library_(std::move(library))
, manual_(manual)
, run_(std::move(run))
{
}
std::string const&
name() const
@@ -52,61 +62,58 @@ public:
return library_;
}
/** Returns `true` if this suite only runs manually. */
/// Returns `true` if this suite only runs manually.
bool
manual() const
{
return m_manual;
return manual_;
}
/** Return the canonical suite name as a string. */
template <class = void>
/// Return the canonical suite name as a string.
std::string
full_name() const;
full_name() const
{
return library_ + "." + module_ + "." + name_;
}
/** Run a new instance of the associated test suite. */
/// Run a new instance of the associated test suite.
void
run(runner& r) const
{
m_run (r);
run_ (r);
}
friend
bool
operator<(suite_info const& lhs, suite_info const& rhs)
{
return
std::tie(lhs.library_, lhs.module_, lhs.name_) <
std::tie(rhs.library_, rhs.module_, rhs.name_);
}
};
//------------------------------------------------------------------------------
template <class>
suite_info::suite_info (std::string const& name, std::string const& module,
std::string const& library, bool manual, run_type run)
: name_ (name)
, module_ (module)
, library_ (library)
, m_manual (manual)
, m_run (std::move (run))
{
}
template <class>
std::string
suite_info::full_name() const
{
return library_ + "." + module_ + "." + name_;
}
inline
bool
operator< (suite_info const& lhs, suite_info const& rhs)
{
return lhs.full_name() < rhs.full_name();
}
/** Convenience for producing suite_info for a given test type. */
/// Convenience for producing suite_info for a given test type.
template<class Suite>
suite_info
make_suite_info (std::string const& name, std::string const& module,
std::string const& library, bool manual)
make_suite_info(
std::string name,
std::string module,
std::string library,
bool manual)
{
return suite_info(name, module, library, manual,
[](runner& r) { return Suite{}(r); });
return suite_info(
std::move(name),
std::move(module),
std::move(library),
manual,
[](runner& r)
{
Suite{}(r);
}
);
}
} // unit_test

View File

@@ -18,7 +18,7 @@
namespace beast {
namespace unit_test {
/** A container of test suites. */
/// A container of test suites.
class suite_list
: public detail::const_container <std::set <suite_info>>
{
@@ -30,11 +30,15 @@ private:
public:
/** Insert a suite into the set.
The suite must not already exist.
*/
template <class Suite>
void
insert (char const* name, char const* module, char const* library,
insert(
char const* name,
char const* module,
char const* library,
bool manual);
};
@@ -42,7 +46,10 @@ public:
template <class Suite>
void
suite_list::insert (char const* name, char const* module, char const* library,
suite_list::insert(
char const* name,
char const* module,
char const* library,
bool manual)
{
#ifndef NDEBUG
@@ -59,9 +66,8 @@ suite_list::insert (char const* name, char const* module, char const* library,
assert (result.second); // Duplicate type
}
#endif
cont().emplace (std::move (make_suite_info <Suite> (
name, module, library, manual)));
cont().emplace(make_suite_info<Suite>(
name, module, library, manual));
}
} // unit_test

View File

@@ -85,7 +85,7 @@ public:
{
using namespace std::chrono;
using clock_type = std::chrono::high_resolution_clock;
log << name;
log << name << std::endl;
for(std::size_t trial = 1; trial <= repeat; ++trial)
{
auto const t0 = clock_type::now();
@@ -93,7 +93,7 @@ public:
auto const elapsed = clock_type::now() - t0;
log <<
"Trial " << trial << ": " <<
duration_cast<milliseconds>(elapsed).count() << " ms";
duration_cast<milliseconds>(elapsed).count() << " ms" << std::endl;
}
}
@@ -109,10 +109,10 @@ public:
static std::size_t constexpr Repeat = 50;
log << "sizeof(request parser) == " <<
sizeof(basic_parser_v1<true, null_parser<true>>);
sizeof(basic_parser_v1<true, null_parser<true>>) << '\n';
log << "sizeof(response parser) == " <<
sizeof(basic_parser_v1<false, null_parser<true>>);
sizeof(basic_parser_v1<false, null_parser<true>>)<< '\n';
testcase << "Parser speed test, " <<
((Repeat * size_ + 512) / 1024) << "KB in " <<