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 
TestResult()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
setTestName(const std::string & name)90 TestResult::setTestName( const std::string &name )
91 {
92    name_ = name;
93 }
94 
95 TestResult &
addFailure(const char * file,unsigned int line,const char * expr)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
addFailureInfo(const char * file,unsigned int line,const char * expr,unsigned int nestingLevel)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 &
popPredicateContext()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
failed() const161 TestResult::failed() const
162 {
163    return !failures_.empty();
164 }
165 
166 
167 unsigned int
getAssertionNestingLevel() const168 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
printFailure(bool printTestName) const182 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
indentText(const std::string & text,const std::string & indent)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 &
addToLastFailure(const std::string & message)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 &
operator <<(Json::Int64 value)253 TestResult::operator << ( Json::Int64 value ) {
254    return addToLastFailure( Json::valueToString(value) );
255 }
256 
257 
258 TestResult &
operator <<(Json::UInt64 value)259 TestResult::operator << ( Json::UInt64 value ) {
260    return addToLastFailure( Json::valueToString(value) );
261 }
262 
263 
264 TestResult &
operator <<(bool value)265 TestResult::operator << ( bool value ) {
266    return addToLastFailure(value ? "true" : "false");
267 }
268 
269 
270 // class TestCase
271 // //////////////////////////////////////////////////////////////////
272 
TestCase()273 TestCase::TestCase()
274    : result_( 0 )
275 {
276 }
277 
278 
~TestCase()279 TestCase::~TestCase()
280 {
281 }
282 
283 
284 void
run(TestResult & result)285 TestCase::run( TestResult &result )
286 {
287    result_ = &result;
288    runTestCase();
289 }
290 
291 
292 
293 // class Runner
294 // //////////////////////////////////////////////////////////////////
295 
Runner()296 Runner::Runner()
297 {
298 }
299 
300 
301 Runner &
add(TestCaseFactory factory)302 Runner::add( TestCaseFactory factory )
303 {
304    tests_.push_back( factory );
305    return *this;
306 }
307 
308 
309 unsigned int
testCount() const310 Runner::testCount() const
311 {
312    return static_cast<unsigned int>( tests_.size() );
313 }
314 
315 
316 std::string
testNameAt(unsigned int index) const317 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
runTestAt(unsigned int index,TestResult & result) const327 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
runAllTest(bool printSummary) const355 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
testIndex(const std::string & testName,unsigned int & indexOut) const397 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
listTests() const414 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
runCommandLine(int argc,const char * argv[]) const425 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
msvcrtSilentReportHook(int reportType,char * message,int * returnValue)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
preventDialogOnCrash()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
printUsage(const char * appName)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 &
checkStringEqual(TestResult & result,const std::string & expected,const std::string & actual,const char * file,unsigned int line,const char * expr)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