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 #define _CRT_SECURE_NO_WARNINGS 1 // Prevents deprecation warning with MSVC 7 #include "jsontest.h" 8 #include <stdio.h> 9 #include <string> 10 11 #if defined(_MSC_VER) 12 // Used to install a report hook that prevent dialog on assertion and error. 13 #include <crtdbg.h> 14 #endif // if defined(_MSC_VER) 15 16 #if defined(_WIN32) 17 // Used to prevent dialog on memory fault. 18 // Limits headers included by Windows.h 19 #define WIN32_LEAN_AND_MEAN 20 #define NOSERVICE 21 #define NOMCX 22 #define NOIME 23 #define NOSOUND 24 #define NOCOMM 25 #define NORPC 26 #define NOGDI 27 #define NOUSER 28 #define NODRIVERS 29 #define NOLOGERROR 30 #define NOPROFILER 31 #define NOMEMMGR 32 #define NOLFILEIO 33 #define NOOPENFILE 34 #define NORESOURCE 35 #define NOATOM 36 #define NOLANGUAGE 37 #define NOLSTRING 38 #define NODBCS 39 #define NOKEYBOARDINFO 40 #define NOGDICAPMASKS 41 #define NOCOLOR 42 #define NOGDIOBJ 43 #define NODRAWTEXT 44 #define NOTEXTMETRIC 45 #define NOSCALABLEFONT 46 #define NOBITMAP 47 #define NORASTEROPS 48 #define NOMETAFILE 49 #define NOSYSMETRICS 50 #define NOSYSTEMPARAMSINFO 51 #define NOMSG 52 #define NOWINSTYLES 53 #define NOWINOFFSETS 54 #define NOSHOWWINDOW 55 #define NODEFERWINDOWPOS 56 #define NOVIRTUALKEYCODES 57 #define NOKEYSTATES 58 #define NOWH 59 #define NOMENUS 60 #define NOSCROLL 61 #define NOCLIPBOARD 62 #define NOICONS 63 #define NOMB 64 #define NOSYSCOMMANDS 65 #define NOMDI 66 #define NOCTLMGR 67 #define NOWINMESSAGES 68 #include <windows.h> 69 #endif // if defined(_WIN32) 70 71 namespace JsonTest { 72 73 // class TestResult 74 // ////////////////////////////////////////////////////////////////// 75 76 TestResult::TestResult() 77 : predicateId_(1), lastUsedPredicateId_(0), messageTarget_(0) { 78 // The root predicate has id 0 79 rootPredicateNode_.id_ = 0; 80 rootPredicateNode_.next_ = 0; 81 predicateStackTail_ = &rootPredicateNode_; 82 } 83 84 void TestResult::setTestName(const std::string& name) { name_ = name; } 85 86 TestResult& 87 TestResult::addFailure(const char* file, unsigned int line, const char* expr) { 88 /// Walks the PredicateContext stack adding them to failures_ if not already 89 /// added. 90 unsigned int nestingLevel = 0; 91 PredicateContext* lastNode = rootPredicateNode_.next_; 92 for (; lastNode != 0; lastNode = lastNode->next_) { 93 if (lastNode->id_ > lastUsedPredicateId_) // new PredicateContext 94 { 95 lastUsedPredicateId_ = lastNode->id_; 96 addFailureInfo( 97 lastNode->file_, lastNode->line_, lastNode->expr_, nestingLevel); 98 // Link the PredicateContext to the failure for message target when 99 // popping the PredicateContext. 100 lastNode->failure_ = &(failures_.back()); 101 } 102 ++nestingLevel; 103 } 104 105 // Adds the failed assertion 106 addFailureInfo(file, line, expr, nestingLevel); 107 messageTarget_ = &(failures_.back()); 108 return *this; 109 } 110 111 void TestResult::addFailureInfo(const char* file, 112 unsigned int line, 113 const char* expr, 114 unsigned int nestingLevel) { 115 Failure failure; 116 failure.file_ = file; 117 failure.line_ = line; 118 if (expr) { 119 failure.expr_ = expr; 120 } 121 failure.nestingLevel_ = nestingLevel; 122 failures_.push_back(failure); 123 } 124 125 TestResult& TestResult::popPredicateContext() { 126 PredicateContext* lastNode = &rootPredicateNode_; 127 while (lastNode->next_ != 0 && lastNode->next_->next_ != 0) { 128 lastNode = lastNode->next_; 129 } 130 // Set message target to popped failure 131 PredicateContext* tail = lastNode->next_; 132 if (tail != 0 && tail->failure_ != 0) { 133 messageTarget_ = tail->failure_; 134 } 135 // Remove tail from list 136 predicateStackTail_ = lastNode; 137 lastNode->next_ = 0; 138 return *this; 139 } 140 141 bool TestResult::failed() const { return !failures_.empty(); } 142 143 unsigned int TestResult::getAssertionNestingLevel() const { 144 unsigned int level = 0; 145 const PredicateContext* lastNode = &rootPredicateNode_; 146 while (lastNode->next_ != 0) { 147 lastNode = lastNode->next_; 148 ++level; 149 } 150 return level; 151 } 152 153 void TestResult::printFailure(bool printTestName) const { 154 if (failures_.empty()) { 155 return; 156 } 157 158 if (printTestName) { 159 printf("* Detail of %s test failure:\n", name_.c_str()); 160 } 161 162 // Print in reverse to display the callstack in the right order 163 Failures::const_iterator itEnd = failures_.end(); 164 for (Failures::const_iterator it = failures_.begin(); it != itEnd; ++it) { 165 const Failure& failure = *it; 166 std::string indent(failure.nestingLevel_ * 2, ' '); 167 if (failure.file_) { 168 printf("%s%s(%d): ", indent.c_str(), failure.file_, failure.line_); 169 } 170 if (!failure.expr_.empty()) { 171 printf("%s\n", failure.expr_.c_str()); 172 } else if (failure.file_) { 173 printf("\n"); 174 } 175 if (!failure.message_.empty()) { 176 std::string reindented = indentText(failure.message_, indent + " "); 177 printf("%s\n", reindented.c_str()); 178 } 179 } 180 } 181 182 std::string TestResult::indentText(const std::string& text, 183 const std::string& indent) { 184 std::string reindented; 185 std::string::size_type lastIndex = 0; 186 while (lastIndex < text.size()) { 187 std::string::size_type nextIndex = text.find('\n', lastIndex); 188 if (nextIndex == std::string::npos) { 189 nextIndex = text.size() - 1; 190 } 191 reindented += indent; 192 reindented += text.substr(lastIndex, nextIndex - lastIndex + 1); 193 lastIndex = nextIndex + 1; 194 } 195 return reindented; 196 } 197 198 TestResult& TestResult::addToLastFailure(const std::string& message) { 199 if (messageTarget_ != 0) { 200 messageTarget_->message_ += message; 201 } 202 return *this; 203 } 204 205 TestResult& TestResult::operator<<(Json::Int64 value) { 206 return addToLastFailure(Json::valueToString(value)); 207 } 208 209 TestResult& TestResult::operator<<(Json::UInt64 value) { 210 return addToLastFailure(Json::valueToString(value)); 211 } 212 213 TestResult& TestResult::operator<<(bool value) { 214 return addToLastFailure(value ? "true" : "false"); 215 } 216 217 // class TestCase 218 // ////////////////////////////////////////////////////////////////// 219 220 TestCase::TestCase() : result_(0) {} 221 222 TestCase::~TestCase() {} 223 224 void TestCase::run(TestResult& result) { 225 result_ = &result; 226 runTestCase(); 227 } 228 229 // class Runner 230 // ////////////////////////////////////////////////////////////////// 231 232 Runner::Runner() {} 233 234 Runner& Runner::add(TestCaseFactory factory) { 235 tests_.push_back(factory); 236 return *this; 237 } 238 239 unsigned int Runner::testCount() const { 240 return static_cast<unsigned int>(tests_.size()); 241 } 242 243 std::string Runner::testNameAt(unsigned int index) const { 244 TestCase* test = tests_[index](); 245 std::string name = test->testName(); 246 delete test; 247 return name; 248 } 249 250 void Runner::runTestAt(unsigned int index, TestResult& result) const { 251 TestCase* test = tests_[index](); 252 result.setTestName(test->testName()); 253 printf("Testing %s: ", test->testName()); 254 fflush(stdout); 255 #if JSON_USE_EXCEPTION 256 try { 257 #endif // if JSON_USE_EXCEPTION 258 test->run(result); 259 #if JSON_USE_EXCEPTION 260 } 261 catch (const std::exception& e) { 262 result.addFailure(__FILE__, __LINE__, "Unexpected exception caught:") 263 << e.what(); 264 } 265 #endif // if JSON_USE_EXCEPTION 266 delete test; 267 const char* status = result.failed() ? "FAILED" : "OK"; 268 printf("%s\n", status); 269 fflush(stdout); 270 } 271 272 bool Runner::runAllTest(bool printSummary) const { 273 unsigned int count = testCount(); 274 std::deque<TestResult> failures; 275 for (unsigned int index = 0; index < count; ++index) { 276 TestResult result; 277 runTestAt(index, result); 278 if (result.failed()) { 279 failures.push_back(result); 280 } 281 } 282 283 if (failures.empty()) { 284 if (printSummary) { 285 printf("All %d tests passed\n", count); 286 } 287 return true; 288 } else { 289 for (unsigned int index = 0; index < failures.size(); ++index) { 290 TestResult& result = failures[index]; 291 result.printFailure(count > 1); 292 } 293 294 if (printSummary) { 295 unsigned int failedCount = static_cast<unsigned int>(failures.size()); 296 unsigned int passedCount = count - failedCount; 297 printf("%d/%d tests passed (%d failure(s))\n", 298 passedCount, 299 count, 300 failedCount); 301 } 302 return false; 303 } 304 } 305 306 bool Runner::testIndex(const std::string& testName, 307 unsigned int& indexOut) const { 308 unsigned int count = testCount(); 309 for (unsigned int index = 0; index < count; ++index) { 310 if (testNameAt(index) == testName) { 311 indexOut = index; 312 return true; 313 } 314 } 315 return false; 316 } 317 318 void Runner::listTests() const { 319 unsigned int count = testCount(); 320 for (unsigned int index = 0; index < count; ++index) { 321 printf("%s\n", testNameAt(index).c_str()); 322 } 323 } 324 325 int Runner::runCommandLine(int argc, const char* argv[]) const { 326 typedef std::deque<std::string> TestNames; 327 Runner subrunner; 328 for (int index = 1; index < argc; ++index) { 329 std::string opt = argv[index]; 330 if (opt == "--list-tests") { 331 listTests(); 332 return 0; 333 } else if (opt == "--test-auto") { 334 preventDialogOnCrash(); 335 } else if (opt == "--test") { 336 ++index; 337 if (index < argc) { 338 unsigned int testNameIndex; 339 if (testIndex(argv[index], testNameIndex)) { 340 subrunner.add(tests_[testNameIndex]); 341 } else { 342 fprintf(stderr, "Test '%s' does not exist!\n", argv[index]); 343 return 2; 344 } 345 } else { 346 printUsage(argv[0]); 347 return 2; 348 } 349 } else { 350 printUsage(argv[0]); 351 return 2; 352 } 353 } 354 bool succeeded; 355 if (subrunner.testCount() > 0) { 356 succeeded = subrunner.runAllTest(subrunner.testCount() > 1); 357 } else { 358 succeeded = runAllTest(true); 359 } 360 return succeeded ? 0 : 1; 361 } 362 363 #if defined(_MSC_VER) && defined(_DEBUG) 364 // Hook MSVCRT assertions to prevent dialog from appearing 365 static int 366 msvcrtSilentReportHook(int reportType, char* message, int* /*returnValue*/) { 367 // The default CRT handling of error and assertion is to display 368 // an error dialog to the user. 369 // Instead, when an error or an assertion occurs, we force the 370 // application to terminate using abort() after display 371 // the message on stderr. 372 if (reportType == _CRT_ERROR || reportType == _CRT_ASSERT) { 373 // calling abort() cause the ReportHook to be called 374 // The following is used to detect this case and let's the 375 // error handler fallback on its default behaviour ( 376 // display a warning message) 377 static volatile bool isAborting = false; 378 if (isAborting) { 379 return TRUE; 380 } 381 isAborting = true; 382 383 fprintf(stderr, "CRT Error/Assert:\n%s\n", message); 384 fflush(stderr); 385 abort(); 386 } 387 // Let's other reportType (_CRT_WARNING) be handled as they would by default 388 return FALSE; 389 } 390 #endif // if defined(_MSC_VER) 391 392 void Runner::preventDialogOnCrash() { 393 #if defined(_MSC_VER) && defined(_DEBUG) 394 // Install a hook to prevent MSVCRT error and assertion from 395 // popping a dialog 396 // This function a NO-OP in release configuration 397 // (which cause warning since msvcrtSilentReportHook is not referenced) 398 _CrtSetReportHook(&msvcrtSilentReportHook); 399 #endif // if defined(_MSC_VER) 400 401 // @todo investiguate this handler (for buffer overflow) 402 // _set_security_error_handler 403 404 #if defined(_WIN32) 405 // Prevents the system from popping a dialog for debugging if the 406 // application fails due to invalid memory access. 407 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | 408 SEM_NOOPENFILEERRORBOX); 409 #endif // if defined(_WIN32) 410 } 411 412 void Runner::printUsage(const char* appName) { 413 printf("Usage: %s [options]\n" 414 "\n" 415 "If --test is not specified, then all the test cases be run.\n" 416 "\n" 417 "Valid options:\n" 418 "--list-tests: print the name of all test cases on the standard\n" 419 " output and exit.\n" 420 "--test TESTNAME: executes the test case with the specified name.\n" 421 " May be repeated.\n" 422 "--test-auto: prevent dialog prompting for debugging on crash.\n", 423 appName); 424 } 425 426 // Assertion functions 427 // ////////////////////////////////////////////////////////////////// 428 429 TestResult& checkStringEqual(TestResult& result, 430 const std::string& expected, 431 const std::string& actual, 432 const char* file, 433 unsigned int line, 434 const char* expr) { 435 if (expected != actual) { 436 result.addFailure(file, line, expr); 437 result << "Expected: '" << expected << "'\n"; 438 result << "Actual : '" << actual << "'"; 439 } 440 return result; 441 } 442 443 } // namespace JsonTest 444