• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
71 namespace JsonTest {
72 
73 // class TestResult
74 // //////////////////////////////////////////////////////////////////
75 
TestResult()76 TestResult::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 
setTestName(const std::string & name)84 void TestResult::setTestName(const std::string& name) { name_ = name; }
85 
86 TestResult&
addFailure(const char * file,unsigned int line,const char * expr)87 TestResult::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 
addFailureInfo(const char * file,unsigned int line,const char * expr,unsigned int nestingLevel)111 void 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 
popPredicateContext()125 TestResult& 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 
failed() const141 bool TestResult::failed() const { return !failures_.empty(); }
142 
getAssertionNestingLevel() const143 unsigned 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 
printFailure(bool printTestName) const153 void 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 
indentText(const std::string & text,const std::string & indent)182 std::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 
addToLastFailure(const std::string & message)198 TestResult& TestResult::addToLastFailure(const std::string& message) {
199   if (messageTarget_ != 0) {
200     messageTarget_->message_ += message;
201   }
202   return *this;
203 }
204 
operator <<(Json::Int64 value)205 TestResult& TestResult::operator<<(Json::Int64 value) {
206   return addToLastFailure(Json::valueToString(value));
207 }
208 
operator <<(Json::UInt64 value)209 TestResult& TestResult::operator<<(Json::UInt64 value) {
210   return addToLastFailure(Json::valueToString(value));
211 }
212 
operator <<(bool value)213 TestResult& TestResult::operator<<(bool value) {
214   return addToLastFailure(value ? "true" : "false");
215 }
216 
217 // class TestCase
218 // //////////////////////////////////////////////////////////////////
219 
TestCase()220 TestCase::TestCase() : result_(0) {}
221 
~TestCase()222 TestCase::~TestCase() {}
223 
run(TestResult & result)224 void TestCase::run(TestResult& result) {
225   result_ = &result;
226   runTestCase();
227 }
228 
229 // class Runner
230 // //////////////////////////////////////////////////////////////////
231 
Runner()232 Runner::Runner() {}
233 
add(TestCaseFactory factory)234 Runner& Runner::add(TestCaseFactory factory) {
235   tests_.push_back(factory);
236   return *this;
237 }
238 
testCount() const239 unsigned int Runner::testCount() const {
240   return static_cast<unsigned int>(tests_.size());
241 }
242 
testNameAt(unsigned int index) const243 std::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 
runTestAt(unsigned int index,TestResult & result) const250 void 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 
runAllTest(bool printSummary) const272 bool 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 
testIndex(const std::string & testName,unsigned int & indexOut) const306 bool 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 
listTests() const318 void 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 
runCommandLine(int argc,const char * argv[]) const325 int 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
365 static int
msvcrtSilentReportHook(int reportType,char * message,int *)366 msvcrtSilentReportHook(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 
preventDialogOnCrash()392 void 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 
printUsage(const char * appName)412 void 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 
checkStringEqual(TestResult & result,const std::string & expected,const std::string & actual,const char * file,unsigned int line,const char * expr)429 TestResult& 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