]> git.stg.codes - stg.git/blob - tests/tut/tut_runner.hpp
33ffe4231124b6f08ad208f6596f2e85ae71988b
[stg.git] / tests / tut / tut_runner.hpp
1 #ifndef TUT_RUNNER_H_GUARD
2 #define TUT_RUNNER_H_GUARD
3
4 #include <string>
5 #include <vector>
6 #include <set>
7 #include "tut_exception.hpp"
8
9 namespace tut
10 {
11
12 /**
13  * Interface.
14  * Test group operations.
15  */
16 struct group_base
17 {
18     virtual ~group_base()
19     {
20     }
21
22     // execute tests iteratively
23     virtual void rewind() = 0;
24     virtual bool run_next(test_result &) = 0;
25
26     // execute one test
27     virtual bool run_test(int n, test_result &tr) = 0;
28 };
29
30
31 /**
32  * Test runner callback interface.
33  * Can be implemented by caller to update
34  * tests results in real-time. User can implement
35  * any of callback methods, and leave unused
36  * in default implementation.
37  */
38 struct callback
39 {
40     /**
41      * Default constructor.
42      */
43     callback()
44     {
45     }
46
47     /**
48      * Virtual destructor is a must for subclassed types.
49      */
50     virtual ~callback()
51     {
52     }
53
54     /**
55      * Called when new test run started.
56      */
57     virtual void run_started()
58     {
59     }
60
61     /**
62      * Called when a group started
63      * @param name Name of the group
64      */
65     virtual void group_started(const std::string& /*name*/)
66     {
67     }
68
69     /**
70      * Called when a test finished.
71      * @param tr Test results.
72      */
73     virtual void test_completed(const test_result& /*tr*/)
74     {
75     }
76
77     /**
78      * Called when a group is completed
79      * @param name Name of the group
80      */
81     virtual void group_completed(const std::string& /*name*/)
82     {
83     }
84
85     /**
86      * Called when all tests in run completed.
87      */
88     virtual void run_completed()
89     {
90     }
91 private:
92     callback(const callback &);
93     void operator=(const callback&);
94 };
95
96 /**
97  * Typedef for runner::list_groups()
98  */
99 typedef std::vector<std::string> groupnames;
100 typedef std::set<callback*> callbacks;
101
102 /**
103  * Test runner.
104  */
105 class test_runner
106 {
107
108 public:
109
110     /**
111      * Constructor
112      */
113     test_runner()
114     {
115     }
116
117     /**
118      * Stores another group for getting by name.
119      */
120     void register_group(const std::string& name, group_base* gr)
121     {
122         if (gr == 0)
123         {
124             throw tut_error("group shall be non-null");
125         }
126
127         if (groups_.find(name) != groups_.end())
128         {
129             std::string msg("attempt to add already existent group " + name);
130             // this exception terminates application so we use cerr also
131             // TODO: should this message appear in stream?
132             std::cerr << msg << std::endl;
133             throw tut_error(msg);
134         }
135
136         groups_.insert( std::make_pair(name, gr) );
137     }
138
139     void set_callback(callback *cb)
140     {
141         clear_callbacks();
142         insert_callback(cb);
143     }
144
145     /**
146      * Stores callback object.
147      */
148     void insert_callback(callback* cb)
149     {
150         if(cb != NULL)
151         {
152             callbacks_.insert(cb);
153         }
154     }
155
156     void erase_callback(callback* cb)
157     {
158         callbacks_.erase(cb);
159     }
160
161     void clear_callbacks()
162     {
163         callbacks_.clear();
164     }
165
166     /**
167      * Returns callback list.
168      */
169     const callbacks &get_callbacks() const
170     {
171         return callbacks_;
172     }
173
174     void set_callbacks(const callbacks &cb)
175     {
176         callbacks_ = cb;
177     }
178
179     /**
180      * Returns list of known test groups.
181      */
182     const groupnames list_groups() const
183     {
184         groupnames ret;
185         const_iterator i = groups_.begin();
186         const_iterator e = groups_.end();
187         while (i != e)
188         {
189             ret.push_back(i->first);
190             ++i;
191         }
192         return ret;
193     }
194
195     /**
196      * Runs all tests in all groups.
197      * @param callback Callback object if exists; null otherwise
198      */
199     void run_tests() const
200     {
201         cb_run_started_();
202
203         const_iterator i = groups_.begin();
204         const_iterator e = groups_.end();
205         while (i != e)
206         {
207             cb_group_started_(i->first);
208             run_all_tests_in_group_(i);
209             cb_group_completed_(i->first);
210
211             ++i;
212         }
213
214         cb_run_completed_();
215     }
216
217     /**
218      * Runs all tests in specified group.
219      */
220     void run_tests(const std::string& group_name) const
221     {
222         cb_run_started_();
223
224         const_iterator i = groups_.find(group_name);
225         if (i == groups_.end())
226         {
227             cb_run_completed_();
228             throw no_such_group(group_name);
229         }
230
231         cb_group_started_(group_name);
232         run_all_tests_in_group_(i);
233         cb_group_completed_(group_name);
234         cb_run_completed_();
235     }
236
237     /**
238      * Runs one test in specified group.
239      */
240     bool run_test(const std::string& group_name, int n, test_result &tr) const
241     {
242         cb_run_started_();
243
244         const_iterator i = groups_.find(group_name);
245         if (i == groups_.end())
246         {
247             cb_run_completed_();
248             throw no_such_group(group_name);
249         }
250
251         cb_group_started_(group_name);
252
253         bool t = i->second->run_test(n, tr);
254
255         if(t && tr.result != test_result::dummy)
256         {
257             cb_test_completed_(tr);
258         }
259
260         cb_group_completed_(group_name);
261         cb_run_completed_();
262
263         return t;
264     }
265
266 protected:
267
268     typedef std::map<std::string, group_base*> groups;
269     typedef groups::iterator iterator;
270     typedef groups::const_iterator const_iterator;
271     groups groups_;
272
273     callbacks callbacks_;
274
275 private:
276     friend class restartable_wrapper;
277
278     void cb_run_started_() const
279     {
280         for(callbacks::const_iterator i = callbacks_.begin(); i != callbacks_.end(); ++i)
281         {
282             (*i)->run_started();
283         }
284     }
285
286     void cb_run_completed_() const
287     {
288         for(callbacks::const_iterator i = callbacks_.begin(); i != callbacks_.end(); ++i)
289         {
290             (*i)->run_completed();
291         }
292     }
293
294     void cb_group_started_(const std::string &group_name) const
295     {
296         for(callbacks::const_iterator i = callbacks_.begin(); i != callbacks_.end(); ++i)
297         {
298             (*i)->group_started(group_name);
299         }
300     }
301
302     void cb_group_completed_(const std::string &group_name) const
303     {
304         for(callbacks::const_iterator i = callbacks_.begin(); i != callbacks_.end(); ++i)
305         {
306             (*i)->group_completed(group_name);
307         }
308     }
309
310     void cb_test_completed_(const test_result &tr) const
311     {
312         for(callbacks::const_iterator i = callbacks_.begin(); i != callbacks_.end(); ++i)
313         {
314             (*i)->test_completed(tr);
315         }
316     }
317
318     void run_all_tests_in_group_(const_iterator i) const
319     {
320         i->second->rewind();
321
322         test_result tr;
323         while(i->second->run_next(tr))
324         {
325             if(tr.result != test_result::dummy)
326             {
327                 cb_test_completed_(tr);
328             }
329
330             if (tr.result == test_result::ex_ctor)
331             {
332                 // test object ctor failed, skip whole group
333                 break;
334             }
335         }
336     }
337 };
338
339 /**
340  * Singleton for test_runner implementation.
341  * Instance with name runner_singleton shall be implemented
342  * by user.
343  */
344 class test_runner_singleton
345 {
346 public:
347
348     static test_runner& get()
349     {
350         static test_runner tr;
351         return tr;
352     }
353 };
354
355 extern test_runner_singleton runner;
356
357 }
358
359 #endif