• 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 <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