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, 9 months ago) by sf-pippijn
Branch: MAIN
CVS Tags: HEAD
Log Message:
initial import

File Contents

# User Rev Content
1 sf-pippijn 1.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 */