1 |
#define _CRT_SECURE_NO_WARNINGS 1 // Prevents deprecation warning with MSVC |
2 |
#include "jsontest.h" |
3 |
#include <stdio.h> |
4 |
#include <string> |
5 |
|
6 |
#if defined(_MSC_VER) |
7 |
// Used to install a report hook that prevent dialog on assertion and error. |
8 |
# include <crtdbg.h> |
9 |
#endif // if defined(_MSC_VER) |
10 |
|
11 |
#if defined(_WIN32) |
12 |
// Used to prevent dialog on memory fault. |
13 |
// Limits headers included by Windows.h |
14 |
# define WIN32_LEAN_AND_MEAN |
15 |
# define NOSERVICE |
16 |
# define NOMCX |
17 |
# define NOIME |
18 |
# define NOSOUND |
19 |
# define NOCOMM |
20 |
# define NORPC |
21 |
# define NOGDI |
22 |
# define NOUSER |
23 |
# define NODRIVERS |
24 |
# define NOLOGERROR |
25 |
# define NOPROFILER |
26 |
# define NOMEMMGR |
27 |
# define NOLFILEIO |
28 |
# define NOOPENFILE |
29 |
# define NORESOURCE |
30 |
# define NOATOM |
31 |
# define NOLANGUAGE |
32 |
# define NOLSTRING |
33 |
# define NODBCS |
34 |
# define NOKEYBOARDINFO |
35 |
# define NOGDICAPMASKS |
36 |
# define NOCOLOR |
37 |
# define NOGDIOBJ |
38 |
# define NODRAWTEXT |
39 |
# define NOTEXTMETRIC |
40 |
# define NOSCALABLEFONT |
41 |
# define NOBITMAP |
42 |
# define NORASTEROPS |
43 |
# define NOMETAFILE |
44 |
# define NOSYSMETRICS |
45 |
# define NOSYSTEMPARAMSINFO |
46 |
# define NOMSG |
47 |
# define NOWINSTYLES |
48 |
# define NOWINOFFSETS |
49 |
# define NOSHOWWINDOW |
50 |
# define NODEFERWINDOWPOS |
51 |
# define NOVIRTUALKEYCODES |
52 |
# define NOKEYSTATES |
53 |
# define NOWH |
54 |
# define NOMENUS |
55 |
# define NOSCROLL |
56 |
# define NOCLIPBOARD |
57 |
# define NOICONS |
58 |
# define NOMB |
59 |
# define NOSYSCOMMANDS |
60 |
# define NOMDI |
61 |
# define NOCTLMGR |
62 |
# define NOWINMESSAGES |
63 |
# include <windows.h> |
64 |
#endif // if defined(_WIN32) |
65 |
|
66 |
namespace JsonTest |
67 |
{ |
68 |
/* |
69 |
* class TestResult |
70 |
* ////////////////////////////////////////////////////////////////// |
71 |
*/ |
72 |
|
73 |
TestResult::TestResult () |
74 |
: predicateId_ (1) |
75 |
, lastUsedPredicateId_ (0) |
76 |
, messageTarget_ (0) |
77 |
{ |
78 |
/* The root predicate has id 0 */ |
79 |
rootPredicateNode_.id_ = 0; |
80 |
rootPredicateNode_.next_ = 0; |
81 |
predicateStackTail_ = &rootPredicateNode_; |
82 |
} |
83 |
|
84 |
void |
85 |
TestResult::setTestName (const std::string &name) |
86 |
{ |
87 |
name_ = name; |
88 |
} |
89 |
|
90 |
TestResult & |
91 |
TestResult::addFailure (const char *file, unsigned int line, const char *expr) |
92 |
{ |
93 |
/* / Walks the PredicateContext stack adding them to failures_ if not already added. */ |
94 |
unsigned int nestingLevel = 0; |
95 |
PredicateContext *lastNode = rootPredicateNode_.next_; |
96 |
|
97 |
for (; lastNode != 0; lastNode = lastNode->next_) |
98 |
{ |
99 |
if (lastNode->id_ > lastUsedPredicateId_) /* new PredicateContext */ |
100 |
{ |
101 |
lastUsedPredicateId_ = lastNode->id_; |
102 |
addFailureInfo (lastNode->file_, lastNode->line_, lastNode->expr_, |
103 |
nestingLevel); |
104 |
/* |
105 |
* Link the PredicateContext to the failure for message target when |
106 |
* popping the PredicateContext. |
107 |
*/ |
108 |
lastNode->failure_ = &(failures_.back ()); |
109 |
} |
110 |
++nestingLevel; |
111 |
} |
112 |
|
113 |
/* Adds the failed assertion */ |
114 |
addFailureInfo (file, line, expr, nestingLevel); |
115 |
messageTarget_ = &(failures_.back ()); |
116 |
return *this; |
117 |
} |
118 |
|
119 |
void |
120 |
TestResult::addFailureInfo (const char *file, unsigned int line, const char *expr, unsigned int nestingLevel) |
121 |
{ |
122 |
Failure failure; |
123 |
|
124 |
failure.file_ = file; |
125 |
failure.line_ = line; |
126 |
if (expr) |
127 |
failure.expr_ = expr; |
128 |
failure.nestingLevel_ = nestingLevel; |
129 |
failures_.push_back (failure); |
130 |
} |
131 |
|
132 |
TestResult & |
133 |
TestResult::popPredicateContext () |
134 |
{ |
135 |
PredicateContext *lastNode = &rootPredicateNode_; |
136 |
|
137 |
while (lastNode->next_ != 0 && lastNode->next_->next_ != 0) |
138 |
lastNode = lastNode->next_; |
139 |
/* Set message target to popped failure */ |
140 |
PredicateContext *tail = lastNode->next_; |
141 |
if (tail != 0 && tail->failure_ != 0) |
142 |
messageTarget_ = tail->failure_; |
143 |
/* Remove tail from list */ |
144 |
predicateStackTail_ = lastNode; |
145 |
lastNode->next_ = 0; |
146 |
return *this; |
147 |
} |
148 |
|
149 |
bool |
150 |
TestResult::failed () const |
151 |
{ |
152 |
return !failures_.empty (); |
153 |
} |
154 |
|
155 |
unsigned int |
156 |
TestResult::getAssertionNestingLevel () const |
157 |
{ |
158 |
unsigned int level = 0; |
159 |
const PredicateContext *lastNode = &rootPredicateNode_; |
160 |
|
161 |
while (lastNode->next_ != 0) |
162 |
{ |
163 |
lastNode = lastNode->next_; |
164 |
++level; |
165 |
} |
166 |
return level; |
167 |
} |
168 |
|
169 |
void |
170 |
TestResult::printFailure (bool printTestName) const |
171 |
{ |
172 |
if (failures_.empty ()) |
173 |
return; |
174 |
|
175 |
if (printTestName) |
176 |
printf ("* Detail of %s test failure:\n", name_.c_str ()); |
177 |
|
178 |
/* Print in reverse to display the callstack in the right order */ |
179 |
Failures::const_iterator itEnd = failures_.end (); |
180 |
for (Failures::const_iterator it = failures_.begin (); it != itEnd; ++it) |
181 |
{ |
182 |
const Failure &failure = *it; |
183 |
std::string indent (failure.nestingLevel_ * 2, ' '); |
184 |
if (failure.file_) |
185 |
printf ("%s%s(%d): ", indent.c_str (), failure.file_, failure.line_); |
186 |
if (!failure.expr_.empty ()) |
187 |
printf ("%s\n", failure.expr_.c_str ()); |
188 |
else if (failure.file_) |
189 |
printf ("\n"); |
190 |
if (!failure.message_.empty ()) |
191 |
{ |
192 |
std::string reindented = indentText (failure.message_, indent + " "); |
193 |
printf ("%s\n", reindented.c_str ()); |
194 |
} |
195 |
} |
196 |
} |
197 |
|
198 |
std::string |
199 |
TestResult::indentText (const std::string &text, const std::string &indent) |
200 |
{ |
201 |
std::string reindented; |
202 |
std::string::size_type lastIndex = 0; |
203 |
|
204 |
while (lastIndex < text.size ()) |
205 |
{ |
206 |
std::string::size_type nextIndex = text.find ('\n', lastIndex); |
207 |
if (nextIndex == std::string::npos) |
208 |
nextIndex = text.size () - 1; |
209 |
reindented += indent; |
210 |
reindented += text.substr (lastIndex, nextIndex - lastIndex + 1); |
211 |
lastIndex = nextIndex + 1; |
212 |
} |
213 |
return reindented; |
214 |
} |
215 |
|
216 |
TestResult & |
217 |
TestResult::addToLastFailure (const std::string &message) |
218 |
{ |
219 |
if (messageTarget_ != 0) |
220 |
messageTarget_->message_ += message; |
221 |
return *this; |
222 |
} |
223 |
|
224 |
TestResult & |
225 |
TestResult::operator << (bool value) |
226 |
{ |
227 |
return addToLastFailure (value ? "true" : "false"); |
228 |
} |
229 |
|
230 |
TestResult & |
231 |
TestResult::operator << (int value) |
232 |
{ |
233 |
char buffer[32]; |
234 |
|
235 |
sprintf (buffer, "%d", value); |
236 |
return addToLastFailure (buffer); |
237 |
} |
238 |
|
239 |
TestResult & |
240 |
TestResult::operator << (unsigned int value) |
241 |
{ |
242 |
char buffer[32]; |
243 |
|
244 |
sprintf (buffer, "%u", value); |
245 |
return addToLastFailure (buffer); |
246 |
} |
247 |
|
248 |
TestResult & |
249 |
TestResult::operator << (double value) |
250 |
{ |
251 |
char buffer[32]; |
252 |
|
253 |
sprintf (buffer, "%16g", value); |
254 |
return addToLastFailure (buffer); |
255 |
} |
256 |
|
257 |
TestResult & |
258 |
TestResult::operator << (const char *value) |
259 |
{ |
260 |
return addToLastFailure (value ? value |
261 |
: "<NULL>"); |
262 |
} |
263 |
|
264 |
TestResult & |
265 |
TestResult::operator << (const std::string &value) |
266 |
{ |
267 |
return addToLastFailure (value); |
268 |
} |
269 |
|
270 |
/* |
271 |
* class TestCase |
272 |
* ////////////////////////////////////////////////////////////////// |
273 |
*/ |
274 |
|
275 |
TestCase::TestCase () |
276 |
: result_ (0) |
277 |
{ |
278 |
} |
279 |
|
280 |
TestCase::~TestCase () |
281 |
{ |
282 |
} |
283 |
|
284 |
void |
285 |
TestCase::run (TestResult &result) |
286 |
{ |
287 |
result_ = &result; |
288 |
runTestCase (); |
289 |
} |
290 |
|
291 |
/* |
292 |
* class Runner |
293 |
* ////////////////////////////////////////////////////////////////// |
294 |
*/ |
295 |
|
296 |
Runner::Runner () |
297 |
{ |
298 |
} |
299 |
|
300 |
Runner & |
301 |
Runner::add (TestCaseFactory factory) |
302 |
{ |
303 |
tests_.push_back (factory); |
304 |
return *this; |
305 |
} |
306 |
|
307 |
unsigned int |
308 |
Runner::testCount () const |
309 |
{ |
310 |
return static_cast<unsigned int> (tests_.size ()); |
311 |
} |
312 |
|
313 |
std::string |
314 |
Runner::testNameAt (unsigned int index) const |
315 |
{ |
316 |
TestCase *test = tests_[index] (); |
317 |
std::string name = test->testName (); |
318 |
|
319 |
delete test; |
320 |
return name; |
321 |
} |
322 |
|
323 |
void |
324 |
Runner::runTestAt (unsigned int index, TestResult &result) const |
325 |
{ |
326 |
TestCase *test = tests_[index] (); |
327 |
|
328 |
result.setTestName (test->testName ()); |
329 |
printf ("Testing %s: ", test->testName ()); |
330 |
fflush (stdout); |
331 |
#if JSON_USE_EXCEPTION |
332 |
try |
333 |
{ |
334 |
#endif /* if JSON_USE_EXCEPTION */ |
335 |
test->run (result); |
336 |
#if JSON_USE_EXCEPTION |
337 |
} |
338 |
|
339 |
catch (const std::exception &e) |
340 |
{ |
341 |
result.addFailure (__FILE__, __LINE__, |
342 |
"Unexpected exception caugth:") << e.what (); |
343 |
} |
344 |
#endif /* if JSON_USE_EXCEPTION */ |
345 |
delete test; |
346 |
const char *status = result.failed () ? "FAILED" |
347 |
: "OK"; |
348 |
printf ("%s\n", status); |
349 |
fflush (stdout); |
350 |
} |
351 |
|
352 |
|
353 |
bool |
354 |
Runner::runAllTest (bool printSummary) const |
355 |
{ |
356 |
unsigned int count = testCount (); |
357 |
|
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 |
failures.push_back (result); |
365 |
} |
366 |
|
367 |
if (failures.empty ()) |
368 |
{ |
369 |
if (printSummary) |
370 |
printf ("All %d tests passed\n", count); |
371 |
return true; |
372 |
} |
373 |
else |
374 |
{ |
375 |
for (unsigned int index = 0; index < failures.size (); ++index) |
376 |
{ |
377 |
TestResult &result = failures[index]; |
378 |
result.printFailure (count > 1); |
379 |
} |
380 |
|
381 |
if (printSummary) |
382 |
{ |
383 |
unsigned int failedCount = static_cast<unsigned int> (failures.size ()); |
384 |
unsigned int passedCount = count - failedCount; |
385 |
printf ("%d/%d tests passed (%d failure(s))\n", passedCount, count, failedCount); |
386 |
} |
387 |
return false; |
388 |
} |
389 |
} |
390 |
|
391 |
bool |
392 |
Runner::testIndex (const std::string &testName, unsigned int &indexOut) const |
393 |
{ |
394 |
unsigned int count = testCount (); |
395 |
|
396 |
for (unsigned int index = 0; index < count; ++index) |
397 |
if (testNameAt (index) == testName) |
398 |
{ |
399 |
indexOut = index; |
400 |
return true; |
401 |
} |
402 |
return false; |
403 |
} |
404 |
|
405 |
void |
406 |
Runner::listTests () const |
407 |
{ |
408 |
unsigned int count = testCount (); |
409 |
|
410 |
for (unsigned int index = 0; index < count; ++index) |
411 |
printf ("%s\n", testNameAt (index).c_str ()); |
412 |
} |
413 |
|
414 |
int |
415 |
Runner::runCommandLine (int argc, const char *argv[]) const |
416 |
{ |
417 |
typedef std::deque<std::string> TestNames; |
418 |
Runner subrunner; |
419 |
for (int index = 1; index < argc; ++index) |
420 |
{ |
421 |
std::string opt = argv[index]; |
422 |
if (opt == "--list-tests") |
423 |
{ |
424 |
listTests (); |
425 |
return 0; |
426 |
} |
427 |
else if (opt == "--test-auto") |
428 |
preventDialogOnCrash (); |
429 |
else if (opt == "--test") |
430 |
{ |
431 |
++index; |
432 |
if (index < argc) |
433 |
{ |
434 |
unsigned int testNameIndex; |
435 |
if (testIndex (argv[index], testNameIndex)) |
436 |
subrunner.add (tests_[testNameIndex]); |
437 |
else |
438 |
{ |
439 |
fprintf (stderr, "Test '%s' does not exist!\n", argv[index]); |
440 |
return 2; |
441 |
} |
442 |
} |
443 |
else |
444 |
{ |
445 |
printUsage (argv[0]); |
446 |
return 2; |
447 |
} |
448 |
} |
449 |
else |
450 |
{ |
451 |
printUsage (argv[0]); |
452 |
return 2; |
453 |
} |
454 |
} |
455 |
bool succeeded; |
456 |
if (subrunner.testCount () > 0) |
457 |
succeeded = subrunner.runAllTest (subrunner.testCount () > 1); |
458 |
else |
459 |
succeeded = runAllTest (true); |
460 |
return succeeded ? 0 |
461 |
: 1; |
462 |
} |
463 |
|
464 |
#if defined(_MSC_VER) |
465 |
/* Hook MSVCRT assertions to prevent dialog from appearing */ |
466 |
static int |
467 |
msvcrtSilentReportHook (int reportType, char *message, int *returnValue) |
468 |
{ |
469 |
/* |
470 |
* The default CRT handling of error and assertion is to display |
471 |
* an error dialog to the user. |
472 |
* Instead, when an error or an assertion occurs, we force the |
473 |
* application to terminate using abort() after display |
474 |
* the message on stderr. |
475 |
*/ |
476 |
if (reportType == _CRT_ERROR || |
477 |
reportType == _CRT_ASSERT) |
478 |
{ |
479 |
/* |
480 |
* calling abort() cause the ReportHook to be called |
481 |
* The following is used to detect this case and let's the |
482 |
* error handler fallback on its default behaviour ( |
483 |
* display a warning message) |
484 |
*/ |
485 |
static volatile bool isAborting = false; |
486 |
if (isAborting) |
487 |
return TRUE; |
488 |
isAborting = true; |
489 |
|
490 |
fprintf (stderr, "CRT Error/Assert:\n%s\n", message); |
491 |
fflush (stderr); |
492 |
abort (); |
493 |
} |
494 |
/* Let's other reportType (_CRT_WARNING) be handled as they would by default */ |
495 |
return FALSE; |
496 |
} |
497 |
#endif /* if defined(_MSC_VER) */ |
498 |
|
499 |
|
500 |
|
501 |
void |
502 |
Runner::preventDialogOnCrash () |
503 |
{ |
504 |
#if defined(_MSC_VER) |
505 |
/* |
506 |
* Install a hook to prevent MSVCRT error and assertion from |
507 |
* popping a dialog. |
508 |
*/ |
509 |
_CrtSetReportHook (&msvcrtSilentReportHook); |
510 |
#endif /* if defined(_MSC_VER) */ |
511 |
|
512 |
/* |
513 |
* @todo investiguate this handler (for buffer overflow) |
514 |
* _set_security_error_handler |
515 |
*/ |
516 |
|
517 |
#if defined(_WIN32) |
518 |
/* |
519 |
* Prevents the system from popping a dialog for debugging if the |
520 |
* application fails due to invalid memory access. |
521 |
*/ |
522 |
SetErrorMode (SEM_FAILCRITICALERRORS |
523 |
| SEM_NOGPFAULTERRORBOX |
524 |
| SEM_NOOPENFILEERRORBOX); |
525 |
#endif /* if defined(_WIN32) */ |
526 |
} |
527 |
|
528 |
void |
529 |
Runner::printUsage (const char *appName) |
530 |
{ |
531 |
printf ( |
532 |
"Usage: %s [options]\n" |
533 |
"\n" |
534 |
"If --test is not specified, then all the test cases be run.\n" |
535 |
"\n" |
536 |
"Valid options:\n" |
537 |
"--list-tests: print the name of all test cases on the standard\n" |
538 |
" output and exit.\n" |
539 |
"--test TESTNAME: executes the test case with the specified name.\n" |
540 |
" May be repeated.\n" |
541 |
"--test-auto: prevent dialog prompting for debugging on crash.\n" |
542 |
, appName); |
543 |
} |
544 |
|
545 |
/* |
546 |
* Assertion functions |
547 |
* ////////////////////////////////////////////////////////////////// |
548 |
*/ |
549 |
|
550 |
TestResult & |
551 |
checkStringEqual (TestResult &result, const std::string &expected, const std::string &actual, const char *file, unsigned int line, const char *expr) |
552 |
{ |
553 |
if (expected != actual) |
554 |
{ |
555 |
result.addFailure (file, line, expr); |
556 |
result << "Expected: '" << expected << "'\n"; |
557 |
result << "Actual : '" << actual << "'"; |
558 |
} |
559 |
return result; |
560 |
} |
561 |
} /* namespace JsonTest */ |