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// class TestResult
74// //////////////////////////////////////////////////////////////////
75
76TestResult::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
84void TestResult::setTestName(const std::string& name) { name_ = name; }
85
86TestResult&
87TestResult::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
111void 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
125TestResult& 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
141bool TestResult::failed() const { return !failures_.empty(); }
142
143unsigned 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
153void 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
182std::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
198TestResult& TestResult::addToLastFailure(const std::string& message) {
199  if (messageTarget_ != 0) {
200    messageTarget_->message_ += message;
201  }
202  return *this;
203}
204
205TestResult& TestResult::operator<<(Json::Int64 value) {
206  return addToLastFailure(Json::valueToString(value));
207}
208
209TestResult& TestResult::operator<<(Json::UInt64 value) {
210  return addToLastFailure(Json::valueToString(value));
211}
212
213TestResult& TestResult::operator<<(bool value) {
214  return addToLastFailure(value ? "true" : "false");
215}
216
217// class TestCase
218// //////////////////////////////////////////////////////////////////
219
220TestCase::TestCase() : result_(0) {}
221
222TestCase::~TestCase() {}
223
224void TestCase::run(TestResult& result) {
225  result_ = &result;
226  runTestCase();
227}
228
229// class Runner
230// //////////////////////////////////////////////////////////////////
231
232Runner::Runner() {}
233
234Runner& Runner::add(TestCaseFactory factory) {
235  tests_.push_back(factory);
236  return *this;
237}
238
239unsigned int Runner::testCount() const {
240  return static_cast<unsigned int>(tests_.size());
241}
242
243std::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
250void 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
272bool 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
306bool 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
318void 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
325int 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
365static int
366msvcrtSilentReportHook(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
392void 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
412void 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
429TestResult& 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