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