ARB
test_global.h
Go to the documentation of this file.
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 namespace 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),
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
#define arb_assert(cond)
Definition: arb_assert.h:245
void show_warnings(const string &helpfile)
char(& yes)[1]
Definition: downcast.h:30
fflush(stdout)
#define true
Definition: ureadseq.h:14
#define false
Definition: ureadseq.h:13
fputc('\n', stderr)
fputs(TRACE_PREFIX, stderr)
static list< LineAttachedMessage > warnings
static ARB_init_perl_interface init
Definition: ARB_ext.c:101
#define NULp
Definition: cxxforward.h:97