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