Check in jsoncpp.

Check in ab1e40f3bce061ea6f9bdc60351d6cde2a4f872b from
https://chromium.googlesource.com/external/jsoncpp/jsoncpp.git

This is the version used by Skia's test suite.

BUG:15087860

Change-Id: I16bf6ac927fa025b6527b48221d749484e1a04f4
diff --git a/src/test_lib_json/jsontest.cpp b/src/test_lib_json/jsontest.cpp
new file mode 100644
index 0000000..327d344
--- /dev/null
+++ b/src/test_lib_json/jsontest.cpp
@@ -0,0 +1,575 @@
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#define _CRT_SECURE_NO_WARNINGS 1 // Prevents deprecation warning with MSVC
+#include "jsontest.h"
+#include <stdio.h>
+#include <string>
+
+#if defined(_MSC_VER)
+// Used to install a report hook that prevent dialog on assertion and error.
+# include <crtdbg.h>
+#endif // if defined(_MSC_VER)
+
+#if defined(_WIN32)
+// Used to prevent dialog on memory fault.
+// Limits headers included by Windows.h
+# define WIN32_LEAN_AND_MEAN
+# define NOSERVICE
+# define NOMCX
+# define NOIME
+# define NOSOUND
+# define NOCOMM
+# define NORPC
+# define NOGDI
+# define NOUSER
+# define NODRIVERS
+# define NOLOGERROR
+# define NOPROFILER
+# define NOMEMMGR
+# define NOLFILEIO
+# define NOOPENFILE
+# define NORESOURCE
+# define NOATOM
+# define NOLANGUAGE
+# define NOLSTRING
+# define NODBCS
+# define NOKEYBOARDINFO
+# define NOGDICAPMASKS
+# define NOCOLOR
+# define NOGDIOBJ
+# define NODRAWTEXT
+# define NOTEXTMETRIC
+# define NOSCALABLEFONT
+# define NOBITMAP
+# define NORASTEROPS
+# define NOMETAFILE
+# define NOSYSMETRICS
+# define NOSYSTEMPARAMSINFO
+# define NOMSG
+# define NOWINSTYLES
+# define NOWINOFFSETS
+# define NOSHOWWINDOW
+# define NODEFERWINDOWPOS
+# define NOVIRTUALKEYCODES
+# define NOKEYSTATES
+# define NOWH
+# define NOMENUS
+# define NOSCROLL
+# define NOCLIPBOARD
+# define NOICONS
+# define NOMB
+# define NOSYSCOMMANDS
+# define NOMDI
+# define NOCTLMGR
+# define NOWINMESSAGES
+# include <windows.h>
+#endif // if defined(_WIN32)
+
+namespace JsonTest {
+
+
+// class TestResult
+// //////////////////////////////////////////////////////////////////
+
+TestResult::TestResult()
+   : predicateId_( 1 )
+   , lastUsedPredicateId_( 0 )
+   , messageTarget_( 0 )
+{
+   // The root predicate has id 0
+   rootPredicateNode_.id_ = 0;
+   rootPredicateNode_.next_ = 0;
+   predicateStackTail_ = &rootPredicateNode_;
+}
+
+
+void 
+TestResult::setTestName( const std::string &name )
+{
+   name_ = name;
+}
+
+TestResult &
+TestResult::addFailure( const char *file, unsigned int line,
+                        const char *expr )
+{
+   /// Walks the PredicateContext stack adding them to failures_ if not already added.
+   unsigned int nestingLevel = 0;
+   PredicateContext *lastNode = rootPredicateNode_.next_;
+   for ( ; lastNode != 0; lastNode = lastNode->next_ )
+   {
+      if ( lastNode->id_ > lastUsedPredicateId_ ) // new PredicateContext
+      {
+         lastUsedPredicateId_ = lastNode->id_;
+         addFailureInfo( lastNode->file_, lastNode->line_, lastNode->expr_,
+                         nestingLevel );
+         // Link the PredicateContext to the failure for message target when 
+         // popping the PredicateContext.
+         lastNode->failure_ = &( failures_.back() );
+      }
+      ++nestingLevel;
+   }
+
+   // Adds the failed assertion
+   addFailureInfo( file, line, expr, nestingLevel );
+   messageTarget_ = &( failures_.back() );
+   return *this;
+}
+
+
+void 
+TestResult::addFailureInfo( const char *file, unsigned int line,
+                            const char *expr, unsigned int nestingLevel )
+{
+   Failure failure;
+   failure.file_ = file;
+   failure.line_ = line;
+   if ( expr )
+   {
+      failure.expr_ = expr;
+   }
+   failure.nestingLevel_ = nestingLevel;
+   failures_.push_back( failure );
+}
+
+
+TestResult &
+TestResult::popPredicateContext()
+{
+   PredicateContext *lastNode = &rootPredicateNode_;
+   while ( lastNode->next_ != 0  &&  lastNode->next_->next_ != 0 )
+   {
+      lastNode = lastNode->next_;
+   }
+   // Set message target to popped failure
+   PredicateContext *tail = lastNode->next_;
+   if ( tail != 0  &&  tail->failure_ != 0 )
+   {
+      messageTarget_ = tail->failure_;
+   }
+   // Remove tail from list
+   predicateStackTail_ = lastNode;
+   lastNode->next_ = 0;
+   return *this;
+}
+
+
+bool 
+TestResult::failed() const
+{
+   return !failures_.empty();
+}
+
+
+unsigned int 
+TestResult::getAssertionNestingLevel() const
+{
+   unsigned int level = 0;
+   const PredicateContext *lastNode = &rootPredicateNode_;
+   while ( lastNode->next_ != 0 )
+   {
+      lastNode = lastNode->next_;
+      ++level;
+   }
+   return level;
+}
+
+
+void 
+TestResult::printFailure( bool printTestName ) const
+{
+   if ( failures_.empty() )
+   {
+      return;
+   }
+
+   if ( printTestName )
+   {
+      printf( "* Detail of %s test failure:\n", name_.c_str() );
+   }
+
+   // Print in reverse to display the callstack in the right order
+   Failures::const_iterator itEnd = failures_.end();
+   for ( Failures::const_iterator it = failures_.begin(); it != itEnd; ++it )
+   {
+      const Failure &failure = *it;
+      std::string indent( failure.nestingLevel_ * 2, ' ' );
+      if ( failure.file_ )
+      {
+         printf( "%s%s(%d): ", indent.c_str(), failure.file_, failure.line_ );
+      }
+      if ( !failure.expr_.empty() )
+      {
+         printf( "%s\n", failure.expr_.c_str() );
+      }
+      else if ( failure.file_ )
+      {
+         printf( "\n" );
+      }
+      if ( !failure.message_.empty() )
+      {
+         std::string reindented = indentText( failure.message_, indent + "  " );
+         printf( "%s\n", reindented.c_str() );
+      }
+   }
+}
+
+
+std::string 
+TestResult::indentText( const std::string &text, 
+                        const std::string &indent )
+{
+   std::string reindented;
+   std::string::size_type lastIndex = 0;
+   while ( lastIndex < text.size() )
+   {
+      std::string::size_type nextIndex = text.find( '\n', lastIndex );
+      if ( nextIndex == std::string::npos )
+      {
+         nextIndex = text.size() - 1;
+      }
+      reindented += indent;
+      reindented += text.substr( lastIndex, nextIndex - lastIndex + 1 );
+      lastIndex = nextIndex + 1;
+   }
+   return reindented;
+}
+
+
+TestResult &
+TestResult::addToLastFailure( const std::string &message )
+{
+   if ( messageTarget_ != 0 )
+   {
+      messageTarget_->message_ += message;
+   }
+   return *this;
+}
+
+TestResult &
+TestResult::operator << ( Json::Int64 value ) {
+   return addToLastFailure( Json::valueToString(value) );
+}
+
+
+TestResult &
+TestResult::operator << ( Json::UInt64 value ) {
+   return addToLastFailure( Json::valueToString(value) );
+}
+
+
+TestResult &
+TestResult::operator << ( bool value ) {
+   return addToLastFailure(value ? "true" : "false");
+}
+
+
+// class TestCase
+// //////////////////////////////////////////////////////////////////
+
+TestCase::TestCase()
+   : result_( 0 )
+{
+}
+
+
+TestCase::~TestCase()
+{
+}
+
+
+void 
+TestCase::run( TestResult &result )
+{
+   result_ = &result;
+   runTestCase();
+}
+
+
+
+// class Runner
+// //////////////////////////////////////////////////////////////////
+
+Runner::Runner()
+{
+}
+
+
+Runner &
+Runner::add( TestCaseFactory factory )
+{
+   tests_.push_back( factory );
+   return *this;
+}
+
+
+unsigned int 
+Runner::testCount() const
+{
+   return static_cast<unsigned int>( tests_.size() );
+}
+
+
+std::string 
+Runner::testNameAt( unsigned int index ) const
+{
+   TestCase *test = tests_[index]();
+   std::string name = test->testName();
+   delete test;
+   return name;
+}
+
+
+void 
+Runner::runTestAt( unsigned int index, TestResult &result ) const
+{
+   TestCase *test = tests_[index]();
+   result.setTestName( test->testName() );
+   printf( "Testing %s: ", test->testName() );
+   fflush( stdout );
+#if JSON_USE_EXCEPTION
+   try 
+   {
+#endif // if JSON_USE_EXCEPTION
+      test->run( result );
+#if JSON_USE_EXCEPTION
+   } 
+   catch ( const std::exception &e ) 
+   {
+      result.addFailure( __FILE__, __LINE__, 
+         "Unexpected exception caught:" ) << e.what();
+   }
+#endif // if JSON_USE_EXCEPTION
+   delete test;
+   const char *status = result.failed() ? "FAILED" 
+                                        : "OK";
+   printf( "%s\n", status );
+   fflush( stdout );
+}
+
+
+bool 
+Runner::runAllTest( bool printSummary ) const
+{
+   unsigned int count = testCount();
+   std::deque<TestResult> failures;
+   for ( unsigned int index = 0; index < count; ++index )
+   {
+      TestResult result;
+      runTestAt( index, result );
+      if ( result.failed() )
+      {
+         failures.push_back( result );
+      }
+   }
+
+   if ( failures.empty() )
+   {
+      if ( printSummary )
+      {
+         printf( "All %d tests passed\n", count );
+      }
+      return true;
+   }
+   else
+   {
+      for ( unsigned int index = 0; index < failures.size(); ++index )
+      {
+         TestResult &result = failures[index];
+         result.printFailure( count > 1 );
+      }
+
+      if ( printSummary )
+      {
+         unsigned int failedCount = static_cast<unsigned int>( failures.size() );
+         unsigned int passedCount = count - failedCount;
+         printf( "%d/%d tests passed (%d failure(s))\n", passedCount, count, failedCount );
+      }
+      return false;
+   }
+}
+
+
+bool 
+Runner::testIndex( const std::string &testName, 
+                   unsigned int &indexOut ) const
+{
+   unsigned int count = testCount();
+   for ( unsigned int index = 0; index < count; ++index )
+   {
+      if ( testNameAt(index) == testName )
+      {
+         indexOut = index;
+         return true;
+      }
+   }
+   return false;
+}
+
+
+void 
+Runner::listTests() const
+{
+   unsigned int count = testCount();
+   for ( unsigned int index = 0; index < count; ++index )
+   {
+      printf( "%s\n", testNameAt( index ).c_str() );
+   }
+}
+
+
+int 
+Runner::runCommandLine( int argc, const char *argv[] ) const
+{
+   typedef std::deque<std::string> TestNames;
+   Runner subrunner;
+   for ( int index = 1; index < argc; ++index )
+   {
+      std::string opt = argv[index];
+      if ( opt == "--list-tests" )
+      {
+         listTests();
+         return 0;
+      }
+      else if ( opt == "--test-auto" )
+      {
+         preventDialogOnCrash();
+      }
+      else if ( opt == "--test" )
+      {
+         ++index;
+         if ( index < argc )
+         {
+            unsigned int testNameIndex;
+            if ( testIndex( argv[index], testNameIndex ) )
+            {
+               subrunner.add( tests_[testNameIndex] );
+            }
+            else
+            {
+               fprintf( stderr, "Test '%s' does not exist!\n", argv[index] );
+               return 2;
+            }
+         }
+         else
+         {
+            printUsage( argv[0] );
+            return 2;
+         }
+      }
+      else
+      {
+         printUsage( argv[0] );
+         return 2;
+      }
+   }
+   bool succeeded;
+   if ( subrunner.testCount() > 0 )
+   {
+      succeeded = subrunner.runAllTest( subrunner.testCount() > 1 );
+   }
+   else
+   {
+      succeeded = runAllTest( true );
+   }
+   return succeeded ? 0 
+                    : 1;
+}
+
+
+#if defined(_MSC_VER)
+// Hook MSVCRT assertions to prevent dialog from appearing
+static int 
+msvcrtSilentReportHook( int reportType, char *message, int *returnValue )
+{
+   // The default CRT handling of error and assertion is to display
+   // an error dialog to the user.
+   // Instead, when an error or an assertion occurs, we force the 
+   // application to terminate using abort() after display
+   // the message on stderr. 
+   if ( reportType == _CRT_ERROR  ||  
+        reportType == _CRT_ASSERT )
+   {
+      // calling abort() cause the ReportHook to be called
+      // The following is used to detect this case and let's the
+      // error handler fallback on its default behaviour (
+      // display a warning message)
+      static volatile bool isAborting = false;
+      if ( isAborting ) 
+      {
+         return TRUE;
+      }
+      isAborting = true;
+
+      fprintf( stderr, "CRT Error/Assert:\n%s\n", message );
+      fflush( stderr );
+      abort();
+   }
+   // Let's other reportType (_CRT_WARNING) be handled as they would by default
+   return FALSE;
+}
+#endif // if defined(_MSC_VER)
+
+
+void 
+Runner::preventDialogOnCrash()
+{
+#if defined(_MSC_VER)
+   // Install a hook to prevent MSVCRT error and assertion from
+   // popping a dialog.
+   _CrtSetReportHook( &msvcrtSilentReportHook );
+#endif // if defined(_MSC_VER)
+
+   // @todo investiguate this handler (for buffer overflow)
+   // _set_security_error_handler
+
+#if defined(_WIN32)
+   // Prevents the system from popping a dialog for debugging if the
+   // application fails due to invalid memory access.
+   SetErrorMode( SEM_FAILCRITICALERRORS 
+                 | SEM_NOGPFAULTERRORBOX 
+                 | SEM_NOOPENFILEERRORBOX );
+#endif // if defined(_WIN32)
+}
+
+void 
+Runner::printUsage( const char *appName )
+{
+   printf( 
+      "Usage: %s [options]\n"
+      "\n"
+      "If --test is not specified, then all the test cases be run.\n"
+      "\n"
+      "Valid options:\n"
+      "--list-tests: print the name of all test cases on the standard\n"
+      "              output and exit.\n"
+      "--test TESTNAME: executes the test case with the specified name.\n"
+      "                 May be repeated.\n"
+      "--test-auto: prevent dialog prompting for debugging on crash.\n"
+      , appName );
+}
+
+
+
+// Assertion functions
+// //////////////////////////////////////////////////////////////////
+
+TestResult &
+checkStringEqual( TestResult &result, 
+                  const std::string &expected, const std::string &actual,
+                  const char *file, unsigned int line, const char *expr )
+{
+   if ( expected != actual )
+   {
+      result.addFailure( file, line, expr );
+      result << "Expected: '" << expected << "'\n";
+      result << "Actual  : '" << actual << "'";
+   }
+   return result;
+}
+
+
+} // namespace JsonTest