• 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 #ifndef JSONTEST_H_INCLUDED
7 #define JSONTEST_H_INCLUDED
8 
9 #include <cstdio>
10 #include <deque>
11 #include <iomanip>
12 #include <json/config.h>
13 #include <json/value.h>
14 #include <json/writer.h>
15 #include <sstream>
16 #include <string>
17 
18 // //////////////////////////////////////////////////////////////////
19 // //////////////////////////////////////////////////////////////////
20 // Mini Unit Testing framework
21 // //////////////////////////////////////////////////////////////////
22 // //////////////////////////////////////////////////////////////////
23 
24 /** \brief Unit testing framework.
25  * \warning: all assertions are non-aborting, test case execution will continue
26  *           even if an assertion namespace.
27  *           This constraint is for portability: the framework needs to compile
28  *           on Visual Studio 6 and must not require exception usage.
29  */
30 namespace JsonTest {
31 
32 class Failure {
33 public:
34   const char* file_;
35   unsigned int line_;
36   Json::String expr_;
37   Json::String message_;
38   unsigned int nestingLevel_;
39 };
40 
41 /// Context used to create the assertion callstack on failure.
42 /// Must be a POD to allow inline initialisation without stepping
43 /// into the debugger.
44 struct PredicateContext {
45   typedef unsigned int Id;
46   Id id_;
47   const char* file_;
48   unsigned int line_;
49   const char* expr_;
50   PredicateContext* next_;
51   /// Related Failure, set when the PredicateContext is converted
52   /// into a Failure.
53   Failure* failure_;
54 };
55 
56 class TestResult {
57 public:
58   TestResult();
59 
60   /// \internal Implementation detail for assertion macros
61   /// Not encapsulated to prevent step into when debugging failed assertions
62   /// Incremented by one on assertion predicate entry, decreased by one
63   /// by addPredicateContext().
64   PredicateContext::Id predicateId_;
65 
66   /// \internal Implementation detail for predicate macros
67   PredicateContext* predicateStackTail_;
68 
69   void setTestName(const Json::String& name);
70 
71   /// Adds an assertion failure.
72   TestResult& addFailure(const char* file, unsigned int line,
73                          const char* expr = JSONCPP_NULL);
74 
75   /// Removes the last PredicateContext added to the predicate stack
76   /// chained list.
77   /// Next messages will be targed at the PredicateContext that was removed.
78   TestResult& popPredicateContext();
79 
80   bool failed() const;
81 
82   void printFailure(bool printTestName) const;
83 
84   // Generic operator that will work with anything ostream can deal with.
85   template <typename T> TestResult& operator<<(const T& value) {
86     Json::OStringStream oss;
87     oss.precision(16);
88     oss.setf(std::ios_base::floatfield);
89     oss << value;
90     return addToLastFailure(oss.str());
91   }
92 
93   // Specialized versions.
94   TestResult& operator<<(bool value);
95   // std:ostream does not support 64bits integers on all STL implementation
96   TestResult& operator<<(Json::Int64 value);
97   TestResult& operator<<(Json::UInt64 value);
98 
99 private:
100   TestResult& addToLastFailure(const Json::String& message);
101   /// Adds a failure or a predicate context
102   void addFailureInfo(const char* file, unsigned int line, const char* expr,
103                       unsigned int nestingLevel);
104   static Json::String indentText(const Json::String& text,
105                                  const Json::String& indent);
106 
107   typedef std::deque<Failure> Failures;
108   Failures failures_;
109   Json::String name_;
110   PredicateContext rootPredicateNode_;
111   PredicateContext::Id lastUsedPredicateId_;
112   /// Failure which is the target of the messages added using operator <<
113   Failure* messageTarget_;
114 };
115 
116 class TestCase {
117 public:
118   TestCase();
119 
120   virtual ~TestCase();
121 
122   void run(TestResult& result);
123 
124   virtual const char* testName() const = 0;
125 
126 protected:
127   TestResult* result_;
128 
129 private:
130   virtual void runTestCase() = 0;
131 };
132 
133 /// Function pointer type for TestCase factory
134 typedef TestCase* (*TestCaseFactory)();
135 
136 class Runner {
137 public:
138   Runner();
139 
140   /// Adds a test to the suite
141   Runner& add(TestCaseFactory factory);
142 
143   /// Runs test as specified on the command-line
144   /// If no command-line arguments are provided, run all tests.
145   /// If --list-tests is provided, then print the list of all test cases
146   /// If --test <testname> is provided, then run test testname.
147   int runCommandLine(int argc, const char* argv[]) const;
148 
149   /// Runs all the test cases
150   bool runAllTest(bool printSummary) const;
151 
152   /// Returns the number of test case in the suite
153   size_t testCount() const;
154 
155   /// Returns the name of the test case at the specified index
156   Json::String testNameAt(size_t index) const;
157 
158   /// Runs the test case at the specified index using the specified TestResult
159   void runTestAt(size_t index, TestResult& result) const;
160 
161   static void printUsage(const char* appName);
162 
163 private: // prevents copy construction and assignment
164   Runner(const Runner& other) JSONCPP_CTOR_DELETE;
165   Runner& operator=(const Runner& other) JSONCPP_CTOR_DELETE;
166 
167 private:
168   void listTests() const;
169   bool testIndex(const Json::String& testName, size_t& indexOut) const;
170   static void preventDialogOnCrash();
171 
172 private:
173   typedef std::deque<TestCaseFactory> Factories;
174   Factories tests_;
175 };
176 
177 template <typename T, typename U>
checkEqual(TestResult & result,T expected,U actual,const char * file,unsigned int line,const char * expr)178 TestResult& checkEqual(TestResult& result, T expected, U actual,
179                        const char* file, unsigned int line, const char* expr) {
180   if (static_cast<U>(expected) != actual) {
181     result.addFailure(file, line, expr);
182     result << "Expected: " << static_cast<U>(expected) << "\n";
183     result << "Actual  : " << actual;
184   }
185   return result;
186 }
187 
188 Json::String ToJsonString(const char* toConvert);
189 Json::String ToJsonString(Json::String in);
190 #if JSONCPP_USING_SECURE_MEMORY
191 Json::String ToJsonString(std::string in);
192 #endif
193 
194 TestResult& checkStringEqual(TestResult& result, const Json::String& expected,
195                              const Json::String& actual, const char* file,
196                              unsigned int line, const char* expr);
197 
198 } // namespace JsonTest
199 
200 /// \brief Asserts that the given expression is true.
201 /// JSONTEST_ASSERT( x == y ) << "x=" << x << ", y=" << y;
202 /// JSONTEST_ASSERT( x == y );
203 #define JSONTEST_ASSERT(expr)                                                  \
204   if (expr) {                                                                  \
205   } else                                                                       \
206     result_->addFailure(__FILE__, __LINE__, #expr)
207 
208 /// \brief Asserts that the given predicate is true.
209 /// The predicate may do other assertions and be a member function of the
210 /// fixture.
211 #define JSONTEST_ASSERT_PRED(expr)                                             \
212   do {                                                                         \
213     JsonTest::PredicateContext _minitest_Context = {                           \
214         result_->predicateId_, __FILE__, __LINE__, #expr, NULL, NULL};         \
215     result_->predicateStackTail_->next_ = &_minitest_Context;                  \
216     result_->predicateId_ += 1;                                                \
217     result_->predicateStackTail_ = &_minitest_Context;                         \
218     (expr);                                                                    \
219     result_->popPredicateContext();                                            \
220   } while (0)
221 
222 /// \brief Asserts that two values are equals.
223 #define JSONTEST_ASSERT_EQUAL(expected, actual)                                \
224   JsonTest::checkEqual(*result_, expected, actual, __FILE__, __LINE__,         \
225                        #expected " == " #actual)
226 
227 /// \brief Asserts that two values are equals.
228 #define JSONTEST_ASSERT_STRING_EQUAL(expected, actual)                         \
229   JsonTest::checkStringEqual(*result_, JsonTest::ToJsonString(expected),       \
230                              JsonTest::ToJsonString(actual), __FILE__,         \
231                              __LINE__, #expected " == " #actual)
232 
233 /// \brief Asserts that a given expression throws an exception
234 #define JSONTEST_ASSERT_THROWS(expr)                                           \
235   do {                                                                         \
236     bool _threw = false;                                                       \
237     try {                                                                      \
238       expr;                                                                    \
239     } catch (...) {                                                            \
240       _threw = true;                                                           \
241     }                                                                          \
242     if (!_threw)                                                               \
243       result_->addFailure(__FILE__, __LINE__,                                  \
244                           "expected exception thrown: " #expr);                \
245   } while (0)
246 
247 /// \brief Begin a fixture test case.
248 #define JSONTEST_FIXTURE(FixtureType, name)                                    \
249   class Test##FixtureType##name : public FixtureType {                         \
250   public:                                                                      \
251     static JsonTest::TestCase* factory() {                                     \
252       return new Test##FixtureType##name();                                    \
253     }                                                                          \
254                                                                                \
255   public: /* overridden from TestCase */                                       \
256     const char* testName() const JSONCPP_OVERRIDE {                            \
257       return #FixtureType "/" #name;                                           \
258     }                                                                          \
259     void runTestCase() JSONCPP_OVERRIDE;                                       \
260   };                                                                           \
261                                                                                \
262   void Test##FixtureType##name::runTestCase()
263 
264 #define JSONTEST_FIXTURE_FACTORY(FixtureType, name)                            \
265   &Test##FixtureType##name::factory
266 
267 #define JSONTEST_REGISTER_FIXTURE(runner, FixtureType, name)                   \
268   (runner).add(JSONTEST_FIXTURE_FACTORY(FixtureType, name))
269 
270 /// \brief Begin a fixture test case.
271 #define JSONTEST_FIXTURE_V2(FixtureType, name, collections)                    \
272   class Test##FixtureType##name : public FixtureType {                         \
273   public:                                                                      \
274     static JsonTest::TestCase* factory() {                                     \
275       return new Test##FixtureType##name();                                    \
276     }                                                                          \
277     static bool collect() {                                                    \
278       (collections).push_back(JSONTEST_FIXTURE_FACTORY(FixtureType, name));    \
279       return true;                                                             \
280     }                                                                          \
281                                                                                \
282   public: /* overridden from TestCase */                                       \
283     const char* testName() const JSONCPP_OVERRIDE {                            \
284       return #FixtureType "/" #name;                                           \
285     }                                                                          \
286     void runTestCase() JSONCPP_OVERRIDE;                                       \
287   };                                                                           \
288                                                                                \
289   static bool test##FixtureType##name##collect =                               \
290       Test##FixtureType##name::collect();                                      \
291                                                                                \
292   void Test##FixtureType##name::runTestCase()
293 
294 #endif // ifndef JSONTEST_H_INCLUDED
295