~ubuntu-branches/ubuntu/trusty/liblas/trusty-proposed

« back to all changes in this revision

Viewing changes to test/unit/tut/tut.hpp

  • Committer: Package Import Robot
  • Author(s): Francesco Paolo Lovergine
  • Date: 2014-01-05 17:00:29 UTC
  • mfrom: (7.1.2 sid)
  • Revision ID: package-import@ubuntu.com-20140105170029-ddtp0j63x5jvck2u
Tags: 1.7.0+dfsg-2
Fixed missing linking of system boost component.
(closes: #733282)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ifndef TUT_H_GUARD
 
2
#define TUT_H_GUARD
 
3
#include <tut/tut_config.hpp>
 
4
 
 
5
#include <iostream>
 
6
#include <map>
 
7
#include <vector>
 
8
#include <set>
 
9
#include <string>
 
10
#include <sstream>
 
11
#include <iterator>
 
12
#include <algorithm>
 
13
 
 
14
#include "tut_exception.hpp"
 
15
#include "tut_result.hpp"
 
16
#include "tut_posix.hpp"
 
17
#include "tut_assert.hpp"
 
18
#include "tut_runner.hpp"
 
19
 
 
20
#if defined(TUT_USE_SEH)
 
21
#include <windows.h>
 
22
#include <winbase.h>
 
23
#endif
 
24
 
 
25
/**
 
26
 * Template Unit Tests Framework for C++.
 
27
 * http://tut.dozen.ru
 
28
 *
 
29
 * @author Vladimir Dyuzhev, Vladimir.Dyuzhev@gmail.com
 
30
 */
 
31
namespace tut
 
32
{
 
33
 
 
34
template <class, int>
 
35
class test_group;
 
36
 
 
37
/**
 
38
 * Test object. Contains data test run upon and default test method
 
39
 * implementation. Inherited from Data to allow tests to
 
40
 * access test data as members.
 
41
 */
 
42
template <class Data>
 
43
class test_object : public Data, public test_object_posix
 
44
{
 
45
    template<class D, int M>
 
46
    friend class test_group;
 
47
 
 
48
    void set_test_group(const char *group)
 
49
    {
 
50
        current_test_group_ = group;
 
51
    }
 
52
 
 
53
    void set_test_id(int current_test_id)
 
54
    {
 
55
        current_test_id_ = current_test_id;
 
56
    }
 
57
 
 
58
public:
 
59
 
 
60
    /**
 
61
     * Default constructor
 
62
     */
 
63
    test_object()
 
64
        : called_method_was_a_dummy_test_(false),
 
65
          current_test_id_(0),
 
66
          current_test_name_(),
 
67
          current_test_group_()
 
68
    {
 
69
    }
 
70
 
 
71
    void set_test_name(const std::string& current_test_name)
 
72
    {
 
73
        current_test_name_ = current_test_name;
 
74
    }
 
75
 
 
76
    const std::string& get_test_name() const
 
77
    {
 
78
        return current_test_name_;
 
79
    }
 
80
 
 
81
    const std::string& get_test_group() const
 
82
    {
 
83
        return current_test_group_;
 
84
    }
 
85
 
 
86
    int get_test_id() const
 
87
    {
 
88
        return current_test_id_;
 
89
    }
 
90
 
 
91
    /**
 
92
     * Default do-nothing test.
 
93
     */
 
94
    template <int n>
 
95
    void test()
 
96
    {
 
97
        called_method_was_a_dummy_test_ = true;
 
98
    }
 
99
 
 
100
    /**
 
101
     * The flag is set to true by default (dummy) test.
 
102
     * Used to detect usused test numbers and avoid unnecessary
 
103
     * test object creation which may be time-consuming depending
 
104
     * on operations described in Data::Data() and Data::~Data().
 
105
     */
 
106
    bool called_method_was_a_dummy_test_;
 
107
 
 
108
    virtual ~test_object()
 
109
    {
 
110
    }
 
111
 
 
112
private:
 
113
    int             current_test_id_;
 
114
    std::string     current_test_name_;
 
115
    std::string     current_test_group_;
 
116
};
 
117
 
 
118
 
 
119
/**
 
120
 * Walks through test tree and stores address of each
 
121
 * test method in group. Instantiation stops at 0.
 
122
 */
 
123
template <class Test, class Group, int n>
 
124
struct tests_registerer
 
125
{
 
126
    static void reg(Group& group)
 
127
    {
 
128
        group.reg(n, &Test::template test<n>);
 
129
        tests_registerer<Test, Group, n - 1>::reg(group);
 
130
    }
 
131
};
 
132
 
 
133
template <class Test, class Group>
 
134
struct tests_registerer<Test, Group, 0>
 
135
{
 
136
    static void reg(Group&)
 
137
    {
 
138
    }
 
139
};
 
140
 
 
141
/**
 
142
 * Test group; used to recreate test object instance for
 
143
 * each new test since we have to have reinitialized
 
144
 * Data base class.
 
145
 */
 
146
template <class Data, int MaxTestsInGroup = 50>
 
147
class test_group : public group_base, public test_group_posix
 
148
{
 
149
    test_group(const test_group&);
 
150
    void operator=(const test_group&);
 
151
 
 
152
    const char* name_;
 
153
 
 
154
    typedef void (test_object<Data>::*testmethod)();
 
155
    typedef std::map<int, testmethod> tests;
 
156
    typedef typename tests::iterator tests_iterator;
 
157
    typedef typename tests::const_iterator tests_const_iterator;
 
158
    typedef typename tests::const_reverse_iterator
 
159
    tests_const_reverse_iterator;
 
160
    typedef typename tests::size_type size_type;
 
161
 
 
162
    tests tests_;
 
163
    tests_iterator current_test_;
 
164
 
 
165
    enum seh_result
 
166
    {
 
167
        SEH_OK,
 
168
#if defined(TUT_USE_SEH)
 
169
        SEH_CTOR,
 
170
        SEH_TEST,
 
171
#endif
 
172
        SEH_DUMMY
 
173
    };
 
174
 
 
175
    /**
 
176
     * Exception-in-destructor-safe smart-pointer class.
 
177
     */
 
178
    template <class T>
 
179
    class safe_holder
 
180
    {
 
181
        T* p_;
 
182
        bool permit_throw_in_dtor;
 
183
 
 
184
        safe_holder(const safe_holder&);
 
185
        safe_holder& operator=(const safe_holder&);
 
186
 
 
187
    public:
 
188
        safe_holder()
 
189
            : p_(0),
 
190
              permit_throw_in_dtor(false)
 
191
        {
 
192
        }
 
193
 
 
194
        ~safe_holder()
 
195
        {
 
196
            release();
 
197
        }
 
198
 
 
199
        T* operator->() const
 
200
        {
 
201
            return p_;
 
202
        }
 
203
 
 
204
        T* get() const
 
205
        {
 
206
            return p_;
 
207
        }
 
208
 
 
209
        /**
 
210
         * Tell ptr it can throw from destructor. Right way is to
 
211
         * use std::uncaught_exception(), but some compilers lack
 
212
         * correct implementation of the function.
 
213
         */
 
214
        void permit_throw()
 
215
        {
 
216
            permit_throw_in_dtor = true;
 
217
        }
 
218
 
 
219
        /**
 
220
         * Specially treats exceptions in test object destructor;
 
221
         * if test itself failed, exceptions in destructor
 
222
         * are ignored; if test was successful and destructor failed,
 
223
         * warning exception throwed.
 
224
         */
 
225
        void release()
 
226
        {
 
227
            try
 
228
            {
 
229
#if defined(TUT_USE_SEH)
 
230
                if (delete_obj() == false)
 
231
                {
 
232
                    throw warning("destructor of test object raised"
 
233
                        " an SEH exception");
 
234
                }
 
235
#else
 
236
                bool d = delete_obj();
 
237
                (void)d; // unused variable in Release mode
 
238
                assert(d && "delete failed with SEH disabled: runtime bug?");
 
239
#endif
 
240
            }
 
241
            catch (const std::exception& ex)
 
242
            {
 
243
                if (permit_throw_in_dtor)
 
244
                {
 
245
                    std::string msg = "destructor of test object raised"
 
246
                        " exception: ";
 
247
                    msg += ex.what();
 
248
                    throw warning(msg);
 
249
                }
 
250
            }
 
251
            catch( ... )
 
252
            {
 
253
                if (permit_throw_in_dtor)
 
254
                {
 
255
                    throw warning("destructor of test object raised an"
 
256
                        " exception");
 
257
                }
 
258
            }
 
259
        }
 
260
 
 
261
        /**
 
262
         * Re-init holder to get brand new object.
 
263
         */
 
264
        void reset()
 
265
        {
 
266
            release();
 
267
            permit_throw_in_dtor = false;
 
268
            p_ = new T();
 
269
        }
 
270
 
 
271
        bool delete_obj()
 
272
        {
 
273
#if defined(TUT_USE_SEH)
 
274
            __try
 
275
            {
 
276
#endif
 
277
                T* p = p_;
 
278
                p_ = 0;
 
279
                delete p;
 
280
#if defined(TUT_USE_SEH)
 
281
            }
 
282
            __except(handle_seh_(::GetExceptionCode()))
 
283
            {
 
284
                if (permit_throw_in_dtor)
 
285
                {
 
286
                    return false;
 
287
                }
 
288
            }
 
289
#endif
 
290
            return true;
 
291
        }
 
292
    };
 
293
 
 
294
public:
 
295
 
 
296
    typedef test_object<Data> object;
 
297
 
 
298
    /**
 
299
     * Creates and registers test group with specified name.
 
300
     */
 
301
    test_group(const char* name)
 
302
        : name_(name),
 
303
          tests_(),
 
304
          current_test_()
 
305
    {
 
306
        // register itself
 
307
        runner.get().register_group(name_,this);
 
308
 
 
309
        // register all tests
 
310
        tests_registerer<object, test_group, MaxTestsInGroup>::reg(*this);
 
311
    }
 
312
 
 
313
    /**
 
314
     * This constructor is used in self-test run only.
 
315
     */
 
316
    test_group(const char* name, test_runner& another_runner)
 
317
        : name_(name),
 
318
          tests_(),
 
319
          current_test_()
 
320
    {
 
321
        // register itself
 
322
        another_runner.register_group(name_, this);
 
323
 
 
324
        // register all tests
 
325
        tests_registerer<test_object<Data>, test_group,
 
326
            MaxTestsInGroup>::reg(*this);
 
327
    };
 
328
 
 
329
    /**
 
330
     * Registers test method under given number.
 
331
     */
 
332
    void reg(int n, testmethod tm)
 
333
    {
 
334
        tests_[n] = tm;
 
335
    }
 
336
 
 
337
    /**
 
338
     * Reset test position before first test.
 
339
     */
 
340
    void rewind()
 
341
    {
 
342
        current_test_ = tests_.begin();
 
343
    }
 
344
 
 
345
    /**
 
346
     * Runs next test.
 
347
     */
 
348
    bool run_next(test_result &tr)
 
349
    {
 
350
        if (current_test_ == tests_.end())
 
351
        {
 
352
            return false;
 
353
        }
 
354
 
 
355
        // find next user-specialized test
 
356
        safe_holder<object> obj;
 
357
        while (current_test_ != tests_.end())
 
358
        {
 
359
            tests_iterator current_test = current_test_++;
 
360
 
 
361
            if(run_test_(current_test, obj, tr) && tr.result != test_result::dummy)
 
362
            {
 
363
                return true;
 
364
            }
 
365
        }
 
366
 
 
367
        return false;
 
368
    }
 
369
 
 
370
    /**
 
371
     * Runs one test by position.
 
372
     */
 
373
    bool run_test(int n, test_result &tr)
 
374
    {
 
375
        if (tests_.rbegin() == tests_.rend() ||
 
376
            tests_.rbegin()->first < n)
 
377
        {
 
378
            return false;
 
379
        }
 
380
 
 
381
        // withing scope; check if given test exists
 
382
        tests_iterator ti = tests_.find(n);
 
383
        if (ti == tests_.end())
 
384
        {
 
385
            return false;
 
386
        }
 
387
 
 
388
        safe_holder<object> obj;
 
389
        return run_test_(ti, obj, tr);
 
390
    }
 
391
 
 
392
    /**
 
393
     * VC allows only one exception handling type per function,
 
394
     * so I have to split the method.
 
395
     */
 
396
    bool run_test_(const tests_iterator& ti, safe_holder<object>& obj, test_result &tr)
 
397
    {
 
398
        std::string current_test_name;
 
399
 
 
400
        tr = test_result(name_, ti->first, current_test_name, test_result::ok);
 
401
 
 
402
        try
 
403
        {
 
404
            switch (run_test_seh_(ti->second, obj, current_test_name, ti->first))
 
405
            {
 
406
#if defined(TUT_USE_SEH)
 
407
                case SEH_CTOR:
 
408
                    throw bad_ctor("seh");
 
409
                    break;
 
410
 
 
411
                case SEH_TEST:
 
412
                    throw seh("seh");
 
413
                    break;
 
414
#endif
 
415
                case SEH_DUMMY:
 
416
                    tr.result = test_result::dummy;
 
417
                    break;
 
418
 
 
419
                case SEH_OK:
 
420
                    // ok
 
421
                    break;
 
422
            }
 
423
        }
 
424
        catch (const rethrown& ex)
 
425
        {
 
426
            tr = ex.tr;
 
427
            tr.result = test_result::rethrown;
 
428
        }
 
429
        catch (const tut_error& ex)
 
430
        {
 
431
            tr.result = ex.result();
 
432
            tr.exception_typeid = ex.type();
 
433
            tr.message = ex.what();
 
434
        }
 
435
        catch (const std::exception& ex)
 
436
        {
 
437
            tr.result = test_result::ex;
 
438
            tr.exception_typeid = type_name(ex);
 
439
            tr.message = ex.what();
 
440
        }
 
441
        catch (...)
 
442
        {
 
443
            // test failed with unknown exception
 
444
            tr.result = test_result::ex;
 
445
        }
 
446
 
 
447
        if (obj.get())
 
448
        {
 
449
            tr.name = obj->get_test_name();
 
450
 
 
451
            // try to report to parent, if exists
 
452
            send_result_(obj.get(), tr);
 
453
        }
 
454
        else
 
455
        {
 
456
            tr.name = current_test_name;
 
457
        }
 
458
 
 
459
        return true;
 
460
    }
 
461
 
 
462
    /**
 
463
     * Runs one under SEH if platform supports it.
 
464
     */
 
465
    seh_result run_test_seh_(testmethod tm, safe_holder<object>& obj,
 
466
                             std::string& current_test_name, int current_test_id)
 
467
    {
 
468
#if defined(TUT_USE_SEH)
 
469
        __try
 
470
        {
 
471
#endif
 
472
            if (obj.get() == 0)
 
473
            {
 
474
                reset_holder_(obj);
 
475
            }
 
476
 
 
477
            obj->called_method_was_a_dummy_test_ = false;
 
478
 
 
479
#if defined(TUT_USE_SEH)
 
480
 
 
481
            __try
 
482
            {
 
483
#endif
 
484
                obj.get()->set_test_id(current_test_id);
 
485
                obj.get()->set_test_group(name_);
 
486
                (obj.get()->*tm)();
 
487
#if defined(TUT_USE_SEH)
 
488
            }
 
489
            __except(handle_seh_(::GetExceptionCode()))
 
490
            {
 
491
                current_test_name = obj->get_test_name();
 
492
                return SEH_TEST;
 
493
            }
 
494
#endif
 
495
 
 
496
            if (obj->called_method_was_a_dummy_test_)
 
497
            {
 
498
                // do not call obj.release(); reuse object
 
499
                return SEH_DUMMY;
 
500
            }
 
501
 
 
502
            current_test_name = obj->get_test_name();
 
503
            obj.permit_throw();
 
504
            obj.release();
 
505
#if defined(TUT_USE_SEH)
 
506
        }
 
507
        __except(handle_seh_(::GetExceptionCode()))
 
508
        {
 
509
            return SEH_CTOR;
 
510
        }
 
511
#endif
 
512
        return SEH_OK;
 
513
    }
 
514
 
 
515
    void reset_holder_(safe_holder<object>& obj)
 
516
    {
 
517
        try
 
518
        {
 
519
            obj.reset();
 
520
        }
 
521
        catch (const std::exception& ex)
 
522
        {
 
523
            throw bad_ctor(ex.what());
 
524
        }
 
525
        catch (...)
 
526
        {
 
527
            throw bad_ctor("test constructor has generated an exception;"
 
528
                " group execution is terminated");
 
529
        }
 
530
    }
 
531
};
 
532
 
 
533
#if defined(TUT_USE_SEH)
 
534
/**
 
535
 * Decides should we execute handler or ignore SE.
 
536
 */
 
537
inline int handle_seh_(DWORD excode)
 
538
{
 
539
    switch(excode)
 
540
    {
 
541
    case EXCEPTION_ACCESS_VIOLATION:
 
542
    case EXCEPTION_DATATYPE_MISALIGNMENT:
 
543
    case EXCEPTION_BREAKPOINT:
 
544
    case EXCEPTION_SINGLE_STEP:
 
545
    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
 
546
    case EXCEPTION_FLT_DENORMAL_OPERAND:
 
547
    case EXCEPTION_FLT_DIVIDE_BY_ZERO:
 
548
    case EXCEPTION_FLT_INEXACT_RESULT:
 
549
    case EXCEPTION_FLT_INVALID_OPERATION:
 
550
    case EXCEPTION_FLT_OVERFLOW:
 
551
    case EXCEPTION_FLT_STACK_CHECK:
 
552
    case EXCEPTION_FLT_UNDERFLOW:
 
553
    case EXCEPTION_INT_DIVIDE_BY_ZERO:
 
554
    case EXCEPTION_INT_OVERFLOW:
 
555
    case EXCEPTION_PRIV_INSTRUCTION:
 
556
    case EXCEPTION_IN_PAGE_ERROR:
 
557
    case EXCEPTION_ILLEGAL_INSTRUCTION:
 
558
    case EXCEPTION_NONCONTINUABLE_EXCEPTION:
 
559
    case EXCEPTION_STACK_OVERFLOW:
 
560
    case EXCEPTION_INVALID_DISPOSITION:
 
561
    case EXCEPTION_GUARD_PAGE:
 
562
    case EXCEPTION_INVALID_HANDLE:
 
563
        return EXCEPTION_EXECUTE_HANDLER;
 
564
    };
 
565
 
 
566
    return EXCEPTION_CONTINUE_SEARCH;
 
567
}
 
568
#endif
 
569
}
 
570
 
 
571
#endif // TUT_H_GUARD
 
572