Fix test system to error correctly on interrupt/shutdown servers.
[m6w6/libmemcached] / libtest / test.cc
1 /* uTest
2 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
3 * Copyright (C) 2006-2009 Brian Aker
4 * All rights reserved.
5 *
6 * Use and distribution licensed under the BSD license. See
7 * the COPYING file in the parent directory for full text.
8 */
9
10
11 #include <libtest/common.h>
12
13 #include <cassert>
14 #include <cstdlib>
15 #include <cstring>
16 #include <sys/time.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21 #include <ctime>
22 #include <fnmatch.h>
23 #include <iostream>
24 #include <cerrno>
25
26 #include <signal.h>
27
28 #include <libtest/stats.h>
29
30 #ifndef __INTEL_COMPILER
31 #pragma GCC diagnostic ignored "-Wold-style-cast"
32 #endif
33
34 static in_port_t global_port= 0;
35 static char global_socket[1024];
36
37 in_port_t default_port()
38 {
39 assert(global_port);
40 return global_port;
41 }
42
43 void set_default_port(in_port_t port)
44 {
45 global_port= port;
46 }
47
48 const char *default_socket()
49 {
50 assert(global_socket[0]);
51 return global_socket;
52 }
53
54 void set_default_socket(const char *socket)
55 {
56 strncpy(global_socket, socket, strlen(socket));
57 }
58
59 static void stats_print(Stats *stats)
60 {
61 std::cout << "\tTotal Collections\t\t\t\t" << stats->collection_total << std::endl;
62 std::cout << "\tFailed Collections\t\t\t\t" << stats->collection_failed << std::endl;
63 std::cout << "\tSkipped Collections\t\t\t\t" << stats->collection_skipped << std::endl;
64 std::cout << "\tSucceeded Collections\t\t\t\t" << stats->collection_success << std::endl;
65 std::cout << std::endl;
66 std::cout << "Total\t\t\t\t" << stats->total << std::endl;
67 std::cout << "\tFailed\t\t\t" << stats->failed << std::endl;
68 std::cout << "\tSkipped\t\t\t" << stats->skipped << std::endl;
69 std::cout << "\tSucceeded\t\t" << stats->success << std::endl;
70 }
71
72 static long int timedif(struct timeval a, struct timeval b)
73 {
74 long us, s;
75
76 us = (long)(a.tv_usec - b.tv_usec);
77 us /= 1000;
78 s = (long)(a.tv_sec - b.tv_sec);
79 s *= 1000;
80 return s + us;
81 }
82
83 const char *test_strerror(test_return_t code)
84 {
85 switch (code) {
86 case TEST_SUCCESS:
87 return "ok";
88
89 case TEST_FAILURE:
90 return "failed";
91
92 case TEST_MEMORY_ALLOCATION_FAILURE:
93 return "memory allocation";
94
95 case TEST_SKIPPED:
96 return "skipped";
97
98 case TEST_FATAL:
99 break;
100 }
101
102 return "failed";
103 }
104
105 void create_core(void)
106 {
107 if (getenv("LIBMEMCACHED_NO_COREDUMP") == NULL)
108 {
109 pid_t pid= fork();
110
111 if (pid == 0)
112 {
113 abort();
114 }
115 else
116 {
117 while (waitpid(pid, NULL, 0) != pid) {};
118 }
119 }
120 }
121
122 enum shutdown_t {
123 SHUTDOWN_RUNNING,
124 SHUTDOWN_GRACEFUL,
125 SHUTDOWN_FORCED
126 };
127
128 static Framework *world= NULL;
129 static volatile shutdown_t __shutdown= SHUTDOWN_RUNNING;
130
131 static void *sig_thread(void *arg)
132 {
133 sigset_t *set= (sigset_t *) arg;
134
135 for (;__shutdown == SHUTDOWN_RUNNING;)
136 {
137 int sig;
138 int error;
139 while ((error= sigwait(set, &sig)) == EINTR) ;
140
141 std::cerr << std::endl << "Signal handling thread got signal " << strsignal(sig) << std::endl;
142 switch (sig)
143 {
144 case SIGSEGV:
145 case SIGINT:
146 case SIGABRT:
147 __shutdown= SHUTDOWN_FORCED;
148
149 default:
150 break;
151 }
152 }
153
154 return NULL;
155 }
156
157
158 static void setup_signals(pthread_t& thread)
159 {
160 sigset_t set;
161
162 sigemptyset(&set);
163 sigaddset(&set, SIGSEGV);
164 sigaddset(&set, SIGABRT);
165 sigaddset(&set, SIGINT);
166
167 int error;
168 if ((error= pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0)
169 {
170 std::cerr << __FILE__ << ":" << __LINE__ << " died during pthread_sigmask(" << strerror(error) << ")" << std::endl;
171 exit(EXIT_FAILURE);
172 }
173
174 if ((error= pthread_create(&thread, NULL, &sig_thread, (void *) &set)) != 0)
175 {
176 std::cerr << __FILE__ << ":" << __LINE__ << " died during pthread_create(" << strerror(error) << ")" << std::endl;
177 exit(EXIT_FAILURE);
178 }
179 }
180
181
182 int main(int argc, char *argv[])
183 {
184 world= new Framework();
185
186 if (not world)
187 {
188 return EXIT_FAILURE;
189 }
190
191 pthread_t thread;
192 setup_signals(thread);
193
194 Stats stats;
195
196 get_world(world);
197
198 test_return_t error;
199 void *creators_ptr= world->create(&error);
200 if (test_failed(error))
201 {
202 std::cerr << "create() failed" << std::endl;
203 return EXIT_FAILURE;
204 }
205
206 char *collection_to_run= NULL;
207 if (argc > 1)
208 {
209 collection_to_run= argv[1];
210 }
211 else if (getenv("TEST_COLLECTION"))
212 {
213 collection_to_run= getenv("TEST_COLLECTION");
214 }
215
216 if (collection_to_run)
217 {
218 std::cout << "Only testing " << collection_to_run << std::endl;
219 }
220
221 char *wildcard= NULL;
222 if (argc == 3)
223 {
224 wildcard= argv[2];
225 }
226
227 for (collection_st *next= world->collections; next->name and __shutdown == SHUTDOWN_RUNNING; next++)
228 {
229 test_return_t collection_rc= TEST_SUCCESS;
230 bool failed= false;
231 bool skipped= false;
232
233 if (collection_to_run && fnmatch(collection_to_run, next->name, 0))
234 continue;
235
236 stats.collection_total++;
237
238 collection_rc= world->startup(creators_ptr);
239
240 if (collection_rc == TEST_SUCCESS and next->pre)
241 {
242 collection_rc= world->runner->pre(next->pre, creators_ptr);
243 }
244
245 switch (collection_rc)
246 {
247 case TEST_SUCCESS:
248 std::cerr << std::endl << next->name << std::endl << std::endl;
249 break;
250
251 case TEST_FATAL:
252 case TEST_FAILURE:
253 std::cerr << std::endl << next->name << " [ failed ]" << std::endl << std::endl;
254 stats.collection_failed++;
255 goto cleanup;
256
257 case TEST_SKIPPED:
258 std::cerr << std::endl << next->name << " [ skipping ]" << std::endl << std::endl;
259 stats.collection_skipped++;
260 goto cleanup;
261
262 case TEST_MEMORY_ALLOCATION_FAILURE:
263 test_assert(0, "Allocation failure, or unknown return");
264 }
265
266 for (test_st *run= next->tests; run->name; run++)
267 {
268 struct timeval start_time, end_time;
269 long int load_time= 0;
270
271 if (wildcard && fnmatch(wildcard, run->name, 0))
272 {
273 continue;
274 }
275
276 std::cerr << "\tTesting " << run->name;
277
278 test_return_t return_code;
279 if (test_success(return_code= world->item.startup(creators_ptr)))
280 {
281 if (test_success(return_code= world->item.flush(creators_ptr, run)))
282 {
283 // @note pre will fail is SKIPPED is returned
284 if (test_success(return_code= world->item.pre(creators_ptr)))
285 {
286 { // Runner Code
287 gettimeofday(&start_time, NULL);
288 return_code= world->runner->run(run->test_fn, creators_ptr);
289 gettimeofday(&end_time, NULL);
290 load_time= timedif(end_time, start_time);
291 }
292 }
293
294 // @todo do something if post fails
295 (void)world->item.post(creators_ptr);
296 }
297 else
298 {
299 std::cerr << __FILE__ << ":" << __LINE__ << " item.flush(failure)" << std::endl;
300 }
301 }
302 else
303 {
304 std::cerr << __FILE__ << ":" << __LINE__ << " item.startup(failure)" << std::endl;
305 }
306
307 stats.total++;
308
309 std::cerr << "\t\t\t\t\t";
310
311 switch (return_code)
312 {
313 case TEST_SUCCESS:
314 std::cerr << load_time / 1000 << "." << load_time % 1000;
315 stats.success++;
316 break;
317
318 case TEST_FATAL:
319 case TEST_FAILURE:
320 stats.failed++;
321 failed= true;
322 break;
323
324 case TEST_SKIPPED:
325 stats.skipped++;
326 skipped= true;
327 break;
328
329 case TEST_MEMORY_ALLOCATION_FAILURE:
330 test_assert(0, "Memory Allocation Error");
331 }
332
333 std::cerr << "[ " << test_strerror(return_code) << " ]" << std::endl;
334
335 if (test_failed(world->on_error(return_code, creators_ptr)))
336 {
337 break;
338 }
339 }
340
341 if (next->post and world->runner->post)
342 {
343 (void) world->runner->post(next->post, creators_ptr);
344 }
345
346 if (failed == 0 and skipped == 0)
347 {
348 stats.collection_success++;
349 }
350 cleanup:
351
352 world->shutdown(creators_ptr);
353 }
354
355 if (__shutdown == SHUTDOWN_RUNNING)
356 {
357 __shutdown= SHUTDOWN_GRACEFUL;
358 }
359
360 if (__shutdown == SHUTDOWN_FORCED)
361 {
362 std::cerr << std::endl << std::endl << "Tests were aborted." << std::endl << std::endl;
363 }
364 else if (stats.collection_failed or stats.collection_skipped)
365 {
366 std::cerr << std::endl << std::endl << "Some test failures and/or skipped test occurred." << std::endl << std::endl;
367 }
368 else
369 {
370 std::cout << std::endl << std::endl << "All tests completed successfully." << std::endl << std::endl;
371 }
372
373 stats_print(&stats);
374
375 delete world;
376
377 return stats.failed == 0 and __shutdown == SHUTDOWN_GRACEFUL ? EXIT_SUCCESS : EXIT_FAILURE;
378 }