source: tags/ms_r18q1/UNIT_TESTER/test_global.h

Last change on this file was 16778, checked in by westram, 6 years ago
  • unittests may permit retries using TEST_ALLOW_RETRY
    • workaround for randomly failing tests
File size: 8.2 KB
Line 
1// ================================================================= //
2//                                                                   //
3//   File      : test_global.h                                       //
4//   Purpose   : special assertion handling if arb is compiled       //
5//               with unit-tests                                     //
6//                                                                   //
7//   Coded by Ralf Westram (coder@reallysoft.de) in September 2010   //
8//   Institute of Microbiology (Technical University Munich)         //
9//   http://www.arb-home.de/                                         //
10//                                                                   //
11// ================================================================= //
12
13#ifndef TEST_GLOBAL_H
14#define TEST_GLOBAL_H
15
16// do not include here - just test
17// insert includes at ../INCLUDE/arb_assert.h@WhyIncludeHere
18#ifndef _STDARG_H
19#ifndef __STDARG_H
20#error Need cstdarg included
21#endif
22#endif
23#if !defined(_STDIO_H) && !defined(_STDIO_H_)
24#error Need cstdio included
25#endif
26#if !defined(_STDLIB_H) && !defined(_STDLIB_H_)
27#error Need cstdlib included
28#endif
29#if !defined(_ERRNO_H) && !defined(_SYS_ERRNO_H_)
30#error Need cerrno included
31#endif
32#if !defined(_STRING_H) && !defined(_STRING_H_)
33#error Need cstring included
34#endif
35#if !defined(CXXFORWARD_H)
36#error Need cxxforward.h included
37#endif
38
39#ifdef UNIT_TESTS
40
41# if defined(DEVEL_RELEASE)
42#  error Unit testing not allowed in release
43# endif
44
45# ifdef __cplusplus
46
47#  define SET_ASSERTION_FAILED_FLAG() arb_test::test_data().assertion_failed = true 
48#  define PRINT_ASSERTION_FAILED_MSG(cond) arb_test::GlobalTestData::assertfailmsg(__FILE__, __LINE__, #cond) 
49
50# else
51#  define SET_ASSERTION_FAILED_FLAG() // impossible in C (assertions in C code will be handled like normal SEGV)
52#  define PRINT_ASSERTION_FAILED_MSG(cond)                      \
53    do {                                                        \
54        fflush(stdout);                                         \
55        fflush(stderr);                                         \
56        fprintf(stderr, "%s:%i: Assertion '%s' failed [C]\n",   \
57                __FILE__, __LINE__, #cond);                     \
58        fflush(stderr);                                         \
59    } while(0)
60# endif
61
62#define SEGV_INSIDE_TEST_STOP_OTHERWISE(backtrace)              \
63    do {                                                        \
64        if (RUNNING_TEST()) {                                   \
65            ARB_SIGSEGV(backtrace);                             \
66        }                                                       \
67        else {                                                  \
68            ARB_STOP(backtrace);                                \
69        }                                                       \
70    } while(0)
71
72# define TRIGGER_ASSERTION(backtrace)                           \
73    do {                                                        \
74        SET_ASSERTION_FAILED_FLAG();                            \
75        SEGV_INSIDE_TEST_STOP_OTHERWISE(backtrace);             \
76    } while(0)
77
78namespace arb_test {
79    class FlushedOutputNoLF {
80        inline void flushall() { fflush(stdout); fflush(stderr); }
81    public:
82        FlushedOutputNoLF() { flushall(); }
83        ~FlushedOutputNoLF() { flushall(); }
84    };
85    struct FlushedOutput : public FlushedOutputNoLF {
86        ~FlushedOutput() { fputc('\n', stderr); }
87    };
88
89    enum FlagAction {
90        FLAG_RAISE,
91        FLAG_IS_RAISED,
92    };
93
94    inline const char *fakeenv(const char *var) {
95        // override some environment variables for unittests
96        if (strcmp(var, "HOME") == 0) return "./homefake"; // normally should be $ARBHOME/UNIT_TESTER/run/homefake
97        return NULp;
98    }
99
100
101    class GlobalTestData {
102        typedef bool (*FlagCallback)(FlagAction action, const char *name);
103
104        FlagCallback flag_cb;
105
106        GlobalTestData() :
107            flag_cb(NULp),
108            annotation(NULp),
109            retry_count(0),
110            show_warnings(true),
111            assertion_failed(false),
112            running_test(false),
113            entered_mutex_loop(false),
114            warnings(0)
115        {}
116        ~GlobalTestData() {
117            unannotate();
118        }
119        GlobalTestData(const GlobalTestData&); // do not synthesize
120        GlobalTestData& operator=(const GlobalTestData&); // do not synthesize
121
122        static GlobalTestData *instance(bool erase) {
123            static GlobalTestData *data = NULp; // singleton
124            if (erase) {
125                delete data;
126                data = NULp;
127            }
128            else if (!data) {
129                static int allocation_count = 0;
130                arb_assert(allocation_count == 0); // allocating GlobalTestData twice is a bug!
131                allocation_count++;
132
133                data = new GlobalTestData;
134            }
135            return data;
136        }
137
138        char     *annotation;  // shown in assertion-failure message
139        unsigned  retry_count; // >0 -> retry to run test multiple times
140
141        void unannotate() {
142            free(annotation);
143            annotation = NULp;
144        }
145
146    public:
147        bool show_warnings;
148        bool assertion_failed;
149        bool running_test;
150        bool entered_mutex_loop; // helper to avoid race-condition
151
152        // counters
153        size_t warnings;
154
155        void raiseLocalFlag(const char *name) const {
156            if (flag_cb) {
157                flag_cb(FLAG_RAISE, name);
158            }
159            else {
160                fputs("cannot raise local flag (called from outside test-code?)\n", stderr);
161            }
162        }
163        void init(FlagCallback fc) { flag_cb = fc; }
164
165        bool not_covered_by_test() const { return !running_test; }
166
167        static GlobalTestData& get_instance() { return *instance(false); }
168        static void erase_instance() { instance(true); }
169
170        void allow_retry(unsigned count) {
171            if (retry_count == 0) { // only allow once (from initial call of test-code)
172                retry_count = count;
173            }
174        }
175        unsigned allowed_retries() const {
176            return retry_count;
177        }
178
179        void annotate(const char *annotation_) {
180            unannotate();
181            annotation = annotation_ ? strdup(annotation_) : NULp;
182        }
183        const char *get_annotation() const { return annotation; }
184
185        static void print_annotation() {
186            char*& annotation = get_instance().annotation;
187            if (annotation) fprintf(stderr, " (%s)", annotation);
188        }
189
190        static void assertfailmsg(const char *filename, int lineno, const char *condition) {
191            FlushedOutput yes;
192            fprintf(stderr, "\n%s:%i: Assertion '%s' failed", filename, lineno, condition);
193            print_annotation();
194        }
195    };
196
197    inline GlobalTestData& test_data() { return GlobalTestData::get_instance(); }
198};
199
200// --------------------------------------------------------------------------------
201
202#define TEST_ANNOTATE(annotation) arb_test::test_data().annotate(annotation)
203#define TEST_ALLOW_RETRY(count)   arb_test::test_data().allow_retry(count)
204#define RUNNING_TEST()            arb_test::test_data().running_test
205
206// special assert for unit tests (additionally to SEGV it sets a global flag)
207#define test_assert(cond,backtrace)             \
208    do {                                        \
209        if (!(cond)) {                          \
210            PRINT_ASSERTION_FAILED_MSG(cond);   \
211            TRIGGER_ASSERTION(backtrace);       \
212        }                                       \
213    } while(0)
214
215// Redefine arb_assert with test_assert when compiling for unit tests.
216//
217// Always request a backtrace because these assertions are unexpected and
218// might require to recompile tests w/o deadlockguard just to determine
219// the callers (using a debugger).
220
221# if defined(ASSERTION_USED)
222#  undef arb_assert
223#  define arb_assert(cond) test_assert(cond, true)
224# endif
225
226#define UNCOVERED() test_assert(arb_test::test_data().not_covered_by_test(), false)
227// the opposite (i.e. COVERED()) would be quite nice, but is not trivial or even impossible
228
229#else // !UNIT_TESTS
230#error test_global.h may only be included if UNIT_TESTS is defined
231#endif
232
233#else
234#error test_global.h included twice
235#endif // TEST_GLOBAL_H
Note: See TracBrowser for help on using the repository browser.