1 // Copyright 2007-2010 Baptiste Lepilleur 2 // Distributed under MIT license, or public domain if desired and 3 // recognized in your jurisdiction. 4 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 5 6 #ifndef JSONTEST_H_INCLUDED 7 #define JSONTEST_H_INCLUDED 8 9 #include <json/config.h> 10 #include <json/value.h> 11 #include <json/writer.h> 12 #include <stdio.h> 13 #include <deque> 14 #include <sstream> 15 #include <string> 16 17 // ////////////////////////////////////////////////////////////////// 18 // ////////////////////////////////////////////////////////////////// 19 // Mini Unit Testing framework 20 // ////////////////////////////////////////////////////////////////// 21 // ////////////////////////////////////////////////////////////////// 22 23 /** \brief Unit testing framework. 24 * \warning: all assertions are non-aborting, test case execution will continue 25 * even if an assertion namespace. 26 * This constraint is for portability: the framework needs to compile 27 * on Visual Studio 6 and must not require exception usage. 28 */ 29 namespace JsonTest { 30 31 class Failure { 32 public: 33 const char* file_; 34 unsigned int line_; 35 std::string expr_; 36 std::string message_; 37 unsigned int nestingLevel_; 38 }; 39 40 /// Context used to create the assertion callstack on failure. 41 /// Must be a POD to allow inline initialisation without stepping 42 /// into the debugger. 43 struct PredicateContext { 44 typedef unsigned int Id; 45 Id id_; 46 const char* file_; 47 unsigned int line_; 48 const char* expr_; 49 PredicateContext* next_; 50 /// Related Failure, set when the PredicateContext is converted 51 /// into a Failure. 52 Failure* failure_; 53 }; 54 55 class TestResult { 56 public: 57 TestResult(); 58 59 /// \internal Implementation detail for assertion macros 60 /// Not encapsulated to prevent step into when debugging failed assertions 61 /// Incremented by one on assertion predicate entry, decreased by one 62 /// by addPredicateContext(). 63 PredicateContext::Id predicateId_; 64 65 /// \internal Implementation detail for predicate macros 66 PredicateContext* predicateStackTail_; 67 68 void setTestName(const std::string& name); 69 70 /// Adds an assertion failure. 71 TestResult& 72 addFailure(const char* file, unsigned int line, const char* expr = 0); 73 74 /// Removes the last PredicateContext added to the predicate stack 75 /// chained list. 76 /// Next messages will be targed at the PredicateContext that was removed. 77 TestResult& popPredicateContext(); 78 79 bool failed() const; 80 81 void printFailure(bool printTestName) const; 82 83 // Generic operator that will work with anything ostream can deal with. 84 template <typename T> TestResult& operator<<(const T& value) { 85 std::ostringstream oss; 86 oss.precision(16); 87 oss.setf(std::ios_base::floatfield); 88 oss << value; 89 return addToLastFailure(oss.str()); 90 } 91 92 // Specialized versions. 93 TestResult& operator<<(bool value); 94 // std:ostream does not support 64bits integers on all STL implementation 95 TestResult& operator<<(Json::Int64 value); 96 TestResult& operator<<(Json::UInt64 value); 97 98 private: 99 TestResult& addToLastFailure(const std::string& message); 100 unsigned int getAssertionNestingLevel() const; 101 /// Adds a failure or a predicate context 102 void addFailureInfo(const char* file, 103 unsigned int line, 104 const char* expr, 105 unsigned int nestingLevel); 106 static std::string indentText(const std::string& text, 107 const std::string& indent); 108 109 typedef std::deque<Failure> Failures; 110 Failures failures_; 111 std::string name_; 112 PredicateContext rootPredicateNode_; 113 PredicateContext::Id lastUsedPredicateId_; 114 /// Failure which is the target of the messages added using operator << 115 Failure* messageTarget_; 116 }; 117 118 class TestCase { 119 public: 120 TestCase(); 121 122 virtual ~TestCase(); 123 124 void run(TestResult& result); 125 126 virtual const char* testName() const = 0; 127 128 protected: 129 TestResult* result_; 130 131 private: 132 virtual void runTestCase() = 0; 133 }; 134 135 /// Function pointer type for TestCase factory 136 typedef TestCase* (*TestCaseFactory)(); 137 138 class Runner { 139 public: 140 Runner(); 141 142 /// Adds a test to the suite 143 Runner& add(TestCaseFactory factory); 144 145 /// Runs test as specified on the command-line 146 /// If no command-line arguments are provided, run all tests. 147 /// If --list-tests is provided, then print the list of all test cases 148 /// If --test <testname> is provided, then run test testname. 149 int runCommandLine(int argc, const char* argv[]) const; 150 151 /// Runs all the test cases 152 bool runAllTest(bool printSummary) const; 153 154 /// Returns the number of test case in the suite 155 unsigned int testCount() const; 156 157 /// Returns the name of the test case at the specified index 158 std::string testNameAt(unsigned int index) const; 159 160 /// Runs the test case at the specified index using the specified TestResult 161 void runTestAt(unsigned int index, TestResult& result) const; 162 163 static void printUsage(const char* appName); 164 165 private: // prevents copy construction and assignment 166 Runner(const Runner& other); 167 Runner& operator=(const Runner& other); 168 169 private: 170 void listTests() const; 171 bool testIndex(const std::string& testName, unsigned int& index) const; 172 static void preventDialogOnCrash(); 173 174 private: 175 typedef std::deque<TestCaseFactory> Factories; 176 Factories tests_; 177 }; 178 179 template <typename T, typename U> 180 TestResult& checkEqual(TestResult& result, 181 const T& expected, 182 const U& actual, 183 const char* file, 184 unsigned int line, 185 const char* expr) { 186 if (static_cast<U>(expected) != actual) { 187 result.addFailure(file, line, expr); 188 result << "Expected: " << static_cast<U>(expected) << "\n"; 189 result << "Actual : " << actual; 190 } 191 return result; 192 } 193 194 TestResult& checkStringEqual(TestResult& result, 195 const std::string& expected, 196 const std::string& actual, 197 const char* file, 198 unsigned int line, 199 const char* expr); 200 201 } // namespace JsonTest 202 203 /// \brief Asserts that the given expression is true. 204 /// JSONTEST_ASSERT( x == y ) << "x=" << x << ", y=" << y; 205 /// JSONTEST_ASSERT( x == y ); 206 #define JSONTEST_ASSERT(expr) \ 207 if (expr) { \ 208 } else \ 209 result_->addFailure(__FILE__, __LINE__, #expr) 210 211 /// \brief Asserts that the given predicate is true. 212 /// The predicate may do other assertions and be a member function of the 213 /// fixture. 214 #define JSONTEST_ASSERT_PRED(expr) \ 215 { \ 216 JsonTest::PredicateContext _minitest_Context = { \ 217 result_->predicateId_, __FILE__, __LINE__, #expr \ 218 }; \ 219 result_->predicateStackTail_->next_ = &_minitest_Context; \ 220 result_->predicateId_ += 1; \ 221 result_->predicateStackTail_ = &_minitest_Context; \ 222 (expr); \ 223 result_->popPredicateContext(); \ 224 } 225 226 /// \brief Asserts that two values are equals. 227 #define JSONTEST_ASSERT_EQUAL(expected, actual) \ 228 JsonTest::checkEqual(*result_, \ 229 expected, \ 230 actual, \ 231 __FILE__, \ 232 __LINE__, \ 233 #expected " == " #actual) 234 235 /// \brief Asserts that two values are equals. 236 #define JSONTEST_ASSERT_STRING_EQUAL(expected, actual) \ 237 JsonTest::checkStringEqual(*result_, \ 238 std::string(expected), \ 239 std::string(actual), \ 240 __FILE__, \ 241 __LINE__, \ 242 #expected " == " #actual) 243 244 /// \brief Asserts that a given expression throws an exception 245 #define JSONTEST_ASSERT_THROWS(expr) \ 246 { \ 247 bool _threw = false; \ 248 try { \ 249 expr; \ 250 } \ 251 catch (...) { \ 252 _threw = true; \ 253 } \ 254 if (!_threw) \ 255 result_->addFailure( \ 256 __FILE__, __LINE__, "expected exception thrown: " #expr); \ 257 } 258 259 /// \brief Begin a fixture test case. 260 #define JSONTEST_FIXTURE(FixtureType, name) \ 261 class Test##FixtureType##name : public FixtureType { \ 262 public: \ 263 static JsonTest::TestCase* factory() { \ 264 return new Test##FixtureType##name(); \ 265 } \ 266 \ 267 public: /* overidden from TestCase */ \ 268 virtual const char* testName() const { return #FixtureType "/" #name; } \ 269 virtual void runTestCase(); \ 270 }; \ 271 \ 272 void Test##FixtureType##name::runTestCase() 273 274 #define JSONTEST_FIXTURE_FACTORY(FixtureType, name) \ 275 &Test##FixtureType##name::factory 276 277 #define JSONTEST_REGISTER_FIXTURE(runner, FixtureType, name) \ 278 (runner).add(JSONTEST_FIXTURE_FACTORY(FixtureType, name)) 279 280 #endif // ifndef JSONTEST_H_INCLUDED 281