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
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
576