the Compartmented Robust Posix C++ Unit Test system | hosted by |
---|
A test isn't worth much if it isn't possible to assert the
correctness of the unit under test. Although you can use the
standard C/C++ assert()
macro, crpcut
offers a wealth of its own assert macros with several benefits.
Consider the C-style intrusive list class, written below:
#ifndef ilist_element_hpp #define ilist_element_hpp template <typename T> class ilist_element { public: ilist_element() : p_prev(static_cast<T*>(this)), p_next(static_cast<T*>(this)) { } ~ilist_element() { unlink(); } void unlink() { p_next->p_prev = p_prev; p_prev->p_prev = p_next; p_next = 0; p_prev = 0; } void insert_after(T& e) { e.p_next = p_next; e.p_prev = static_cast<T*>(this); p_next->p_prev = &e; p_next = &e; } void insert_before(T& e) { e.p_prev = p_prev; e.p_next = this; p_prev.p_next = &e; p_prev = &e; } T *next() { return p_next; } T *prev() { return p_prev; } bool is_empty() const { return p_next == p_prev && p_next == this; } private: T *p_prev; T *p_next; }; #endif // ilist_element_hpp
A few initial tests:
#include <crpcut.hpp> #include "ilist_element.hpp" struct elem : public ilist_element<elem> { elem(int i) : n(i) {} int n; }; TEST(create_and_destroy_empty) { ilist_element<elem> list; ASSERT_TRUE(list.is_empty()); } TEST(insert_and_traverse_one_element) { ilist_element<elem> list; elem obj(1); list.insert_after(obj); ASSERT_FALSE(list.is_empty()); ASSERT_TRUE(list.next()->n == 1); } TEST(several_elements) { ilist_element<elem> list; elem obj1(1); list.insert_after(obj1); elem *p2 = new elem(2); list.insert_after(*p2); elem obj3(3); list.insert_after(obj3); elem *i = list.next(); ASSERT_EQ(i->n, 3); ASSERT_EQ((i=i->next())->n, 2); ASSERT_EQ((i=i->next())->n, 1); ASSERT_EQ(i, &list); delete p2; ASSERT_EQ((i=i->prev())->n, 1); ASSERT_EQ((i=i->prev())->n, 3); ASSERT_EQ(i->prev(), &list); } TEST(unlink) { ilist_element<elem> list; elem obj1(1); list.insert_after(obj1); elem obj2(2); list.insert_after(obj2); elem obj3(3); list.insert_after(obj3); obj2.unlink(); elem *i = list.prev(); ASSERT_EQ(i->n, 1); i = i->prev(); ASSERT_EQ(i->n, 3); } int main(int argc, char *argv[]) { return crpcut::run(argc, argv); }
Expressions with side effects are OK in crpcut
ASSERT_*()
macros. They are carefully
designed to evaluate each parameter only once.
The result of running this test program is:
FAILED: several_elements phase="running" -------------------------------------------------------------- /home/bjorn/devel/crpcut/doc-src/samples/itest.cpp:66 ASSERT_EQ(i, &list) where i = 0x7fffd4181720 &list = 0x7fffd4181740 ------------------------------------------------------------------------------- =============================================================================== FAILED: unlink /tmp/crpcutWisH3j/unlink is not empty!! phase="running" -------------------------------------------------------------- Died with core dump ------------------------------------------------------------------------------- =============================================================================== Files remain under /tmp/crpcutWisH3j Total 4 test cases selected UNTESTED : 0 PASSED : 2 FAILED : 2
In several_elements an assertion fails. If the parameters to an assertion are output streamable, the values are printed in the report, otherwise crpcut displays a hex-dump of the objects memory. When an assertion fails, the test process is terminated. Is it the test or the list implementation that is wrong?
Another way to fail a test is to use the
FAIL
macro. It is
used like the INFO
macro described in “Test basics” on the
previous page, but instead
of just printing the message, the test is terminated as a
failure.
The second error, in unlink, is more interesting. The expected way to leave the test function body defaults to a return (or run off the end.) Leaving by exception or, as in this case, by crashing, is failing. Since each test runs in its own process and its own working directory, the crash is not fatal to the crpcut execution, and the information in the core dump can be used when debugging the code. Can you spot the bug?
The full list of ASSERT_*()
macros are:
ASSERT_*()
macros
ASSERT_EQ(a, b)
Assert equality between two expressions.
ASSERT_FALSE(expr)
Assert non-truth value of an expression.
ASSERT_GE(a, b)
Assert greater-than or equal relation between two expressions.
ASSERT_GT(a, b)
Assert greater-than relation between two expressions.
ASSERT_LE(a, b)
Assert less-than or equal relation between two expressions.
ASSERT_LT(a, b)
Assert less-than relation between two expressions.
ASSERT_NE(a, b)
Assert not-equal relation between two expressions.
ASSERT_NO_THROW(expr)
Assert that an expression/statement does not throw any exception.
ASSERT_PRED(pred, ...)
Assert that a predicate evaluates to true. A predicate is something that looks like a function and returns a boolean. For example:
ASSERT_PRED(std::is_sorted<std::vector<type>::iterator>, v.begin(), v.end());
ASSERT_THROW(expr, exc_type)
Assert that an expression/statement throws the correct exception.
ASSERT_TRUE(expr)
Assert truth value of an expression.