the Compartmented Robust Posix C++ Unit Test system

Changing expectations

A test can fail by trapping an assertion, by leaving the test function body in an unexpected way, or by leaving files in the working directory. Normally, the expected way to leave the test body is to return from it, or run off the end. A few test modifiers can change that, however. Consider this simple class:


     #include <map>
     #include <string>
     #include <cassert>
     
     class symtable
     {
     public:
       void add(const char *name, int val)
       {
         assert(name);
         table[name] = val;
       }
       int lookup(const char *name)
       {
         assert(name);
         return table.at(name);
       }
     private:
       std::map<std::string, int> table;
     };

Clearly the author of the class intended to differentiate between unusual situations (a name doesn't exist when looking it up,) and a programming error (a NULL pointer.) A test of the class should reflect that and provoke both situations. The exception can be tested using ASSERT_THROW(expr, exc_type, matcher?), but we can also use the EXPECT_EXCEPTION(type) modifier. The assert can likewise be tested with the EXPECT_SIGNAL_DEATH(signo, action?) modifier.


     #include "symtable.hpp"
     #include <crpcut.hpp>
     
     TEST(insert_and_lookup)
     {
       symtable s;
       s.add("one", 1);
       s.add("two", 2);
       ASSERT_EQ(s.lookup("one"), 1);
       ASSERT_EQ(s.lookup("two"), 2);
     }
     
     TEST(lookup_nonexisting, EXPECT_EXCEPTION(std::out_of_range))
     {
       symtable s;
       s.add("one", 1);
       s.lookup("two");
     }
     
     TEST(add_null, EXPECT_SIGNAL_DEATH(SIGABRT), NO_CORE_FILE)
     {
       symtable s;
       s.add(0, 1);
     }
     
     TEST(lookup_null, EXPECT_SIGNAL_DEATH(SIGABRT), NO_CORE_FILE)
     {
       symtable s;
       s.add("one", 1);
       s.lookup(0);
     }
     
     int main(int argc, char *argv[])
     {
       return crpcut::run(argc, argv);
     }

The NO_CORE_FILE modifier prevents a trapped assertion from dumping core. After all, we expect it to crash and the core dump would be unnecessary clutter.

Running this test program with the -v / --verbose command line flag, yields:


     PASSED!: insert_and_lookup
     ===============================================================================
     PASSED!: lookup_nonexisting
     ===============================================================================
     PASSED!: add_null
     stderr-------------------------------------------------------------------------
     symtable-test: samples/symtable.hpp:36: void symtable::add(const char*, int): Assertion `name' failed.
     
     ===============================================================================
     PASSED!: lookup_null
     stderr-------------------------------------------------------------------------
     symtable-test: samples/symtable.hpp:41: int symtable::lookup(const char*): Assertion `name' failed.
     
     ===============================================================================
     4 test cases selected
     
                    Sum   Critical   Non-critical
     PASSED   :       4          4              0

Of course, had either of the tests lookup_nonexisting, add_null or lookup_null returned, the test would've failed.