blob: ef9c543fa472ea224f81870dd39229f58acaeee5 [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.
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -050013#include <crtdbg.h>
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -040014#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
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -050019#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>
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -040069#endif // if defined(_WIN32)
70
71namespace JsonTest {
72
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -040073// class TestResult
74// //////////////////////////////////////////////////////////////////
75
76TestResult::TestResult()
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -050077 : predicateId_(1), lastUsedPredicateId_(0), messageTarget_(0) {
78 // The root predicate has id 0
79 rootPredicateNode_.id_ = 0;
80 rootPredicateNode_.next_ = 0;
81 predicateStackTail_ = &rootPredicateNode_;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -040082}
83
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -050084void TestResult::setTestName(const std::string& name) { name_ = name; }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -040085
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -050086TestResult&
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;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400109}
110
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500111void 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);
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400123}
124
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500125TestResult& 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;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400139}
140
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500141bool TestResult::failed() const { return !failures_.empty(); }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400142
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500143unsigned 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;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400151}
152
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500153void TestResult::printFailure(bool printTestName) const {
154 if (failures_.empty()) {
155 return;
156 }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400157
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500158 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 }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400180}
181
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500182std::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;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400196}
197
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500198TestResult& TestResult::addToLastFailure(const std::string& message) {
199 if (messageTarget_ != 0) {
200 messageTarget_->message_ += message;
201 }
202 return *this;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400203}
204
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500205TestResult& TestResult::operator<<(Json::Int64 value) {
206 return addToLastFailure(Json::valueToString(value));
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400207}
208
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500209TestResult& TestResult::operator<<(Json::UInt64 value) {
210 return addToLastFailure(Json::valueToString(value));
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400211}
212
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500213TestResult& TestResult::operator<<(bool value) {
214 return addToLastFailure(value ? "true" : "false");
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400215}
216
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400217// class TestCase
218// //////////////////////////////////////////////////////////////////
219
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500220TestCase::TestCase() : result_(0) {}
221
222TestCase::~TestCase() {}
223
224void TestCase::run(TestResult& result) {
225 result_ = &result;
226 runTestCase();
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400227}
228
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400229// class Runner
230// //////////////////////////////////////////////////////////////////
231
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500232Runner::Runner() {}
233
234Runner& Runner::add(TestCaseFactory factory) {
235 tests_.push_back(factory);
236 return *this;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400237}
238
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500239unsigned int Runner::testCount() const {
240 return static_cast<unsigned int>(tests_.size());
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400241}
242
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500243std::string Runner::testNameAt(unsigned int index) const {
244 TestCase* test = tests_[index]();
245 std::string name = test->testName();
246 delete test;
247 return name;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400248}
249
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500250void 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);
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400255#if JSON_USE_EXCEPTION
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500256 try {
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400257#endif // if JSON_USE_EXCEPTION
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500258 test->run(result);
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400259#if JSON_USE_EXCEPTION
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500260 }
261 catch (const std::exception& e) {
262 result.addFailure(__FILE__, __LINE__, "Unexpected exception caught:")
263 << e.what();
264 }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400265#endif // if JSON_USE_EXCEPTION
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500266 delete test;
267 const char* status = result.failed() ? "FAILED" : "OK";
268 printf("%s\n", status);
269 fflush(stdout);
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400270}
271
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500272bool 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 }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400282
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500283 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 }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400293
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500294 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;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400312 return true;
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500313 }
314 }
315 return false;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400316}
317
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500318void 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 }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400323}
324
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500325int 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;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400361}
362
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500363#if defined(_MSC_VER) && defined(_DEBUG)
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400364// Hook MSVCRT assertions to prevent dialog from appearing
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500365static 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;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400382
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500383 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;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400389}
390#endif // if defined(_MSC_VER)
391
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500392void 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);
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400399#endif // if defined(_MSC_VER)
400
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500401// @todo investiguate this handler (for buffer overflow)
402// _set_security_error_handler
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400403
404#if defined(_WIN32)
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500405 // 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);
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400409#endif // if defined(_WIN32)
410}
411
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500412void 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);
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400424}
425
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400426// Assertion functions
427// //////////////////////////////////////////////////////////////////
428
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500429TestResult& 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;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400441}
442
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400443} // namespace JsonTest