Utility library
#include "crow/unit-test.hpp"
namespace Crow::UnitTest;
Running the unit tests requires a user-created test wrapper in your project;
this is called unit-test.cpp
by default, but can be named anything. A shell
script (rs-update-tests
) is supplied that can be used to automatically
generate unit-test.cpp
(if you follow some test naming conventions), but
this is not necessary. Writing unit-test.cpp
by hand is simple, and an
example is supplied that explains what is needed.
The rs-update-tests
script searches your CMake file for any source files
named *-test.cpp
, then searches inside those files for functions with the
form void test_*()
. All of those functions are assumed to be required test
functions, and will be run in the order in which the test files appear in
your CMake file.
The other script, rs-bump-version
, finds the local CMakeLists.txt
(of the
project you run it from), increments the patch number in the version string,
and regenerates the local version.hpp
.
template <typename Range> std::string format_range(const Range& r);
template <typename Range> std::string format_map(const Range& r);
These format a range (such as a vector) as "[a,b,c,...]"
, or a range of
pairs (such as a map) as "{a:1,b:2,c:3,...}"
.
template <typename T> std::string type_name();
Returns a human readable type name, derived from std::type_info::name()
.
MSVC already supplies human readable names, so those are simply passed through
unchanged. On GCC, and GCC-like compilers such as Clang and Xcode, this will
attempt to demangle the name by calling abi::__cxa_demangle()
; if this fails
it will give up and return the original name.
template <typename T> class Counted {
Counted();
Counted(const T& t);
Counted(const Counted& c);
Counted(Counted&& c) noexcept;
~Counted() noexcept;
Counted& operator=(const Counted& c);
Counted& operator=(Counted&& c) noexcept;
Counted& operator=(const T& t);
T& operator*() noexcept;
const T& operator*() const noexcept;
T* operator->() noexcept;
const T* operator->() const noexcept;
const T& get() const noexcept;
void set(const T& t);
static int count() noexcept;
static void reset() noexcept;
};
bool operator==(const Counted<T>& a, const Counted<T>& b);
bool operator!=(const Counted<T>& a, const Counted<T>& b);
bool operator<(const Counted<T>& a, const Counted<T>& b);
bool operator>(const Counted<T>& a, const Counted<T>& b);
bool operator<=(const Counted<T>& a, const Counted<T>& b);
bool operator>=(const Counted<T>& a, const Counted<T>& b);
std::ostream& operator<<(std::ostream& out, const Counted<T>& c);
A type wrapper with population tracking. This is intended for testing code that allocates and deallocates objects(e.g. containers or smart pointers); the population count makes it easy to tell whether constructor and destructor counts are matched.
The *
and ->
operators, and the get()
function, return references to the
contained value; set()
changes the value. The count()
function indicates
how many Counted<T>
objects are currently in existence(this may be negative
if the destructor is called too many times and the process survives). The
reset()
function sets the counter back to zero (so testing can continue
even if some objects have been leaked by earlier tests).
The comparison and output operators simply call the corresponding operators on
T
.
void begin_tests(int argc, char** argv);
int end_tests();
Call these at the beginning and end of main(). Command line arguments passed
to begin_tests()
can be used to control which tests are run. The
end_tests()
function returns 0 on success, 1 if any tests failed; this value
should be returned from main()
.
The unit test program should look like this:
#include "crow/unit-test.hpp"
int main(int argc, char** argv) {
// Call begin_tests() at the start of your main() function. If any
// command line arguments are passed, these are interpreted as regular
// expressions, and only tests whose names match one of the expressions
// will be run.
Crow::UnitTest::begin_tests(argc, argv);
// Call individual test functions using the UNIT_TEST() macro. The
// argument is the name of the test function, minus the "test_" prefix.
UNIT_TEST(example) // runs test_example()
// Call end_tests() at the end of your main() function.
return Crow::UnitTest::end_tests();
}
The TRY(), TEST(),
and REQUIRE()
macros will always evaluate their
argument exactly once. All of the other macros may evaluate any of their
arguments any number of times (including zero).
#define UNIT_TEST(name)
Call this from main()
for each test. The argument is the name of the test
function, without the test_
prefix.
#define FAIL_TEST(message)
Explicitly fail a test. This is normally only used internally, but can be called explicitly if you choose.
#define TRY(expr)
Evaluate the expression, and fail if it throws an exception.
#define TEST(expr)
#define REQUIRE(expr)
Evaluate the expression, which is expected to return a bool
or something
explicitly convertible to bool
, and fail if the result is false or an
exception is thrown. REQUIRE()
differs from TEST()
in that it returns
immediately from the current test function on failure; this is intended for
situations where a test failure indicates that further testing would be
pointless or impossible.
#define TEST_EQUAL(lhs, rhs)
#define TEST_EQUAL_RANGES(lhs, rhs)
Evaluate the two expressions, and fail if they not equal, or if either of them throws an exception.
#define TEST_IN_RANGE(expr, min, max)
Evaluate the expressions, and fail if the result is outside the range(i.e. if
expr<min
or expr>max
).
#define TEST_NEAR(lhs, rhs, epsilon)
Evaluate the expressions, which are expected to return double
or something
implicitly convertible to double
, and fail if the two values are not within
epsilon
of each other.
#define TEST_MATCH(expr, pattern)
#define TEST_MATCH_ICASE(expr, pattern)
Evaluate the expression, which is expected to return a std::string
or
something implicitly convertible to std::string
, and fail if the expression
does not match the regex pattern, or throws an exception. The second version
does a case insensitive match.
#define TEST_THROW(expr, extype)
#define TEST_THROW_MESSAGE(expr, extype, message)
#define TEST_THROW_MATCH(expr, extype, pattern)
Evaluate the expression, and fail if it does not throw an exception, or throws one of the wrong type. The second and third versions also check that the error message matches a string (exactly) or a regular expression.
#define TEST_TYPE(lhs, rhs)
The two expressions are expected to be type expressions; fail if they are different types.