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 74 // class TestResult 75 // ////////////////////////////////////////////////////////////////// 76 77 TestResult::TestResult() 78 : predicateId_( 1 ) 79 , lastUsedPredicateId_( 0 ) 80 , messageTarget_( 0 ) 81 { 82 // The root predicate has id 0 83 rootPredicateNode_.id_ = 0; 84 rootPredicateNode_.next_ = 0; 85 predicateStackTail_ = &rootPredicateNode_; 86 } 87 88 89 void 90 TestResult::setTestName( const std::string &name ) 91 { 92 name_ = name; 93 } 94 95 TestResult & 96 TestResult::addFailure( const char *file, unsigned int line, 97 const char *expr ) 98 { 99 /// Walks the PredicateContext stack adding them to failures_ if not already added. 100 unsigned int nestingLevel = 0; 101 PredicateContext *lastNode = rootPredicateNode_.next_; 102 for ( ; lastNode != 0; lastNode = lastNode->next_ ) 103 { 104 if ( lastNode->id_ > lastUsedPredicateId_ ) // new PredicateContext 105 { 106 lastUsedPredicateId_ = lastNode->id_; 107 addFailureInfo( lastNode->file_, lastNode->line_, lastNode->expr_, 108 nestingLevel ); 109 // Link the PredicateContext to the failure for message target when 110 // popping the PredicateContext. 111 lastNode->failure_ = &( failures_.back() ); 112 } 113 ++nestingLevel; 114 } 115 116 // Adds the failed assertion 117 addFailureInfo( file, line, expr, nestingLevel ); 118 messageTarget_ = &( failures_.back() ); 119 return *this; 120 } 121 122 123 void 124 TestResult::addFailureInfo( const char *file, unsigned int line, 125 const char *expr, unsigned int nestingLevel ) 126 { 127 Failure failure; 128 failure.file_ = file; 129 failure.line_ = line; 130 if ( expr ) 131 { 132 failure.expr_ = expr; 133 } 134 failure.nestingLevel_ = nestingLevel; 135 failures_.push_back( failure ); 136 } 137 138 139 TestResult & 140 TestResult::popPredicateContext() 141 { 142 PredicateContext *lastNode = &rootPredicateNode_; 143 while ( lastNode->next_ != 0 && lastNode->next_->next_ != 0 ) 144 { 145 lastNode = lastNode->next_; 146 } 147 // Set message target to popped failure 148 PredicateContext *tail = lastNode->next_; 149 if ( tail != 0 && tail->failure_ != 0 ) 150 { 151 messageTarget_ = tail->failure_; 152 } 153 // Remove tail from list 154 predicateStackTail_ = lastNode; 155 lastNode->next_ = 0; 156 return *this; 157 } 158 159 160 bool 161 TestResult::failed() const 162 { 163 return !failures_.empty(); 164 } 165 166 167 unsigned int 168 TestResult::getAssertionNestingLevel() const 169 { 170 unsigned int level = 0; 171 const PredicateContext *lastNode = &rootPredicateNode_; 172 while ( lastNode->next_ != 0 ) 173 { 174 lastNode = lastNode->next_; 175 ++level; 176 } 177 return level; 178 } 179 180 181 void 182 TestResult::printFailure( bool printTestName ) const 183 { 184 if ( failures_.empty() ) 185 { 186 return; 187 } 188 189 if ( printTestName ) 190 { 191 printf( "* Detail of %s test failure:\n", name_.c_str() ); 192 } 193 194 // Print in reverse to display the callstack in the right order 195 Failures::const_iterator itEnd = failures_.end(); 196 for ( Failures::const_iterator it = failures_.begin(); it != itEnd; ++it ) 197 { 198 const Failure &failure = *it; 199 std::string indent( failure.nestingLevel_ * 2, ' ' ); 200 if ( failure.file_ ) 201 { 202 printf( "%s%s(%d): ", indent.c_str(), failure.file_, failure.line_ ); 203 } 204 if ( !failure.expr_.empty() ) 205 { 206 printf( "%s\n", failure.expr_.c_str() ); 207 } 208 else if ( failure.file_ ) 209 { 210 printf( "\n" ); 211 } 212 if ( !failure.message_.empty() ) 213 { 214 std::string reindented = indentText( failure.message_, indent + " " ); 215 printf( "%s\n", reindented.c_str() ); 216 } 217 } 218 } 219 220 221 std::string 222 TestResult::indentText( const std::string &text, 223 const std::string &indent ) 224 { 225 std::string reindented; 226 std::string::size_type lastIndex = 0; 227 while ( lastIndex < text.size() ) 228 { 229 std::string::size_type nextIndex = text.find( '\n', lastIndex ); 230 if ( nextIndex == std::string::npos ) 231 { 232 nextIndex = text.size() - 1; 233 } 234 reindented += indent; 235 reindented += text.substr( lastIndex, nextIndex - lastIndex + 1 ); 236 lastIndex = nextIndex + 1; 237 } 238 return reindented; 239 } 240 241 242 TestResult & 243 TestResult::addToLastFailure( const std::string &message ) 244 { 245 if ( messageTarget_ != 0 ) 246 { 247 messageTarget_->message_ += message; 248 } 249 return *this; 250 } 251 252 TestResult & 253 TestResult::operator << ( Json::Int64 value ) { 254 return addToLastFailure( Json::valueToString(value) ); 255 } 256 257 258 TestResult & 259 TestResult::operator << ( Json::UInt64 value ) { 260 return addToLastFailure( Json::valueToString(value) ); 261 } 262 263 264 TestResult & 265 TestResult::operator << ( bool value ) { 266 return addToLastFailure(value ? "true" : "false"); 267 } 268 269 270 // class TestCase 271 // ////////////////////////////////////////////////////////////////// 272 273 TestCase::TestCase() 274 : result_( 0 ) 275 { 276 } 277 278 279 TestCase::~TestCase() 280 { 281 } 282 283 284 void 285 TestCase::run( TestResult &result ) 286 { 287 result_ = &result; 288 runTestCase(); 289 } 290 291 292 293 // class Runner 294 // ////////////////////////////////////////////////////////////////// 295 296 Runner::Runner() 297 { 298 } 299 300 301 Runner & 302 Runner::add( TestCaseFactory factory ) 303 { 304 tests_.push_back( factory ); 305 return *this; 306 } 307 308 309 unsigned int 310 Runner::testCount() const 311 { 312 return static_cast<unsigned int>( tests_.size() ); 313 } 314 315 316 std::string 317 Runner::testNameAt( unsigned int index ) const 318 { 319 TestCase *test = tests_[index](); 320 std::string name = test->testName(); 321 delete test; 322 return name; 323 } 324 325 326 void 327 Runner::runTestAt( unsigned int index, TestResult &result ) const 328 { 329 TestCase *test = tests_[index](); 330 result.setTestName( test->testName() ); 331 printf( "Testing %s: ", test->testName() ); 332 fflush( stdout ); 333 #if JSON_USE_EXCEPTION 334 try 335 { 336 #endif // if JSON_USE_EXCEPTION 337 test->run( result ); 338 #if JSON_USE_EXCEPTION 339 } 340 catch ( const std::exception &e ) 341 { 342 result.addFailure( __FILE__, __LINE__, 343 "Unexpected exception caught:" ) << e.what(); 344 } 345 #endif // if JSON_USE_EXCEPTION 346 delete test; 347 const char *status = result.failed() ? "FAILED" 348 : "OK"; 349 printf( "%s\n", status ); 350 fflush( stdout ); 351 } 352 353 354 bool 355 Runner::runAllTest( bool printSummary ) const 356 { 357 unsigned int count = testCount(); 358 std::deque<TestResult> failures; 359 for ( unsigned int index = 0; index < count; ++index ) 360 { 361 TestResult result; 362 runTestAt( index, result ); 363 if ( result.failed() ) 364 { 365 failures.push_back( result ); 366 } 367 } 368 369 if ( failures.empty() ) 370 { 371 if ( printSummary ) 372 { 373 printf( "All %d tests passed\n", count ); 374 } 375 return true; 376 } 377 else 378 { 379 for ( unsigned int index = 0; index < failures.size(); ++index ) 380 { 381 TestResult &result = failures[index]; 382 result.printFailure( count > 1 ); 383 } 384 385 if ( printSummary ) 386 { 387 unsigned int failedCount = static_cast<unsigned int>( failures.size() ); 388 unsigned int passedCount = count - failedCount; 389 printf( "%d/%d tests passed (%d failure(s))\n", passedCount, count, failedCount ); 390 } 391 return false; 392 } 393 } 394 395 396 bool 397 Runner::testIndex( const std::string &testName, 398 unsigned int &indexOut ) const 399 { 400 unsigned int count = testCount(); 401 for ( unsigned int index = 0; index < count; ++index ) 402 { 403 if ( testNameAt(index) == testName ) 404 { 405 indexOut = index; 406 return true; 407 } 408 } 409 return false; 410 } 411 412 413 void 414 Runner::listTests() const 415 { 416 unsigned int count = testCount(); 417 for ( unsigned int index = 0; index < count; ++index ) 418 { 419 printf( "%s\n", testNameAt( index ).c_str() ); 420 } 421 } 422 423 424 int 425 Runner::runCommandLine( int argc, const char *argv[] ) const 426 { 427 typedef std::deque<std::string> TestNames; 428 Runner subrunner; 429 for ( int index = 1; index < argc; ++index ) 430 { 431 std::string opt = argv[index]; 432 if ( opt == "--list-tests" ) 433 { 434 listTests(); 435 return 0; 436 } 437 else if ( opt == "--test-auto" ) 438 { 439 preventDialogOnCrash(); 440 } 441 else if ( opt == "--test" ) 442 { 443 ++index; 444 if ( index < argc ) 445 { 446 unsigned int testNameIndex; 447 if ( testIndex( argv[index], testNameIndex ) ) 448 { 449 subrunner.add( tests_[testNameIndex] ); 450 } 451 else 452 { 453 fprintf( stderr, "Test '%s' does not exist!\n", argv[index] ); 454 return 2; 455 } 456 } 457 else 458 { 459 printUsage( argv[0] ); 460 return 2; 461 } 462 } 463 else 464 { 465 printUsage( argv[0] ); 466 return 2; 467 } 468 } 469 bool succeeded; 470 if ( subrunner.testCount() > 0 ) 471 { 472 succeeded = subrunner.runAllTest( subrunner.testCount() > 1 ); 473 } 474 else 475 { 476 succeeded = runAllTest( true ); 477 } 478 return succeeded ? 0 479 : 1; 480 } 481 482 483 #if defined(_MSC_VER) 484 // Hook MSVCRT assertions to prevent dialog from appearing 485 static int 486 msvcrtSilentReportHook( int reportType, char *message, int *returnValue ) 487 { 488 // The default CRT handling of error and assertion is to display 489 // an error dialog to the user. 490 // Instead, when an error or an assertion occurs, we force the 491 // application to terminate using abort() after display 492 // the message on stderr. 493 if ( reportType == _CRT_ERROR || 494 reportType == _CRT_ASSERT ) 495 { 496 // calling abort() cause the ReportHook to be called 497 // The following is used to detect this case and let's the 498 // error handler fallback on its default behaviour ( 499 // display a warning message) 500 static volatile bool isAborting = false; 501 if ( isAborting ) 502 { 503 return TRUE; 504 } 505 isAborting = true; 506 507 fprintf( stderr, "CRT Error/Assert:\n%s\n", message ); 508 fflush( stderr ); 509 abort(); 510 } 511 // Let's other reportType (_CRT_WARNING) be handled as they would by default 512 return FALSE; 513 } 514 #endif // if defined(_MSC_VER) 515 516 517 void 518 Runner::preventDialogOnCrash() 519 { 520 #if defined(_MSC_VER) 521 // Install a hook to prevent MSVCRT error and assertion from 522 // popping a dialog. 523 _CrtSetReportHook( &msvcrtSilentReportHook ); 524 #endif // if defined(_MSC_VER) 525 526 // @todo investiguate this handler (for buffer overflow) 527 // _set_security_error_handler 528 529 #if defined(_WIN32) 530 // Prevents the system from popping a dialog for debugging if the 531 // application fails due to invalid memory access. 532 SetErrorMode( SEM_FAILCRITICALERRORS 533 | SEM_NOGPFAULTERRORBOX 534 | SEM_NOOPENFILEERRORBOX ); 535 #endif // if defined(_WIN32) 536 } 537 538 void 539 Runner::printUsage( const char *appName ) 540 { 541 printf( 542 "Usage: %s [options]\n" 543 "\n" 544 "If --test is not specified, then all the test cases be run.\n" 545 "\n" 546 "Valid options:\n" 547 "--list-tests: print the name of all test cases on the standard\n" 548 " output and exit.\n" 549 "--test TESTNAME: executes the test case with the specified name.\n" 550 " May be repeated.\n" 551 "--test-auto: prevent dialog prompting for debugging on crash.\n" 552 , appName ); 553 } 554 555 556 557 // Assertion functions 558 // ////////////////////////////////////////////////////////////////// 559 560 TestResult & 561 checkStringEqual( TestResult &result, 562 const std::string &expected, const std::string &actual, 563 const char *file, unsigned int line, const char *expr ) 564 { 565 if ( expected != actual ) 566 { 567 result.addFailure( file, line, expr ); 568 result << "Expected: '" << expected << "'\n"; 569 result << "Actual : '" << actual << "'"; 570 } 571 return result; 572 } 573 574 575 } // namespace JsonTest 576