source: trunk/UNIT_TESTER/test_global.h

Last change on this file was 19363, checked in by westram, 16 months ago
File size: 8.3 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
78// marker for mock objects (i.e. fake objects simulating partial behavior)
79typedef enum { UNITTEST_MOCK } UnittestMock;
80
81namespace arb_test {
82    class FlushedOutputNoLF {
83        inline void flushall() { fflush(stdout); fflush(stderr); }
84    public:
85        FlushedOutputNoLF() { flushall(); }
86        ~FlushedOutputNoLF() { flushall(); }
87    };
88    struct FlushedOutput : public FlushedOutputNoLF {
89        ~FlushedOutput() { fputc('\n', stderr); }
90    };
91
92    enum FlagAction {
93        FLAG_RAISE,
94        FLAG_IS_RAISED,
95    };
96
97    inline const char *fakeenv(const char *var) {
98        // override some environment variables for unittests
99        if (strcmp(var, "HOME") == 0) return "./homefake"; // normally should be $ARBHOME/UNIT_TESTER/run/homefake
100        return NULp;
101    }
102
103
104    class GlobalTestData {
105        typedef bool (*FlagCallback)(FlagAction action, const char *name);
106
107        FlagCallback flag_cb;
108
109        GlobalTestData() :
110            flag_cb(NULp),
111            annotation(NULp),
112            retry_count(0),
113            show_warnings(true),
114            assertion_failed(false),
115            running_test(false),
116            entered_mutex_loop(false),
117            warnings(0)
118        {}
119        ~GlobalTestData() {
120            unannotate();
121        }
122        GlobalTestData(const GlobalTestData&); // do not synthesize
123        GlobalTestData& operator=(const GlobalTestData&); // do not synthesize
124
125        static GlobalTestData *instance(bool erase) {
126            static GlobalTestData *data = NULp; // singleton
127            if (erase) {
128                delete data;
129                data = NULp;
130            }
131            else if (!data) {
132                static int allocation_count = 0;
133                arb_assert(allocation_count == 0); // allocating GlobalTestData twice is a bug!
134                allocation_count++;
135
136                data = new GlobalTestData;
137            }
138            return data;
139        }
140
141        char     *annotation;  // shown in assertion-failure message
142        unsigned  retry_count; // >0 -> retry to run test multiple times
143
144        void unannotate() {
145            free(annotation);
146            annotation = NULp;
147        }
148
149    public:
150        bool show_warnings;
151        bool assertion_failed;
152        bool running_test;
153        bool entered_mutex_loop; // helper to avoid race-condition
154
155        // counters
156        size_t warnings;
157
158        void raiseLocalFlag(const char *name) const {
159            if (flag_cb) {
160                flag_cb(FLAG_RAISE, name);
161            }
162            else {
163                fputs("cannot raise local flag (called from outside test-code?)\n", stderr);
164            }
165        }
166        void init(FlagCallback fc) { flag_cb = fc; }
167
168        bool not_covered_by_test() const { return !running_test; }
169
170        static GlobalTestData& get_instance() { return *instance(false); }
171        static void erase_instance() { instance(true); }
172
173        void allow_retry(unsigned count) {
174            if (retry_count == 0) { // only allow once (from initial call of test-code)
175                retry_count = count;
176            }
177        }
178        unsigned allowed_retries() const {
179            return retry_count;
180        }
181
182        void annotate(const char *annotation_) {
183            unannotate();
184            annotation = annotation_ ? strdup(annotation_) : NULp;
185        }
186        const char *get_annotation() const { return annotation; }
187
188        static void print_annotation() {
189            char*& annotation = get_instance().annotation;
190            if (annotation) fprintf(stderr, " (%s)", annotation);
191        }
192
193        static void assertfailmsg(const char *filename, int lineno, const char *condition) {
194            FlushedOutput yes;
195            fprintf(stderr, "\n%s:%i: Assertion '%s' failed", filename, lineno, condition);
196            print_annotation();
197        }
198    };
199
200    inline GlobalTestData& test_data() { return GlobalTestData::get_instance(); }
201};
202
203// --------------------------------------------------------------------------------
204
205#define TEST_ANNOTATE(annotation) arb_test::test_data().annotate(annotation)
206#define TEST_ALLOW_RETRY(count)   arb_test::test_data().allow_retry(count)
207#define RUNNING_TEST()            arb_test::test_data().running_test
208
209// special assert for unit tests (additionally to SEGV it sets a global flag)
210#define test_assert(cond,backtrace)             \
211    do {                                        \
212        if (!(cond)) {                          \
213            PRINT_ASSERTION_FAILED_MSG(cond);   \
214            TRIGGER_ASSERTION(backtrace);       \
215        }                                       \
216    } while(0)
217
218// Redefine arb_assert with test_assert when compiling for unit tests.
219//
220// Always request a backtrace because these assertions are unexpected and
221// might require to recompile tests w/o deadlockguard just to determine
222// the callers (using a debugger).
223
224# if defined(ASSERTION_USED)
225#  undef arb_assert
226#  define arb_assert(cond) test_assert(cond, true)
227# endif
228
229#define UNCOVERED() test_assert(arb_test::test_data().not_covered_by_test(), false)
230// the opposite (i.e. COVERED()) would be quite nice, but is not trivial or even impossible
231
232#else // !UNIT_TESTS
233#error test_global.h may only be included if UNIT_TESTS is defined
234#endif
235
236#else
237#error test_global.h included twice
238#endif // TEST_GLOBAL_H
Note: See TracBrowser for help on using the repository browser.