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