• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
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 <cstdio>
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_(JSONCPP_NULL) {
78   // The root predicate has id 0
79   rootPredicateNode_.id_ = 0;
80   rootPredicateNode_.next_ = JSONCPP_NULL;
81   predicateStackTail_ = &rootPredicateNode_;
82 }
83 
setTestName(const Json::String & name)84 void TestResult::setTestName(const Json::String& name) { name_ = name; }
85 
addFailure(const char * file,unsigned int line,const char * expr)86 TestResult& TestResult::addFailure(const char* file, unsigned int line,
87                                    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 != JSONCPP_NULL; lastNode = lastNode->next_) {
93     if (lastNode->id_ > lastUsedPredicateId_) // new PredicateContext
94     {
95       lastUsedPredicateId_ = lastNode->id_;
96       addFailureInfo(lastNode->file_, lastNode->line_, lastNode->expr_,
97                      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, unsigned int line,
112                                 const char* expr, unsigned int nestingLevel) {
113   Failure failure;
114   failure.file_ = file;
115   failure.line_ = line;
116   if (expr) {
117     failure.expr_ = expr;
118   }
119   failure.nestingLevel_ = nestingLevel;
120   failures_.push_back(failure);
121 }
122 
popPredicateContext()123 TestResult& TestResult::popPredicateContext() {
124   PredicateContext* lastNode = &rootPredicateNode_;
125   while (lastNode->next_ != JSONCPP_NULL &&
126          lastNode->next_->next_ != JSONCPP_NULL) {
127     lastNode = lastNode->next_;
128   }
129   // Set message target to popped failure
130   PredicateContext* tail = lastNode->next_;
131   if (tail != JSONCPP_NULL && tail->failure_ != JSONCPP_NULL) {
132     messageTarget_ = tail->failure_;
133   }
134   // Remove tail from list
135   predicateStackTail_ = lastNode;
136   lastNode->next_ = JSONCPP_NULL;
137   return *this;
138 }
139 
failed() const140 bool TestResult::failed() const { return !failures_.empty(); }
141 
printFailure(bool printTestName) const142 void TestResult::printFailure(bool printTestName) const {
143   if (failures_.empty()) {
144     return;
145   }
146 
147   if (printTestName) {
148     printf("* Detail of %s test failure:\n", name_.c_str());
149   }
150 
151   // Print in reverse to display the callstack in the right order
152   for (Failures::const_iterator it = failures_.begin(); it != failures_.end();
153        ++it) {
154     const Failure& failure = *it;
155     Json::String indent(failure.nestingLevel_ * 2, ' ');
156     if (failure.file_) {
157       printf("%s%s(%u): ", indent.c_str(), failure.file_, failure.line_);
158     }
159     if (!failure.expr_.empty()) {
160       printf("%s\n", failure.expr_.c_str());
161     } else if (failure.file_) {
162       printf("\n");
163     }
164     if (!failure.message_.empty()) {
165       Json::String reindented = indentText(failure.message_, indent + "  ");
166       printf("%s\n", reindented.c_str());
167     }
168   }
169 }
170 
indentText(const Json::String & text,const Json::String & indent)171 Json::String TestResult::indentText(const Json::String& text,
172                                     const Json::String& indent) {
173   Json::String reindented;
174   Json::String::size_type lastIndex = 0;
175   while (lastIndex < text.size()) {
176     Json::String::size_type nextIndex = text.find('\n', lastIndex);
177     if (nextIndex == Json::String::npos) {
178       nextIndex = text.size() - 1;
179     }
180     reindented += indent;
181     reindented += text.substr(lastIndex, nextIndex - lastIndex + 1);
182     lastIndex = nextIndex + 1;
183   }
184   return reindented;
185 }
186 
addToLastFailure(const Json::String & message)187 TestResult& TestResult::addToLastFailure(const Json::String& message) {
188   if (messageTarget_ != JSONCPP_NULL) {
189     messageTarget_->message_ += message;
190   }
191   return *this;
192 }
193 
operator <<(Json::Int64 value)194 TestResult& TestResult::operator<<(Json::Int64 value) {
195   return addToLastFailure(Json::valueToString(value));
196 }
197 
operator <<(Json::UInt64 value)198 TestResult& TestResult::operator<<(Json::UInt64 value) {
199   return addToLastFailure(Json::valueToString(value));
200 }
201 
operator <<(bool value)202 TestResult& TestResult::operator<<(bool value) {
203   return addToLastFailure(value ? "true" : "false");
204 }
205 
206 // class TestCase
207 // //////////////////////////////////////////////////////////////////
208 
TestCase()209 TestCase::TestCase() : result_(JSONCPP_NULL) {}
210 
~TestCase()211 TestCase::~TestCase() {}
212 
run(TestResult & result)213 void TestCase::run(TestResult& result) {
214   result_ = &result;
215   runTestCase();
216 }
217 
218 // class Runner
219 // //////////////////////////////////////////////////////////////////
220 
Runner()221 Runner::Runner() {}
222 
add(TestCaseFactory factory)223 Runner& Runner::add(TestCaseFactory factory) {
224   tests_.push_back(factory);
225   return *this;
226 }
227 
testCount() const228 size_t Runner::testCount() const { return tests_.size(); }
229 
testNameAt(size_t index) const230 Json::String Runner::testNameAt(size_t index) const {
231   TestCase* test = tests_[index]();
232   Json::String name = test->testName();
233   delete test;
234   return name;
235 }
236 
runTestAt(size_t index,TestResult & result) const237 void Runner::runTestAt(size_t index, TestResult& result) const {
238   TestCase* test = tests_[index]();
239   result.setTestName(test->testName());
240   printf("Testing %s: ", test->testName());
241   fflush(stdout);
242 #if JSON_USE_EXCEPTION
243   try {
244 #endif // if JSON_USE_EXCEPTION
245     test->run(result);
246 #if JSON_USE_EXCEPTION
247   } catch (const std::exception& e) {
248     result.addFailure(__FILE__, __LINE__, "Unexpected exception caught:")
249         << e.what();
250   }
251 #endif // if JSON_USE_EXCEPTION
252   delete test;
253   const char* status = result.failed() ? "FAILED" : "OK";
254   printf("%s\n", status);
255   fflush(stdout);
256 }
257 
runAllTest(bool printSummary) const258 bool Runner::runAllTest(bool printSummary) const {
259   size_t const count = testCount();
260   std::deque<TestResult> failures;
261   for (size_t index = 0; index < count; ++index) {
262     TestResult result;
263     runTestAt(index, result);
264     if (result.failed()) {
265       failures.push_back(result);
266     }
267   }
268 
269   if (failures.empty()) {
270     if (printSummary) {
271       printf("All %zu tests passed\n", count);
272     }
273     return true;
274   }
275   for (size_t index = 0; index < failures.size(); ++index) {
276     TestResult& result = failures[index];
277     result.printFailure(count > 1);
278   }
279 
280   if (printSummary) {
281     size_t const failedCount = failures.size();
282     size_t const passedCount = count - failedCount;
283     printf("%zu/%zu tests passed (%zu failure(s))\n", passedCount, count,
284            failedCount);
285   }
286   return false;
287 }
288 
testIndex(const Json::String & testName,size_t & indexOut) const289 bool Runner::testIndex(const Json::String& testName, size_t& indexOut) const {
290   const size_t count = testCount();
291   for (size_t index = 0; index < count; ++index) {
292     if (testNameAt(index) == testName) {
293       indexOut = index;
294       return true;
295     }
296   }
297   return false;
298 }
299 
listTests() const300 void Runner::listTests() const {
301   const size_t count = testCount();
302   for (size_t index = 0; index < count; ++index) {
303     printf("%s\n", testNameAt(index).c_str());
304   }
305 }
306 
runCommandLine(int argc,const char * argv[]) const307 int Runner::runCommandLine(int argc, const char* argv[]) const {
308   // typedef std::deque<String> TestNames;
309   Runner subrunner;
310   for (int index = 1; index < argc; ++index) {
311     Json::String opt = argv[index];
312     if (opt == "--list-tests") {
313       listTests();
314       return 0;
315     }
316     if (opt == "--test-auto") {
317       preventDialogOnCrash();
318     } else if (opt == "--test") {
319       ++index;
320       if (index < argc) {
321         size_t testNameIndex;
322         if (testIndex(argv[index], testNameIndex)) {
323           subrunner.add(tests_[testNameIndex]);
324         } else {
325           fprintf(stderr, "Test '%s' does not exist!\n", argv[index]);
326           return 2;
327         }
328       } else {
329         printUsage(argv[0]);
330         return 2;
331       }
332     } else {
333       printUsage(argv[0]);
334       return 2;
335     }
336   }
337   bool succeeded;
338   if (subrunner.testCount() > 0) {
339     succeeded = subrunner.runAllTest(subrunner.testCount() > 1);
340   } else {
341     succeeded = runAllTest(true);
342   }
343   return succeeded ? 0 : 1;
344 }
345 
346 #if defined(_MSC_VER) && defined(_DEBUG)
347 // Hook MSVCRT assertions to prevent dialog from appearing
msvcrtSilentReportHook(int reportType,char * message,int *)348 static int msvcrtSilentReportHook(int reportType, char* message,
349                                   int* /*returnValue*/) {
350   // The default CRT handling of error and assertion is to display
351   // an error dialog to the user.
352   // Instead, when an error or an assertion occurs, we force the
353   // application to terminate using abort() after display
354   // the message on stderr.
355   if (reportType == _CRT_ERROR || reportType == _CRT_ASSERT) {
356     // calling abort() cause the ReportHook to be called
357     // The following is used to detect this case and let's the
358     // error handler fallback on its default behaviour (
359     // display a warning message)
360     static volatile bool isAborting = false;
361     if (isAborting) {
362       return TRUE;
363     }
364     isAborting = true;
365 
366     fprintf(stderr, "CRT Error/Assert:\n%s\n", message);
367     fflush(stderr);
368     abort();
369   }
370   // Let's other reportType (_CRT_WARNING) be handled as they would by default
371   return FALSE;
372 }
373 #endif // if defined(_MSC_VER)
374 
preventDialogOnCrash()375 void Runner::preventDialogOnCrash() {
376 #if defined(_MSC_VER) && defined(_DEBUG)
377   // Install a hook to prevent MSVCRT error and assertion from
378   // popping a dialog
379   // This function a NO-OP in release configuration
380   // (which cause warning since msvcrtSilentReportHook is not referenced)
381   _CrtSetReportHook(&msvcrtSilentReportHook);
382 #endif // if defined(_MSC_VER)
383 
384   // @todo investigate this handler (for buffer overflow)
385   // _set_security_error_handler
386 
387 #if defined(_WIN32)
388   // Prevents the system from popping a dialog for debugging if the
389   // application fails due to invalid memory access.
390   SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
391                SEM_NOOPENFILEERRORBOX);
392 #endif // if defined(_WIN32)
393 }
394 
printUsage(const char * appName)395 void Runner::printUsage(const char* appName) {
396   printf("Usage: %s [options]\n"
397          "\n"
398          "If --test is not specified, then all the test cases be run.\n"
399          "\n"
400          "Valid options:\n"
401          "--list-tests: print the name of all test cases on the standard\n"
402          "              output and exit.\n"
403          "--test TESTNAME: executes the test case with the specified name.\n"
404          "                 May be repeated.\n"
405          "--test-auto: prevent dialog prompting for debugging on crash.\n",
406          appName);
407 }
408 
409 // Assertion functions
410 // //////////////////////////////////////////////////////////////////
411 
ToJsonString(const char * toConvert)412 Json::String ToJsonString(const char* toConvert) {
413   return Json::String(toConvert);
414 }
415 
ToJsonString(Json::String in)416 Json::String ToJsonString(Json::String in) { return in; }
417 
418 #if JSONCPP_USING_SECURE_MEMORY
ToJsonString(std::string in)419 Json::String ToJsonString(std::string in) {
420   return Json::String(in.data(), in.data() + in.length());
421 }
422 #endif
423 
checkStringEqual(TestResult & result,const Json::String & expected,const Json::String & actual,const char * file,unsigned int line,const char * expr)424 TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
425                              const Json::String& actual, const char* file,
426                              unsigned int line, const char* expr) {
427   if (expected != actual) {
428     result.addFailure(file, line, expr);
429     result << "Expected: '" << expected << "'\n";
430     result << "Actual  : '" << actual << "'";
431   }
432   return result;
433 }
434 
435 } // namespace JsonTest
436