ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/dclient/testsuite/json/jsontest.cpp
Revision: 1.1
Committed: Sun Oct 17 08:15:15 2010 UTC (13 years, 8 months ago) by sf-pippijn
Branch: MAIN
CVS Tags: HEAD
Log Message:
initial import

File Contents

# Content
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 */