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

« back to all changes in this revision

Viewing changes to test/unit/tut/tut_restartable.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_RESTARTABLE_H_GUARD
 
2
#define TUT_RESTARTABLE_H_GUARD
 
3
 
 
4
#include <tut/tut.hpp>
 
5
#include <fstream>
 
6
#include <iostream>
 
7
#include <stdexcept>
 
8
#include <cassert>
 
9
 
 
10
/**
 
11
 * Optional restartable wrapper for test_runner.
 
12
 *
 
13
 * Allows to restart test runs finished due to abnormal
 
14
 * test application termination (such as segmentation
 
15
 * fault or math error).
 
16
 *
 
17
 * @author Vladimir Dyuzhev, Vladimir.Dyuzhev@gmail.com
 
18
 */
 
19
 
 
20
namespace tut
 
21
{
 
22
 
 
23
namespace util
 
24
{
 
25
 
 
26
/**
 
27
 * Escapes non-alphabetical characters in string.
 
28
 */
 
29
std::string escape(const std::string& orig)
 
30
{
 
31
    std::string rc;
 
32
    std::string::const_iterator i,e;
 
33
    i = orig.begin();
 
34
    e = orig.end();
 
35
 
 
36
    while (i != e)
 
37
    {
 
38
        if ((*i >= 'a' && *i <= 'z') ||
 
39
                (*i >= 'A' && *i <= 'Z') ||
 
40
                (*i >= '0' && *i <= '9') )
 
41
        {
 
42
            rc += *i;
 
43
        }
 
44
        else
 
45
        {
 
46
            rc += '\\';
 
47
            rc += ('a'+(((unsigned int)*i) >> 4));
 
48
            rc += ('a'+(((unsigned int)*i) & 0xF));
 
49
        }
 
50
 
 
51
        ++i;
 
52
    }
 
53
    return rc;
 
54
}
 
55
 
 
56
/**
 
57
 * Un-escapes string.
 
58
 */
 
59
std::string unescape(const std::string& orig)
 
60
{
 
61
    std::string rc;
 
62
    std::string::const_iterator i,e;
 
63
    i = orig.begin();
 
64
    e = orig.end();
 
65
 
 
66
    while (i != e)
 
67
    {
 
68
        if (*i != '\\')
 
69
        {
 
70
            rc += *i;
 
71
        }
 
72
        else
 
73
        {
 
74
            ++i;
 
75
            if (i == e)
 
76
            {
 
77
                throw std::invalid_argument("unexpected end of string");
 
78
            }
 
79
            unsigned int c1 = *i;
 
80
            ++i;
 
81
            if (i == e)
 
82
            {
 
83
                throw std::invalid_argument("unexpected end of string");
 
84
            }
 
85
            unsigned int c2 = *i;
 
86
            rc += (((c1 - 'a') << 4) + (c2 - 'a'));
 
87
        }
 
88
 
 
89
        ++i;
 
90
    }
 
91
    return rc;
 
92
}
 
93
 
 
94
/**
 
95
 * Serialize test_result avoiding interfering with operator <<.
 
96
 */
 
97
void serialize(std::ostream& os, const tut::test_result& tr)
 
98
{
 
99
    os << escape(tr.group) << std::endl;
 
100
    os << tr.test << ' ';
 
101
    switch(tr.result)
 
102
    {
 
103
    case test_result::ok:
 
104
        os << 0;
 
105
        break;
 
106
    case test_result::fail:
 
107
        os << 1;
 
108
        break;
 
109
    case test_result::ex:
 
110
        os << 2;
 
111
        break;
 
112
    case test_result::warn:
 
113
        os << 3;
 
114
        break;
 
115
    case test_result::term:
 
116
        os << 4;
 
117
        break;
 
118
    case test_result::rethrown:
 
119
        os << 5;
 
120
        break;
 
121
    case test_result::ex_ctor:
 
122
        os << 6;
 
123
        break;
 
124
    case test_result::dummy:
 
125
        assert(!"Should never be called");
 
126
    default:
 
127
        throw std::logic_error("operator << : bad result_type");
 
128
    }
 
129
    os << ' ' << escape(tr.message) << std::endl;
 
130
}
 
131
 
 
132
/**
 
133
 * deserialization for test_result
 
134
 */
 
135
bool deserialize(std::istream& is, tut::test_result& tr)
 
136
{
 
137
    std::getline(is,tr.group);
 
138
    if (is.eof())
 
139
    {
 
140
        return false;
 
141
    }
 
142
    tr.group = unescape(tr.group);
 
143
 
 
144
    tr.test = -1;
 
145
    is >> tr.test;
 
146
    if (tr.test < 0)
 
147
    {
 
148
        throw std::logic_error("operator >> : bad test number");
 
149
    }
 
150
 
 
151
    int n = -1;
 
152
    is >> n;
 
153
    switch(n)
 
154
    {
 
155
    case 0:
 
156
        tr.result = test_result::ok;
 
157
        break;
 
158
    case 1:
 
159
        tr.result = test_result::fail;
 
160
        break;
 
161
    case 2:
 
162
        tr.result = test_result::ex;
 
163
        break;
 
164
    case 3:
 
165
        tr.result = test_result::warn;
 
166
        break;
 
167
    case 4:
 
168
        tr.result = test_result::term;
 
169
        break;
 
170
    case 5:
 
171
        tr.result = test_result::rethrown;
 
172
        break;
 
173
    case 6:
 
174
        tr.result = test_result::ex_ctor;
 
175
        break;
 
176
    default:
 
177
        throw std::logic_error("operator >> : bad result_type");
 
178
    }
 
179
 
 
180
    is.ignore(1); // space
 
181
    std::getline(is,tr.message);
 
182
    tr.message = unescape(tr.message);
 
183
    if (!is.good())
 
184
    {
 
185
        throw std::logic_error("malformed test result");
 
186
    }
 
187
    return true;
 
188
}
 
189
}
 
190
 
 
191
/**
 
192
 * Restartable test runner wrapper.
 
193
 */
 
194
class restartable_wrapper
 
195
{
 
196
    test_runner& runner_;
 
197
    callbacks callbacks_;
 
198
 
 
199
    std::string dir_;
 
200
    std::string log_; // log file: last test being executed
 
201
    std::string jrn_; // journal file: results of all executed tests
 
202
 
 
203
public:
 
204
    /**
 
205
     * Default constructor.
 
206
     * @param dir Directory where to search/put log and journal files
 
207
     */
 
208
    restartable_wrapper(const std::string& dir = ".")
 
209
        : runner_(runner.get()),
 
210
          callbacks_(),
 
211
          dir_(dir),
 
212
          log_( dir + '/' + "log.tut" ),
 
213
          jrn_( dir + '/' + "journal.tut" )
 
214
    {
 
215
        // dozen: it works, but it would be better to use system path separator
 
216
    }
 
217
 
 
218
    /**
 
219
     * Stores another group for getting by name.
 
220
     */
 
221
    void register_group(const std::string& name, group_base* gr)
 
222
    {
 
223
        runner_.register_group(name,gr);
 
224
    }
 
225
 
 
226
    /**
 
227
     * Stores callback object.
 
228
     */
 
229
    void set_callback(callback* cb)
 
230
    {
 
231
        callbacks_.clear();
 
232
        callbacks_.insert(cb);
 
233
    }
 
234
 
 
235
    void insert_callback(callback* cb)
 
236
    {
 
237
        callbacks_.insert(cb);
 
238
    }
 
239
 
 
240
    void erase_callback(callback* cb)
 
241
    {
 
242
        callbacks_.erase(cb);
 
243
    }
 
244
 
 
245
    void set_callbacks(const callbacks& cb)
 
246
    {
 
247
        callbacks_ = cb;
 
248
    }
 
249
 
 
250
    const callbacks& get_callbacks() const
 
251
    {
 
252
        return runner_.get_callbacks();
 
253
    }
 
254
 
 
255
    /**
 
256
     * Returns list of known test groups.
 
257
     */
 
258
    groupnames list_groups() const
 
259
    {
 
260
        return runner_.list_groups();
 
261
    }
 
262
 
 
263
    /**
 
264
     * Runs all tests in all groups.
 
265
     */
 
266
    void run_tests() const
 
267
    {
 
268
        // where last run was failed
 
269
        std::string fail_group;
 
270
        int fail_test;
 
271
        read_log_(fail_group,fail_test);
 
272
        bool fail_group_reached = (fail_group == "");
 
273
 
 
274
        // iterate over groups
 
275
        tut::groupnames gn = list_groups();
 
276
        tut::groupnames::const_iterator gni,gne;
 
277
        gni = gn.begin();
 
278
        gne = gn.end();
 
279
        while (gni != gne)
 
280
        {
 
281
            // skip all groups before one that failed
 
282
            if (!fail_group_reached)
 
283
            {
 
284
                if (*gni != fail_group)
 
285
                {
 
286
                    ++gni;
 
287
                    continue;
 
288
                }
 
289
                fail_group_reached = true;
 
290
            }
 
291
 
 
292
            // first or restarted run
 
293
            int test = (*gni == fail_group && fail_test >= 0) ? fail_test + 1 : 1;
 
294
            while(true)
 
295
            {
 
296
                // last executed test pos
 
297
                register_execution_(*gni,test);
 
298
 
 
299
                tut::test_result tr;
 
300
                if( !runner_.run_test(*gni,test, tr) || tr.result == test_result::dummy )
 
301
                {
 
302
                    break;
 
303
                }
 
304
                register_test_(tr);
 
305
 
 
306
                ++test;
 
307
            }
 
308
 
 
309
            ++gni;
 
310
        }
 
311
 
 
312
        // show final results to user
 
313
        invoke_callback_();
 
314
 
 
315
        // truncate files as mark of successful finish
 
316
        truncate_();
 
317
    }
 
318
 
 
319
private:
 
320
    /**
 
321
     * Shows results from journal file.
 
322
     */
 
323
    void invoke_callback_() const
 
324
    {
 
325
        runner_.set_callbacks(callbacks_);
 
326
        runner_.cb_run_started_();
 
327
 
 
328
        std::string current_group;
 
329
        std::ifstream ijournal(jrn_.c_str());
 
330
        while (ijournal.good())
 
331
        {
 
332
            tut::test_result tr;
 
333
            if( !util::deserialize(ijournal,tr) )
 
334
            {
 
335
                break;
 
336
            }
 
337
            runner_.cb_test_completed_(tr);
 
338
        }
 
339
 
 
340
        runner_.cb_run_completed_();
 
341
    }
 
342
 
 
343
    /**
 
344
     * Register test into journal.
 
345
     */
 
346
    void register_test_(const test_result& tr) const
 
347
    {
 
348
        std::ofstream ojournal(jrn_.c_str(), std::ios::app);
 
349
        util::serialize(ojournal, tr);
 
350
        ojournal << std::flush;
 
351
        if (!ojournal.good())
 
352
        {
 
353
            throw std::runtime_error("unable to register test result in file "
 
354
                + jrn_);
 
355
        }
 
356
    }
 
357
 
 
358
    /**
 
359
     * Mark the fact test going to be executed
 
360
     */
 
361
    void register_execution_(const std::string& grp, int test) const
 
362
    {
 
363
        // last executed test pos
 
364
        std::ofstream olog(log_.c_str());
 
365
        olog << util::escape(grp) << std::endl << test << std::endl << std::flush;
 
366
        if (!olog.good())
 
367
        {
 
368
            throw std::runtime_error("unable to register execution in file "
 
369
                + log_);
 
370
        }
 
371
    }
 
372
 
 
373
    /**
 
374
     * Truncate tests.
 
375
     */
 
376
    void truncate_() const
 
377
    {
 
378
        std::ofstream olog(log_.c_str());
 
379
        std::ofstream ojournal(jrn_.c_str());
 
380
    }
 
381
 
 
382
    /**
 
383
     * Read log file
 
384
     */
 
385
    void read_log_(std::string& fail_group, int& fail_test) const
 
386
    {
 
387
        // read failure point, if any
 
388
        std::ifstream ilog(log_.c_str());
 
389
        std::getline(ilog,fail_group);
 
390
        fail_group = util::unescape(fail_group);
 
391
        ilog >> fail_test;
 
392
        if (!ilog.good())
 
393
        {
 
394
            fail_group = "";
 
395
            fail_test = -1;
 
396
            truncate_();
 
397
        }
 
398
        else
 
399
        {
 
400
            // test was terminated...
 
401
            tut::test_result tr(fail_group, fail_test, "", tut::test_result::term);
 
402
            register_test_(tr);
 
403
        }
 
404
    }
 
405
};
 
406
 
 
407
}
 
408
 
 
409
#endif
 
410