1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/beast
8 //
9 
10 #ifndef BOOST_BEAST_UNIT_TEST_RUNNER_H_INCLUDED
11 #define BOOST_BEAST_UNIT_TEST_RUNNER_H_INCLUDED
12 
13 #include <boost/beast/_experimental/unit_test/suite_info.hpp>
14 #include <boost/assert.hpp>
15 #include <mutex>
16 #include <ostream>
17 #include <string>
18 
19 namespace boost {
20 namespace beast {
21 namespace unit_test {
22 
23 /** Unit test runner interface.
24 
25     Derived classes can customize the reporting behavior. This interface is
26     injected into the unit_test class to receive the results of the tests.
27 */
28 class runner
29 {
30     std::string arg_;
31     bool default_ = false;
32     bool failed_ = false;
33     bool cond_ = false;
34     std::recursive_mutex mutex_;
35 
36 public:
37     runner() = default;
38     virtual ~runner() = default;
39     runner(runner const&) = delete;
40     runner& operator=(runner const&) = delete;
41 
42     /** Set the argument string.
43 
44         The argument string is available to suites and
45         allows for customization of the test. Each suite
46         defines its own syntax for the argument string.
47         The same argument is passed to all suites.
48     */
49     void
arg(std::string const & s)50     arg(std::string const& s)
51     {
52         arg_ = s;
53     }
54 
55     /** Returns the argument string. */
56     std::string const&
arg() const57     arg() const
58     {
59         return arg_;
60     }
61 
62     /** Run the specified suite.
63         @return `true` if any conditions failed.
64     */
65     template<class = void>
66     bool
67     run(suite_info const& s);
68 
69     /** Run a sequence of suites.
70         The expression
71             `FwdIter::value_type`
72         must be convertible to `suite_info`.
73         @return `true` if any conditions failed.
74     */
75     template<class FwdIter>
76     bool
77     run(FwdIter first, FwdIter last);
78 
79     /** Conditionally run a sequence of suites.
80         pred will be called as:
81         @code
82             bool pred(suite_info const&);
83         @endcode
84         @return `true` if any conditions failed.
85     */
86     template<class FwdIter, class Pred>
87     bool
88     run_if(FwdIter first, FwdIter last, Pred pred = Pred{});
89 
90     /** Run all suites in a container.
91         @return `true` if any conditions failed.
92     */
93     template<class SequenceContainer>
94     bool
95     run_each(SequenceContainer const& c);
96 
97     /** Conditionally run suites in a container.
98         pred will be called as:
99         @code
100             bool pred(suite_info const&);
101         @endcode
102         @return `true` if any conditions failed.
103     */
104     template<class SequenceContainer, class Pred>
105     bool
106     run_each_if(SequenceContainer const& c, Pred pred = Pred{});
107 
108 protected:
109     /// Called when a new suite starts.
110     virtual
111     void
on_suite_begin(suite_info const &)112     on_suite_begin(suite_info const&)
113     {
114     }
115 
116     /// Called when a suite ends.
117     virtual
118     void
on_suite_end()119     on_suite_end()
120     {
121     }
122 
123     /// Called when a new case starts.
124     virtual
125     void
on_case_begin(std::string const &)126     on_case_begin(std::string const&)
127     {
128     }
129 
130     /// Called when a new case ends.
131     virtual
132     void
on_case_end()133     on_case_end()
134     {
135     }
136 
137     /// Called for each passing condition.
138     virtual
139     void
on_pass()140     on_pass()
141     {
142     }
143 
144     /// Called for each failing condition.
145     virtual
146     void
on_fail(std::string const &)147     on_fail(std::string const&)
148     {
149     }
150 
151     /// Called when a test logs output.
152     virtual
153     void
on_log(std::string const &)154     on_log(std::string const&)
155     {
156     }
157 
158 private:
159     friend class suite;
160 
161     // Start a new testcase.
162     template<class = void>
163     void
164     testcase(std::string const& name);
165 
166     template<class = void>
167     void
168     pass();
169 
170     template<class = void>
171     void
172     fail(std::string const& reason);
173 
174     template<class = void>
175     void
176     log(std::string const& s);
177 };
178 
179 //------------------------------------------------------------------------------
180 
181 template<class>
182 bool
run(suite_info const & s)183 runner::run(suite_info const& s)
184 {
185     // Enable 'default' testcase
186     default_ = true;
187     failed_ = false;
188     on_suite_begin(s);
189     s.run(*this);
190     // Forgot to call pass or fail.
191     BOOST_ASSERT(cond_);
192     on_case_end();
193     on_suite_end();
194     return failed_;
195 }
196 
197 template<class FwdIter>
198 bool
run(FwdIter first,FwdIter last)199 runner::run(FwdIter first, FwdIter last)
200 {
201     bool failed(false);
202     for(;first != last; ++first)
203         failed = run(*first) || failed;
204     return failed;
205 }
206 
207 template<class FwdIter, class Pred>
208 bool
run_if(FwdIter first,FwdIter last,Pred pred)209 runner::run_if(FwdIter first, FwdIter last, Pred pred)
210 {
211     bool failed(false);
212     for(;first != last; ++first)
213         if(pred(*first))
214             failed = run(*first) || failed;
215     return failed;
216 }
217 
218 template<class SequenceContainer>
219 bool
run_each(SequenceContainer const & c)220 runner::run_each(SequenceContainer const& c)
221 {
222     bool failed(false);
223     for(auto const& s : c)
224         failed = run(s) || failed;
225     return failed;
226 }
227 
228 template<class SequenceContainer, class Pred>
229 bool
run_each_if(SequenceContainer const & c,Pred pred)230 runner::run_each_if(SequenceContainer const& c, Pred pred)
231 {
232     bool failed(false);
233     for(auto const& s : c)
234         if(pred(s))
235             failed = run(s) || failed;
236     return failed;
237 }
238 
239 template<class>
240 void
testcase(std::string const & name)241 runner::testcase(std::string const& name)
242 {
243     std::lock_guard<std::recursive_mutex> lock(mutex_);
244     // Name may not be empty
245     BOOST_ASSERT(default_ || ! name.empty());
246     // Forgot to call pass or fail
247     BOOST_ASSERT(default_ || cond_);
248     if(! default_)
249         on_case_end();
250     default_ = false;
251     cond_ = false;
252     on_case_begin(name);
253 }
254 
255 template<class>
256 void
pass()257 runner::pass()
258 {
259     std::lock_guard<std::recursive_mutex> lock(mutex_);
260     if(default_)
261         testcase("");
262     on_pass();
263     cond_ = true;
264 }
265 
266 template<class>
267 void
fail(std::string const & reason)268 runner::fail(std::string const& reason)
269 {
270     std::lock_guard<std::recursive_mutex> lock(mutex_);
271     if(default_)
272         testcase("");
273     on_fail(reason);
274     failed_ = true;
275     cond_ = true;
276 }
277 
278 template<class>
279 void
log(std::string const & s)280 runner::log(std::string const& s)
281 {
282     std::lock_guard<std::recursive_mutex> lock(mutex_);
283     if(default_)
284         testcase("");
285     on_log(s);
286 }
287 
288 } // unit_test
289 } // beast
290 } // boost
291 
292 #endif
293