blob: 327d344bcea9fe76f9546e6e28a09dbf9c9efc8e [file] [log] [blame]
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -04001// 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
71namespace JsonTest {
72
73
74// class TestResult
75// //////////////////////////////////////////////////////////////////
76
77TestResult::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
89void
90TestResult::setTestName( const std::string &name )
91{
92 name_ = name;
93}
94
95TestResult &
96TestResult::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
123void
124TestResult::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
139TestResult &
140TestResult::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
160bool
161TestResult::failed() const
162{
163 return !failures_.empty();
164}
165
166
167unsigned int
168TestResult::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
181void
182TestResult::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
221std::string
222TestResult::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
242TestResult &
243TestResult::addToLastFailure( const std::string &message )
244{
245 if ( messageTarget_ != 0 )
246 {
247 messageTarget_->message_ += message;
248 }
249 return *this;
250}
251
252TestResult &
253TestResult::operator << ( Json::Int64 value ) {
254 return addToLastFailure( Json::valueToString(value) );
255}
256
257
258TestResult &
259TestResult::operator << ( Json::UInt64 value ) {
260 return addToLastFailure( Json::valueToString(value) );
261}
262
263
264TestResult &
265TestResult::operator << ( bool value ) {
266 return addToLastFailure(value ? "true" : "false");
267}
268
269
270// class TestCase
271// //////////////////////////////////////////////////////////////////
272
273TestCase::TestCase()
274 : result_( 0 )
275{
276}
277
278
279TestCase::~TestCase()
280{
281}
282
283
284void
285TestCase::run( TestResult &result )
286{
287 result_ = &result;
288 runTestCase();
289}
290
291
292
293// class Runner
294// //////////////////////////////////////////////////////////////////
295
296Runner::Runner()
297{
298}
299
300
301Runner &
302Runner::add( TestCaseFactory factory )
303{
304 tests_.push_back( factory );
305 return *this;
306}
307
308
309unsigned int
310Runner::testCount() const
311{
312 return static_cast<unsigned int>( tests_.size() );
313}
314
315
316std::string
317Runner::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
326void
327Runner::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
354bool
355Runner::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
396bool
397Runner::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
413void
414Runner::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
424int
425Runner::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
485static int
486msvcrtSilentReportHook( 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
517void
518Runner::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
538void
539Runner::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
560TestResult &
561checkStringEqual( 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