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 // marker for mock objects (i.e. fake objects simulating partial behavior)
79 typedef enum { UNITTEST_MOCK } UnittestMock;
80 
81 namespace 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),
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
#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:116