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