1 // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 // vi: set et ts=4 sw=2 sts=2: 3 #ifndef DUNE_COMMON_TEST_TESTSUITE_HH 4 #define DUNE_COMMON_TEST_TESTSUITE_HH 5 6 #include <iostream> 7 #include <sstream> 8 #include <string> 9 10 #include <dune/common/exceptions.hh> 11 #include <dune/common/test/collectorstream.hh> 12 13 14 15 namespace Dune { 16 17 18 19 /** 20 * \brief A Simple helper class to organize your test suite 21 * 22 * Usage: Construct a TestSuite and call check() or require() 23 * with the condition to check and probably a name for this check. 24 * These methods return a stream such that you can pipe in an 25 * explanantion accompanied by respective data to give a reason 26 * for a test failure. 27 */ 28 class TestSuite 29 { 30 public: 31 enum ThrowPolicy 32 { 33 AlwaysThrow, 34 ThrowOnRequired 35 }; 36 37 /** 38 * \brief Create TestSuite 39 * 40 * \param name A name to identify this TestSuite. Defaults to "". 41 * \param policy If AlwaysThrow any failing check will throw, otherwise only required checks will do. 42 */ TestSuite(ThrowPolicy policy,std::string name="")43 TestSuite(ThrowPolicy policy, std::string name="") : 44 name_(name), 45 checks_(0), 46 failedChecks_(0), 47 throwPolicy_(policy==AlwaysThrow) 48 {} 49 50 /** 51 * \brief Create TestSuite 52 * 53 * \param name A name to identify this TestSuite. Defaults to "". 54 * \param policy If AlwaysThrow any failing check will throw, otherwise only required checks will do. Defaults to ThrowOnRequired 55 */ TestSuite(std::string name="",ThrowPolicy policy=ThrowOnRequired)56 TestSuite(std::string name="", ThrowPolicy policy=ThrowOnRequired) : 57 name_(name), 58 checks_(0), 59 failedChecks_(0), 60 throwPolicy_(policy==AlwaysThrow) 61 {} 62 63 /** 64 * \brief Check condition 65 * 66 * This will throw an exception if the check fails and if the AlwaysThrow policy was used on creation. 67 * 68 * \param conditon Checks if this is true and increases the failure counter if not. 69 * \param name A name to identify this check. Defaults to "" 70 * \returns A CollectorStream that can be used to create a diagnostic message to be printed on failure. 71 */ check(bool condition,std::string name="")72 CollectorStream check(bool condition, std::string name="") 73 { 74 ++checks_; 75 if (not condition) 76 ++failedChecks_; 77 78 return CollectorStream([condition, name, this](std::string reason) { 79 if (not condition) 80 this->announceCheckResult(throwPolicy_, "CHECK ", name, reason); 81 }); 82 } 83 84 /** 85 * \brief Check a required condition 86 * 87 * This will always throw an exception if the check fails. 88 * 89 * \param conditon Checks if this is true and increases the failure counter if not. 90 * \param name A name to identify this check. Defaults to "" 91 * \returns A CollectorStream that can be used to create a diagnostic message to be printed on failure. 92 */ require(bool condition,std::string name="")93 CollectorStream require(bool condition, std::string name="") 94 { 95 ++checks_; 96 if (not condition) 97 ++failedChecks_; 98 99 return CollectorStream([condition, name, this](std::string reason) { 100 if (not condition) 101 this->announceCheckResult(true, "REQUIRED CHECK", name, reason); 102 }); 103 } 104 105 /** 106 * \brief Collect data from a sub-TestSuite 107 * 108 * This will incorporate the accumulated results of the sub-TestSuite 109 * into this one. If the sub-TestSuite failed, i.e., contained failed 110 * checks, a summary will be printed. 111 */ subTest(const TestSuite & subTest)112 void subTest(const TestSuite& subTest) 113 { 114 checks_ += subTest.checks_; 115 failedChecks_ += subTest.failedChecks_; 116 117 if (not subTest) 118 announceCheckResult(throwPolicy_, "SUBTEST", subTest.name(), std::to_string(subTest.failedChecks_)+"/"+std::to_string(subTest.checks_) + " checks failed in this subtest."); 119 } 120 121 /** 122 * \brief Check if this TestSuite failed 123 * 124 * \returns False if any of the executed tests failed, otherwise true. 125 */ operator bool() const126 explicit operator bool () const 127 { 128 return (failedChecks_==0); 129 } 130 131 /** 132 * \brief Query name 133 * 134 * \returns Name of this TestSuite 135 */ name() const136 std::string name() const 137 { 138 return name_; 139 } 140 141 /** 142 * \brief Print a summary of this TestSuite 143 * 144 * \returns False if any of the executed tests failed, otherwise true. 145 */ report() const146 bool report() const 147 { 148 if (failedChecks_>0) 149 std::cout << composeMessage("TEST ", name(), std::to_string(failedChecks_)+"/"+std::to_string(checks_) + " checks failed in this test.") << std::endl; 150 return (failedChecks_==0); 151 } 152 153 /** 154 * \brief Exit the test. 155 * 156 * This wil print a summary of the test and return an integer 157 * to be used on program exit. 158 * 159 * \returns 1 if any of the executed tests failed, otherwise 0. 160 */ exit() const161 int exit() const 162 { 163 return (report() ? 0: 1); 164 } 165 166 protected: 167 168 // Compose a diagnostic message composeMessage(std::string type,std::string name,std::string reason)169 static std::string composeMessage(std::string type, std::string name, std::string reason) 170 { 171 std::ostringstream s; 172 s << type << " FAILED"; 173 if (name!="") 174 s << "(" << name << ")"; 175 s << ": "; 176 if (reason!="") 177 s << reason; 178 return s.str(); 179 } 180 181 // Announce check results. To be called on failed checks announceCheckResult(bool throwException,std::string type,std::string name,std::string reason)182 static void announceCheckResult(bool throwException, std::string type, std::string name, std::string reason) 183 { 184 std::string message = composeMessage(type, name, reason); 185 std::cout << message << std::endl; 186 if (throwException) 187 { 188 Dune::Exception ex; 189 ex.message(message); 190 throw ex; 191 } 192 } 193 194 std::string name_; 195 std::size_t checks_; 196 std::size_t failedChecks_; 197 bool throwPolicy_; 198 }; 199 200 201 202 } // namespace Dune 203 204 205 206 #endif // DUNE_COMMON_TEST_TESTSUITE_HH 207