Home | History | Annotate | Download | only in test_lib_json
      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 // class TestResult
     74 // //////////////////////////////////////////////////////////////////
     75 
     76 TestResult::TestResult()
     77     : predicateId_(1), lastUsedPredicateId_(0), messageTarget_(0) {
     78   // The root predicate has id 0
     79   rootPredicateNode_.id_ = 0;
     80   rootPredicateNode_.next_ = 0;
     81   predicateStackTail_ = &rootPredicateNode_;
     82 }
     83 
     84 void TestResult::setTestName(const std::string& name) { name_ = name; }
     85 
     86 TestResult&
     87 TestResult::addFailure(const char* file, unsigned int line, const char* expr) {
     88   /// Walks the PredicateContext stack adding them to failures_ if not already
     89   /// added.
     90   unsigned int nestingLevel = 0;
     91   PredicateContext* lastNode = rootPredicateNode_.next_;
     92   for (; lastNode != 0; lastNode = lastNode->next_) {
     93     if (lastNode->id_ > lastUsedPredicateId_) // new PredicateContext
     94     {
     95       lastUsedPredicateId_ = lastNode->id_;
     96       addFailureInfo(
     97           lastNode->file_, lastNode->line_, lastNode->expr_, nestingLevel);
     98       // Link the PredicateContext to the failure for message target when
     99       // popping the PredicateContext.
    100       lastNode->failure_ = &(failures_.back());
    101     }
    102     ++nestingLevel;
    103   }
    104 
    105   // Adds the failed assertion
    106   addFailureInfo(file, line, expr, nestingLevel);
    107   messageTarget_ = &(failures_.back());
    108   return *this;
    109 }
    110 
    111 void TestResult::addFailureInfo(const char* file,
    112                                 unsigned int line,
    113                                 const char* expr,
    114                                 unsigned int nestingLevel) {
    115   Failure failure;
    116   failure.file_ = file;
    117   failure.line_ = line;
    118   if (expr) {
    119     failure.expr_ = expr;
    120   }
    121   failure.nestingLevel_ = nestingLevel;
    122   failures_.push_back(failure);
    123 }
    124 
    125 TestResult& TestResult::popPredicateContext() {
    126   PredicateContext* lastNode = &rootPredicateNode_;
    127   while (lastNode->next_ != 0 && lastNode->next_->next_ != 0) {
    128     lastNode = lastNode->next_;
    129   }
    130   // Set message target to popped failure
    131   PredicateContext* tail = lastNode->next_;
    132   if (tail != 0 && tail->failure_ != 0) {
    133     messageTarget_ = tail->failure_;
    134   }
    135   // Remove tail from list
    136   predicateStackTail_ = lastNode;
    137   lastNode->next_ = 0;
    138   return *this;
    139 }
    140 
    141 bool TestResult::failed() const { return !failures_.empty(); }
    142 
    143 unsigned int TestResult::getAssertionNestingLevel() const {
    144   unsigned int level = 0;
    145   const PredicateContext* lastNode = &rootPredicateNode_;
    146   while (lastNode->next_ != 0) {
    147     lastNode = lastNode->next_;
    148     ++level;
    149   }
    150   return level;
    151 }
    152 
    153 void TestResult::printFailure(bool printTestName) const {
    154   if (failures_.empty()) {
    155     return;
    156   }
    157 
    158   if (printTestName) {
    159     printf("* Detail of %s test failure:\n", name_.c_str());
    160   }
    161 
    162   // Print in reverse to display the callstack in the right order
    163   Failures::const_iterator itEnd = failures_.end();
    164   for (Failures::const_iterator it = failures_.begin(); it != itEnd; ++it) {
    165     const Failure& failure = *it;
    166     std::string indent(failure.nestingLevel_ * 2, ' ');
    167     if (failure.file_) {
    168       printf("%s%s(%d): ", indent.c_str(), failure.file_, failure.line_);
    169     }
    170     if (!failure.expr_.empty()) {
    171       printf("%s\n", failure.expr_.c_str());
    172     } else if (failure.file_) {
    173       printf("\n");
    174     }
    175     if (!failure.message_.empty()) {
    176       std::string reindented = indentText(failure.message_, indent + "  ");
    177       printf("%s\n", reindented.c_str());
    178     }
    179   }
    180 }
    181 
    182 std::string TestResult::indentText(const std::string& text,
    183                                    const std::string& indent) {
    184   std::string reindented;
    185   std::string::size_type lastIndex = 0;
    186   while (lastIndex < text.size()) {
    187     std::string::size_type nextIndex = text.find('\n', lastIndex);
    188     if (nextIndex == std::string::npos) {
    189       nextIndex = text.size() - 1;
    190     }
    191     reindented += indent;
    192     reindented += text.substr(lastIndex, nextIndex - lastIndex + 1);
    193     lastIndex = nextIndex + 1;
    194   }
    195   return reindented;
    196 }
    197 
    198 TestResult& TestResult::addToLastFailure(const std::string& message) {
    199   if (messageTarget_ != 0) {
    200     messageTarget_->message_ += message;
    201   }
    202   return *this;
    203 }
    204 
    205 TestResult& TestResult::operator<<(Json::Int64 value) {
    206   return addToLastFailure(Json::valueToString(value));
    207 }
    208 
    209 TestResult& TestResult::operator<<(Json::UInt64 value) {
    210   return addToLastFailure(Json::valueToString(value));
    211 }
    212 
    213 TestResult& TestResult::operator<<(bool value) {
    214   return addToLastFailure(value ? "true" : "false");
    215 }
    216 
    217 // class TestCase
    218 // //////////////////////////////////////////////////////////////////
    219 
    220 TestCase::TestCase() : result_(0) {}
    221 
    222 TestCase::~TestCase() {}
    223 
    224 void TestCase::run(TestResult& result) {
    225   result_ = &result;
    226   runTestCase();
    227 }
    228 
    229 // class Runner
    230 // //////////////////////////////////////////////////////////////////
    231 
    232 Runner::Runner() {}
    233 
    234 Runner& Runner::add(TestCaseFactory factory) {
    235   tests_.push_back(factory);
    236   return *this;
    237 }
    238 
    239 unsigned int Runner::testCount() const {
    240   return static_cast<unsigned int>(tests_.size());
    241 }
    242 
    243 std::string Runner::testNameAt(unsigned int index) const {
    244   TestCase* test = tests_[index]();
    245   std::string name = test->testName();
    246   delete test;
    247   return name;
    248 }
    249 
    250 void Runner::runTestAt(unsigned int index, TestResult& result) const {
    251   TestCase* test = tests_[index]();
    252   result.setTestName(test->testName());
    253   printf("Testing %s: ", test->testName());
    254   fflush(stdout);
    255 #if JSON_USE_EXCEPTION
    256   try {
    257 #endif // if JSON_USE_EXCEPTION
    258     test->run(result);
    259 #if JSON_USE_EXCEPTION
    260   }
    261   catch (const std::exception& e) {
    262     result.addFailure(__FILE__, __LINE__, "Unexpected exception caught:")
    263         << e.what();
    264   }
    265 #endif // if JSON_USE_EXCEPTION
    266   delete test;
    267   const char* status = result.failed() ? "FAILED" : "OK";
    268   printf("%s\n", status);
    269   fflush(stdout);
    270 }
    271 
    272 bool Runner::runAllTest(bool printSummary) const {
    273   unsigned int count = testCount();
    274   std::deque<TestResult> failures;
    275   for (unsigned int index = 0; index < count; ++index) {
    276     TestResult result;
    277     runTestAt(index, result);
    278     if (result.failed()) {
    279       failures.push_back(result);
    280     }
    281   }
    282 
    283   if (failures.empty()) {
    284     if (printSummary) {
    285       printf("All %d tests passed\n", count);
    286     }
    287     return true;
    288   } else {
    289     for (unsigned int index = 0; index < failures.size(); ++index) {
    290       TestResult& result = failures[index];
    291       result.printFailure(count > 1);
    292     }
    293 
    294     if (printSummary) {
    295       unsigned int failedCount = static_cast<unsigned int>(failures.size());
    296       unsigned int passedCount = count - failedCount;
    297       printf("%d/%d tests passed (%d failure(s))\n",
    298              passedCount,
    299              count,
    300              failedCount);
    301     }
    302     return false;
    303   }
    304 }
    305 
    306 bool Runner::testIndex(const std::string& testName,
    307                        unsigned int& indexOut) const {
    308   unsigned int count = testCount();
    309   for (unsigned int index = 0; index < count; ++index) {
    310     if (testNameAt(index) == testName) {
    311       indexOut = index;
    312       return true;
    313     }
    314   }
    315   return false;
    316 }
    317 
    318 void Runner::listTests() const {
    319   unsigned int count = testCount();
    320   for (unsigned int index = 0; index < count; ++index) {
    321     printf("%s\n", testNameAt(index).c_str());
    322   }
    323 }
    324 
    325 int Runner::runCommandLine(int argc, const char* argv[]) const {
    326   typedef std::deque<std::string> TestNames;
    327   Runner subrunner;
    328   for (int index = 1; index < argc; ++index) {
    329     std::string opt = argv[index];
    330     if (opt == "--list-tests") {
    331       listTests();
    332       return 0;
    333     } else if (opt == "--test-auto") {
    334       preventDialogOnCrash();
    335     } else if (opt == "--test") {
    336       ++index;
    337       if (index < argc) {
    338         unsigned int testNameIndex;
    339         if (testIndex(argv[index], testNameIndex)) {
    340           subrunner.add(tests_[testNameIndex]);
    341         } else {
    342           fprintf(stderr, "Test '%s' does not exist!\n", argv[index]);
    343           return 2;
    344         }
    345       } else {
    346         printUsage(argv[0]);
    347         return 2;
    348       }
    349     } else {
    350       printUsage(argv[0]);
    351       return 2;
    352     }
    353   }
    354   bool succeeded;
    355   if (subrunner.testCount() > 0) {
    356     succeeded = subrunner.runAllTest(subrunner.testCount() > 1);
    357   } else {
    358     succeeded = runAllTest(true);
    359   }
    360   return succeeded ? 0 : 1;
    361 }
    362 
    363 #if defined(_MSC_VER) && defined(_DEBUG)
    364 // Hook MSVCRT assertions to prevent dialog from appearing
    365 static int
    366 msvcrtSilentReportHook(int reportType, char* message, int* /*returnValue*/) {
    367   // The default CRT handling of error and assertion is to display
    368   // an error dialog to the user.
    369   // Instead, when an error or an assertion occurs, we force the
    370   // application to terminate using abort() after display
    371   // the message on stderr.
    372   if (reportType == _CRT_ERROR || reportType == _CRT_ASSERT) {
    373     // calling abort() cause the ReportHook to be called
    374     // The following is used to detect this case and let's the
    375     // error handler fallback on its default behaviour (
    376     // display a warning message)
    377     static volatile bool isAborting = false;
    378     if (isAborting) {
    379       return TRUE;
    380     }
    381     isAborting = true;
    382 
    383     fprintf(stderr, "CRT Error/Assert:\n%s\n", message);
    384     fflush(stderr);
    385     abort();
    386   }
    387   // Let's other reportType (_CRT_WARNING) be handled as they would by default
    388   return FALSE;
    389 }
    390 #endif // if defined(_MSC_VER)
    391 
    392 void Runner::preventDialogOnCrash() {
    393 #if defined(_MSC_VER) && defined(_DEBUG)
    394   // Install a hook to prevent MSVCRT error and assertion from
    395   // popping a dialog
    396   // This function a NO-OP in release configuration
    397   // (which cause warning since msvcrtSilentReportHook is not referenced)
    398   _CrtSetReportHook(&msvcrtSilentReportHook);
    399 #endif // if defined(_MSC_VER)
    400 
    401 // @todo investiguate this handler (for buffer overflow)
    402 // _set_security_error_handler
    403 
    404 #if defined(_WIN32)
    405   // Prevents the system from popping a dialog for debugging if the
    406   // application fails due to invalid memory access.
    407   SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
    408                SEM_NOOPENFILEERRORBOX);
    409 #endif // if defined(_WIN32)
    410 }
    411 
    412 void Runner::printUsage(const char* appName) {
    413   printf("Usage: %s [options]\n"
    414          "\n"
    415          "If --test is not specified, then all the test cases be run.\n"
    416          "\n"
    417          "Valid options:\n"
    418          "--list-tests: print the name of all test cases on the standard\n"
    419          "              output and exit.\n"
    420          "--test TESTNAME: executes the test case with the specified name.\n"
    421          "                 May be repeated.\n"
    422          "--test-auto: prevent dialog prompting for debugging on crash.\n",
    423          appName);
    424 }
    425 
    426 // Assertion functions
    427 // //////////////////////////////////////////////////////////////////
    428 
    429 TestResult& checkStringEqual(TestResult& result,
    430                              const std::string& expected,
    431                              const std::string& actual,
    432                              const char* file,
    433                              unsigned int line,
    434                              const char* expr) {
    435   if (expected != actual) {
    436     result.addFailure(file, line, expr);
    437     result << "Expected: '" << expected << "'\n";
    438     result << "Actual  : '" << actual << "'";
    439   }
    440   return result;
    441 }
    442 
    443 } // namespace JsonTest
    444