Home | History | Annotate | Download | only in test_lib_json
      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