source: tags/ms_r16q4/UNIT_TESTER/test_global.h

Last change on this file was 13970, checked in by westram, 9 years ago
File size: 7.7 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
36#ifdef UNIT_TESTS
37
38# if defined(DEVEL_RELEASE)
39#  error Unit testing not allowed in release
40# endif
41
42# ifdef __cplusplus
43
44#  define SET_ASSERTION_FAILED_FLAG() arb_test::test_data().assertion_failed = true 
45#  define PRINT_ASSERTION_FAILED_MSG(cond) arb_test::GlobalTestData::assertfailmsg(__FILE__, __LINE__, #cond) 
46
47# else
48#  define SET_ASSERTION_FAILED_FLAG() // impossible in C (assertions in C code will be handled like normal SEGV)
49#  define PRINT_ASSERTION_FAILED_MSG(cond)                      \
50    do {                                                        \
51        fflush(stdout);                                         \
52        fflush(stderr);                                         \
53        fprintf(stderr, "%s:%i: Assertion '%s' failed [C]\n",   \
54                __FILE__, __LINE__, #cond);                     \
55        fflush(stderr);                                         \
56    } while(0)
57# endif
58
59#define SEGV_INSIDE_TEST_STOP_OTHERWISE(backtrace)              \
60    do {                                                        \
61        if (RUNNING_TEST()) {                                   \
62            ARB_SIGSEGV(backtrace);                             \
63        }                                                       \
64        else {                                                  \
65            ARB_STOP(backtrace);                                \
66        }                                                       \
67    } while(0)
68
69# define TRIGGER_ASSERTION(backtrace)                           \
70    do {                                                        \
71        SET_ASSERTION_FAILED_FLAG();                            \
72        SEGV_INSIDE_TEST_STOP_OTHERWISE(backtrace);             \
73    } while(0)
74
75namespace arb_test {
76    class FlushedOutputNoLF {
77        inline void flushall() { fflush(stdout); fflush(stderr); }
78    public:
79        FlushedOutputNoLF() { flushall(); }
80        ~FlushedOutputNoLF() { flushall(); }
81    };
82    struct FlushedOutput : public FlushedOutputNoLF {
83        ~FlushedOutput() { fputc('\n', stderr); }
84    };
85
86    enum FlagAction {
87        FLAG_RAISE,
88        FLAG_IS_RAISED,
89    };
90
91    inline const char *fakeenv(const char *var) {
92        // override some environment variables for unittests
93        if (strcmp(var, "HOME") == 0) return "./homefake"; // normally should be $ARBHOME/UNIT_TESTER/run/homefake
94        return NULL;
95    }
96
97
98    class GlobalTestData {
99        typedef bool (*FlagCallback)(FlagAction action, const char *name);
100
101        FlagCallback flag_cb;
102
103        GlobalTestData()
104            : flag_cb(NULL),
105              annotation(NULL),
106              show_warnings(true),
107              assertion_failed(false),
108              running_test(false),
109              entered_mutex_loop(false),
110              warnings(0)
111        {}
112        ~GlobalTestData() {
113            unannotate();
114        }
115        GlobalTestData(const GlobalTestData&); // do not synthesize
116        GlobalTestData& operator=(const GlobalTestData&); // do not synthesize
117
118        static GlobalTestData *instance(bool erase) {
119            static GlobalTestData *data = 0;             // singleton
120            if (erase) {
121                delete data;
122                data = 0;
123            }
124            else if (!data) {
125                static int allocation_count = 0;
126                arb_assert(allocation_count == 0); // allocating GlobalTestData twice is a bug!
127                allocation_count++;
128
129                data = new GlobalTestData;
130            }
131            return data;
132        }
133
134        char *annotation; // shown in assertion-failure message
135
136        void unannotate() {
137            free(annotation);
138            annotation = NULL;
139        }
140
141    public:
142        bool show_warnings;
143        bool assertion_failed;
144        bool running_test;
145        bool entered_mutex_loop; // helper to avoid race-condition
146
147        // counters
148        size_t warnings;
149
150        void raiseLocalFlag(const char *name) const {
151            if (flag_cb) {
152                flag_cb(FLAG_RAISE, name);
153            }
154            else {
155                fputs("cannot raise local flag (called from outside test-code?)\n", stderr);
156            }
157        }
158        void init(FlagCallback fc) { flag_cb = fc; }
159
160        bool not_covered_by_test() const { return !running_test; }
161
162        static GlobalTestData& get_instance() { return *instance(false); }
163        static void erase_instance() { instance(true); }
164
165        void annotate(const char *annotation_) {
166            unannotate();
167            annotation = annotation_ ? strdup(annotation_) : NULL;
168        }
169        const char *get_annotation() const { return annotation; }
170
171        static void print_annotation() {
172            char*& annotation = get_instance().annotation;
173            if (annotation) fprintf(stderr, " (%s)", annotation);
174        }
175
176        static void assertfailmsg(const char *filename, int lineno, const char *condition) {
177            FlushedOutput yes;
178            fprintf(stderr, "\n%s:%i: Assertion '%s' failed", filename, lineno, condition);
179            print_annotation();
180        }
181    };
182
183    inline GlobalTestData& test_data() { return GlobalTestData::get_instance(); }
184};
185
186// --------------------------------------------------------------------------------
187
188#define TEST_ANNOTATE(annotation) arb_test::test_data().annotate(annotation)
189#define RUNNING_TEST()            arb_test::test_data().running_test
190
191// special assert for unit tests (additionally to SEGV it sets a global flag)
192#define test_assert(cond,backtrace)             \
193    do {                                        \
194        if (!(cond)) {                          \
195            PRINT_ASSERTION_FAILED_MSG(cond);   \
196            TRIGGER_ASSERTION(backtrace);       \
197        }                                       \
198    } while(0)
199
200// Redefine arb_assert with test_assert when compiling for unit tests.
201//
202// Always request a backtrace because these assertions are unexpected and
203// might require to recompile tests w/o deadlockguard just to determine
204// the callers (using a debugger).
205
206# if defined(ASSERTION_USED)
207#  undef arb_assert
208#  define arb_assert(cond) test_assert(cond, true)
209# endif
210
211#define UNCOVERED() test_assert(arb_test::test_data().not_covered_by_test(), false)
212// the opposite (i.e. COVERED()) would be quite nice, but is not trivial or even impossible
213
214#else // !UNIT_TESTS
215#error test_global.h may only be included if UNIT_TESTS is defined
216#endif
217
218#else
219#error test_global.h included twice
220#endif // TEST_GLOBAL_H
Note: See TracBrowser for help on using the repository browser.