Home | History | Annotate | Download | only in support
      1 #ifndef TEST_SUPPORT_VERBOSE_ASSERT
      2 #define TEST_SUPPORT_VERBOSE_ASSERT
      3 
      4 #include <iostream>
      5 #include <cstdio>
      6 #include <sstream>
      7 #include <string>
      8 #include "test_macros.h"
      9 
     10 namespace verbose_assert {
     11 
     12 typedef std::basic_ostream<char>&(EndLType)(std::basic_ostream<char>&);
     13 
     14 template <class Stream, class Tp,
     15     class = decltype(std::declval<Stream&>() << std::declval<Tp const&>())>
     16 std::true_type IsStreamableImp(int);
     17 template <class Stream, class Tp> std::false_type IsStreamableImp(long);
     18 
     19 template <class Stream, class Tp>
     20 struct IsStreamable : decltype(IsStreamableImp<Stream, Tp>(0)) {};
     21 
     22 template <class Tp, int ST = (IsStreamable<decltype(std::cerr), Tp>::value ? 1
     23         : (IsStreamable<decltype(std::wcerr), Tp>::value ? 2 : -1))>
     24 struct SelectStream {
     25   static_assert(ST == -1, "specialization required for ST != -1");
     26   static void Print(Tp const&) { std::clog << "Value Not Streamable!\n"; }
     27 };
     28 
     29 template <class Tp>
     30 struct SelectStream<Tp, 1> {
     31   static void Print(Tp const& val) { std::cerr << val; }
     32 };
     33 
     34 template <class Tp>
     35 struct SelectStream<Tp, 2> {
     36   static void Print(Tp const& val) { std::wcerr << val; }
     37 };
     38 
     39 struct AssertData {
     40   AssertData(const char* xcheck, const char* xfile, const char* xfunc,
     41              unsigned long xline, bool xpassed = true)
     42       : passed(xpassed), check(xcheck), file(xfile), func(xfunc), line(xline),
     43         msg() {}
     44 
     45   AssertData& SetFailed(std::string xmsg = std::string()) {
     46     msg = xmsg;
     47     passed = false;
     48     return *this;
     49   }
     50 
     51   void PrintFailed() const {
     52     std::fprintf(stderr, "%s:%lu %s: Assertion '%s' failed.\n", file, line,
     53                  func, check);
     54     if (!msg.empty())
     55       std::fprintf(stderr, "%s\n", msg.data());
     56   }
     57 
     58   bool passed;
     59   const char* check;
     60   const char* file;
     61   const char* func;
     62   unsigned long line;
     63   std::string msg;
     64 };
     65 
     66 // AssertHandler is the class constructed by failing CHECK macros. AssertHandler
     67 // will log information about the failures and abort when it is destructed.
     68 class AssertHandler {
     69 public:
     70   AssertHandler(AssertData const& Data)
     71       : passed(Data.passed) {
     72     if (!passed)
     73       Data.PrintFailed();
     74   }
     75 
     76   ~AssertHandler() TEST_NOEXCEPT_FALSE {
     77     if (!passed) {
     78       error_log << std::endl;
     79       std::abort();
     80     }
     81   }
     82 
     83   class LogType {
     84     friend class AssertHandler;
     85 
     86     template <class Tp>
     87     friend LogType& operator<<(LogType& log, Tp const& value) {
     88       if (!log.is_disabled) {
     89         SelectStream<Tp>::Print(value);
     90       }
     91       return log;
     92     }
     93 
     94     friend LogType& operator<<(LogType& log, EndLType* m) {
     95       if (!log.is_disabled) {
     96         SelectStream<EndLType*>::Print(m);
     97       }
     98       return log;
     99     }
    100 
    101   private:
    102     LogType(bool disable) : is_disabled(disable) {}
    103     bool is_disabled;
    104 
    105     LogType(LogType const&);
    106     LogType& operator=(LogType const&);
    107   };
    108 
    109   LogType& GetLog() {
    110     if (passed)
    111       return null_log;
    112     return error_log;
    113   }
    114 
    115 private:
    116   static LogType null_log;
    117   static LogType error_log;
    118 
    119   AssertHandler& operator=(const AssertHandler&) = delete;
    120   AssertHandler(const AssertHandler&) = delete;
    121   AssertHandler() = delete;
    122 
    123 private:
    124   bool passed;
    125 };
    126 
    127 AssertHandler::LogType AssertHandler::null_log(true);
    128 AssertHandler::LogType AssertHandler::error_log(false);
    129 
    130 template <class It1>
    131 std::string PrintRange(const char* Name, It1 F, It1 E) {
    132   std::stringstream ss;
    133   ss << "  " << Name << " = [";
    134   while (F != E) {
    135     ss << *F;
    136     ++F;
    137     if (F != E)
    138       ss << ", ";
    139   }
    140   ss << "]\n";
    141   return ss.str();
    142 }
    143 
    144 template <class Tp, class Up>
    145 std::string PrintMismatch(Tp const& LHS, Up const& RHS, int Elem) {
    146   std::stringstream ss;
    147   ss << "  Element " << Elem << " mismatched: `" << LHS << "` != `" << RHS
    148      << "`!\n";
    149   return ss.str();
    150 };
    151 
    152 struct EqualToComp {
    153   template <class Tp, class Up>
    154   bool operator()(Tp const& LHS, Up const& RHS) const {
    155     return LHS == RHS;
    156   }
    157 };
    158 
    159 template <class It1, class It2, class Comp>
    160 AssertData CheckCollectionsEqual(It1 F1, It1 E1, It2 F2, It2 E2,
    161                                  AssertData Data, Comp C = EqualToComp()) {
    162   const It1 F1Orig = F1;
    163   const It2 F2Orig = F2;
    164   bool Failed = false;
    165   std::string ErrorMsg;
    166   int Idx = 0;
    167   while (F1 != E1 && F2 != E2) {
    168     if (!(C(*F1, *F2))) {
    169       ErrorMsg += PrintMismatch(*F1, *F2, Idx);
    170       Failed = true;
    171       break;
    172     }
    173     ++Idx;
    174     ++F1;
    175     ++F2;
    176   }
    177   if (!Failed && (F1 != E1 || F2 != E2)) {
    178     ErrorMsg += "  Ranges have different sizes!\n";
    179     Failed = true;
    180   }
    181   if (Failed) {
    182     ErrorMsg += PrintRange("LHS", F1Orig, E1);
    183     ErrorMsg += PrintRange("RHS", F2Orig, E2);
    184     Data.SetFailed(ErrorMsg);
    185   }
    186   return Data;
    187 }
    188 } // namespace verbose_assert
    189 
    190 #ifdef __GNUC__
    191 #define ASSERT_FN_NAME() __PRETTY_FUNCTION__
    192 #else
    193 #define ASSERT_FN_NAME() __func__
    194 #endif
    195 
    196 #define DISPLAY(...) "    " #__VA_ARGS__ " = " << (__VA_ARGS__) << "\n"
    197 
    198 #define ASSERT(...)                                                            \
    199   ::verbose_assert::AssertHandler(::verbose_assert::AssertData(                \
    200     #__VA_ARGS__, __FILE__, ASSERT_FN_NAME(), __LINE__,(__VA_ARGS__))).GetLog()
    201 
    202 #define ASSERT_EQ(LHS, RHS) \
    203   ASSERT(LHS == RHS) << DISPLAY(LHS) << DISPLAY(RHS)
    204 #define ASSERT_NEQ(LHS, RHS) \
    205   ASSERT(LHS != RHS) << DISPLAY(LHS) << DISPLAY(RHS)
    206 #define ASSERT_PRED(PRED, LHS, RHS) \
    207   ASSERT(PRED(LHS, RHS)) << DISPLAY(LHS) << DISPLAY(RHS)
    208 
    209 #define ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, Comp)                        \
    210   (::verbose_assert::AssertHandler(                                            \
    211        ::verbose_assert::CheckCollectionsEqual(                                \
    212            F1, E1, F2, E2,                                                     \
    213            ::verbose_assert::AssertData("CheckCollectionsEqual(" #F1 ", " #E1  \
    214                                         ", " #F2 ", " #E2 ")",                 \
    215                                         __FILE__, ASSERT_FN_NAME(), __LINE__), \
    216            Comp))                                                              \
    217        .GetLog())
    218 
    219 #define ASSERT_COLLECTION_EQ(F1, E1, F2, E2)                                   \
    220   ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, ::verbose_assert::EqualToComp())
    221 
    222 #endif
    223