the Compartmented Robust Posix C++ Unit Test system

Chapter 10. Writing predicates

A predicate is anything that can be called as if it was a function, and that returns something that can be used as a bool. Examples of predicates are functions (of course,) pointers to functions, instances of classes that implement operator ()().

Predicates are used with ASSERT_PRED(pred, ...). Logically ASSERT_PRED(predicate, param1, param2, ...paramN) is identical with ASSERT_TRUE(predicate(param1, param2, ...paramN)). The difference lies in error reporting. ASSERT_PRED(pred, ...) will list the value of each of the parameters in a violation report, whereas ASSERT_PRED(pred, ...) will only include the result.

The below example uses both versions:


     
     #include <crpcut.hpp>
     
     bool has_substring_in(const std::string &needle, const std::string &haystack)
     {
       return haystack.find(needle) != std::string::npos;
     }
     
     TEST(using_assert_pred)
     {
       const char needle[]   = "pqr";
       const char haystack[] = "a mountain of hay";
       ASSERT_PRED(has_substring_in, needle, haystack);
     }
     
     TEST(using_assert_true)
     {
       const char needle[]   = "pqr";
       const char haystack[] = "a mountain of hay";
       ASSERT_TRUE(has_substring_in(needle, haystack));
     }
     
     int main(int argc, char *argv[])
     {
       return crpcut::run(argc, argv);
     }

The result shows the difference clearly


     FAILED: using_assert_pred
     phase="running"  --------------------------------------------------------------
     /home/bjorn/devel/crpcut/doc-src/samples/simple-pred.cpp:39
     ASSERT_PRED(has_substring_in, needle, haystack)
       param1 = pqr
       param2 = a mountain of hay
     
     -------------------------------------------------------------------------------
     ===============================================================================
     FAILED: using_assert_true
     phase="running"  --------------------------------------------------------------
     /home/bjorn/devel/crpcut/doc-src/samples/simple-pred.cpp:46
     ASSERT_TRUE(has_substring_in(needle, haystack))
       where has_substring_in(needle, haystack) = 0
     -------------------------------------------------------------------------------
     ===============================================================================
     Total 2 test cases selected
     UNTESTED : 0
     PASSED   : 0
     FAILED   : 2

If a predicate has an output-stream operator defined, it will be called when making violation reports.

Sometimes you may want the type of the predicate object to be determined by the types of one or several expressions. crpcut solves that with the function template crpcut::match<pred_type>(), together with the traits class template crpcut::match_traits. By default, the type returned from crpcut::match<pred_type>() is pred_type, and the object constructor is called with the parameters to crpcut::match<pred_type>(). However, you can override the traits class template crpcut::match_traits for your predicate class, to provide a better suitable type, for example a specialization on the parameters.

The above example now generalized, is unsurprisingly longer, and slightly different:


     
     #include <crpcut.hpp>
     
     class is_substring
     {
     public:
       template <typename T>
       class implementation
       {
       public:
         implementation(const T t) : needle(t) {}
         template <typename U>
         bool operator()(const U* haystack) const
         {
           return operator()(std::basic_string<U>(haystack));
         }
         template <typename U>
         bool operator()(const std::basic_string<U> &haystack) const
         {
           return haystack.find(needle) != std::basic_string<U>::npos;
         }
         friend std::ostream &operator<<(std::ostream &os, const implementation& i)
         {
           return os << "failed when searcing for " << i.needle;
         }
       private:
         T needle;
       };
     };
     
     
     namespace crpcut {
     
       template <typename T>
       struct match_traits<is_substring, const T*>
       {
         typedef is_substring::template implementation<const T*> type;
       };
       template <typename T>
       struct match_traits<is_substring, T*>
       {
         typedef is_substring::template implementation<const T*> type;
       };
     
       template <typename T>
       struct match_traits<is_substring, std::basic_string<T> >
       {
         typedef is_substring::template implementation<std::basic_string<T> > type;
       };
     }
     
     TEST(using_char_array)
     {
       char needle[]    = "steel";
       const char hay[] = "hay";
       char haystack[]  = "a mountain of hay";
       ASSERT_PRED(crpcut::match<is_substring>(hay), haystack);
       ASSERT_PRED(crpcut::match<is_substring>(needle), haystack);
     }
     
     TEST(using_const_char_ptr)
     {
       const char *needle   = "steel";
       const char *hay      = "hay";
       const char *haystack = "a mountain of hay";
       ASSERT_PRED(crpcut::match<is_substring>(hay), haystack);
       ASSERT_PRED(crpcut::match<is_substring>(needle), haystack);
     }
     
     TEST(using_string)
     {
       std::string needle("steel");
       const std::string hay("hay");
       std::string haystack("a mountain of hay");
       ASSERT_PRED(crpcut::match<is_substring>(hay), haystack);
       ASSERT_PRED(crpcut::match<is_substring>(needle), haystack);
     }
     
     int main(int argc, char *argv[])
     {
       return crpcut::run(argc, argv);
     }

The result from running the test program is:


     FAILED: using_char_array
     phase="running"  --------------------------------------------------------------
     /home/bjorn/devel/crpcut/doc-src/samples/template-pred.cpp:84
     ASSERT_PRED(crpcut::match<is_substring>(needle), haystack)
       param1 = a mountain of hay
     crpcut::match<is_substring>(needle) :
     failed when searcing for steel
     
     -------------------------------------------------------------------------------
     ===============================================================================
     FAILED: using_const_char_ptr
     phase="running"  --------------------------------------------------------------
     /home/bjorn/devel/crpcut/doc-src/samples/template-pred.cpp:93
     ASSERT_PRED(crpcut::match<is_substring>(needle), haystack)
       param1 = a mountain of hay
     crpcut::match<is_substring>(needle) :
     failed when searcing for steel
     
     -------------------------------------------------------------------------------
     ===============================================================================
     FAILED: using_string
     phase="running"  --------------------------------------------------------------
     /home/bjorn/devel/crpcut/doc-src/samples/template-pred.cpp:102
     ASSERT_PRED(crpcut::match<is_substring>(needle), haystack)
       param1 = a mountain of hay
     crpcut::match<is_substring>(needle) :
     failed when searcing for steel
     
     -------------------------------------------------------------------------------
     ===============================================================================
     Total 3 test cases selected
     UNTESTED : 0
     PASSED   : 0
     FAILED   : 3

What may be less obvious is that the above three tests instantiated is_substring::implementation<const char*> for the first two versions, and is_substring::implementation<std::string> for the last.