Next pass through the framework. Also removing boost files since we don't use them...
[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 switch (sig)
142 {
143 case SIGSEGV:
144 case SIGINT:
145 case SIGABRT:
146 std::cerr << std::endl << "Signal handling thread got signal " << strsignal(sig) << std::endl;
147 __shutdown= SHUTDOWN_FORCED;
148 break;
149
150 default:
151 std::cerr << std::endl << "Signal handling thread got unexpected signal " << strsignal(sig) << std::endl;
152 case SIGUSR1:
153 break;
154 }
155 }
156
157 return NULL;
158 }
159
160
161 static void setup_signals(pthread_t& thread)
162 {
163 sigset_t set;
164
165 sigemptyset(&set);
166 sigaddset(&set, SIGSEGV);
167 sigaddset(&set, SIGABRT);
168 sigaddset(&set, SIGINT);
169 sigaddset(&set, SIGUSR1);
170
171 int error;
172 if ((error= pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0)
173 {
174 std::cerr << __FILE__ << ":" << __LINE__ << " died during pthread_sigmask(" << strerror(error) << ")" << std::endl;
175 exit(EXIT_FAILURE);
176 }
177
178 if ((error= pthread_create(&thread, NULL, &sig_thread, (void *) &set)) != 0)
179 {
180 std::cerr << __FILE__ << ":" << __LINE__ << " died during pthread_create(" << strerror(error) << ")" << std::endl;
181 exit(EXIT_FAILURE);
182 }
183 }
184
185
186 int main(int argc, char *argv[])
187 {
188 world= new Framework();
189
190 if (not world)
191 {
192 return EXIT_FAILURE;
193 }
194
195 pthread_t thread;
196 setup_signals(thread);
197
198 Stats stats;
199
200 get_world(world);
201
202 test_return_t error;
203 void *creators_ptr= world->create(error);
204 if (test_failed(error))
205 {
206 std::cerr << "create() failed" << std::endl;
207 return EXIT_FAILURE;
208 }
209
210 char *collection_to_run= NULL;
211 if (argc > 1)
212 {
213 collection_to_run= argv[1];
214 }
215 else if (getenv("TEST_COLLECTION"))
216 {
217 collection_to_run= getenv("TEST_COLLECTION");
218 }
219
220 if (collection_to_run)
221 {
222 std::cout << "Only testing " << collection_to_run << std::endl;
223 }
224
225 char *wildcard= NULL;
226 if (argc == 3)
227 {
228 wildcard= argv[2];
229 }
230
231 for (collection_st *next= world->collections; next->name and __shutdown == SHUTDOWN_RUNNING; next++)
232 {
233 test_return_t collection_rc= TEST_SUCCESS;
234 bool failed= false;
235 bool skipped= false;
236
237 if (collection_to_run && fnmatch(collection_to_run, next->name, 0))
238 continue;
239
240 stats.collection_total++;
241
242 collection_rc= world->startup(creators_ptr);
243
244 if (collection_rc == TEST_SUCCESS and next->pre)
245 {
246 collection_rc= world->runner->pre(next->pre, creators_ptr);
247 }
248
249 switch (collection_rc)
250 {
251 case TEST_SUCCESS:
252 std::cerr << std::endl << next->name << std::endl << std::endl;
253 break;
254
255 case TEST_FATAL:
256 case TEST_FAILURE:
257 std::cerr << std::endl << next->name << " [ failed ]" << std::endl << std::endl;
258 stats.collection_failed++;
259 goto cleanup;
260
261 case TEST_SKIPPED:
262 std::cerr << std::endl << next->name << " [ skipping ]" << std::endl << std::endl;
263 stats.collection_skipped++;
264 goto cleanup;
265
266 case TEST_MEMORY_ALLOCATION_FAILURE:
267 test_assert(0, "Allocation failure, or unknown return");
268 }
269
270 for (test_st *run= next->tests; run->name; run++)
271 {
272 struct timeval start_time, end_time;
273 long int load_time= 0;
274
275 if (wildcard && fnmatch(wildcard, run->name, 0))
276 {
277 continue;
278 }
279
280 std::cerr << "\tTesting " << run->name;
281
282 test_return_t return_code;
283 if (test_success(return_code= world->item.startup(creators_ptr)))
284 {
285 if (test_success(return_code= world->item.flush(creators_ptr, run)))
286 {
287 // @note pre will fail is SKIPPED is returned
288 if (test_success(return_code= world->item.pre(creators_ptr)))
289 {
290 { // Runner Code
291 gettimeofday(&start_time, NULL);
292 return_code= world->runner->run(run->test_fn, creators_ptr);
293 gettimeofday(&end_time, NULL);
294 load_time= timedif(end_time, start_time);
295 }
296 }
297
298 // @todo do something if post fails
299 (void)world->item.post(creators_ptr);
300 }
301 else
302 {
303 std::cerr << __FILE__ << ":" << __LINE__ << " item.flush(failure)" << std::endl;
304 }
305 }
306 else
307 {
308 std::cerr << __FILE__ << ":" << __LINE__ << " item.startup(failure)" << std::endl;
309 }
310
311 stats.total++;
312
313 std::cerr << "\t\t\t\t\t";
314
315 switch (return_code)
316 {
317 case TEST_SUCCESS:
318 std::cerr << load_time / 1000 << "." << load_time % 1000;
319 stats.success++;
320 break;
321
322 case TEST_FATAL:
323 case TEST_FAILURE:
324 stats.failed++;
325 failed= true;
326 break;
327
328 case TEST_SKIPPED:
329 stats.skipped++;
330 skipped= true;
331 break;
332
333 case TEST_MEMORY_ALLOCATION_FAILURE:
334 test_assert(0, "Memory Allocation Error");
335 }
336
337 std::cerr << "[ " << test_strerror(return_code) << " ]" << std::endl;
338
339 if (test_failed(world->on_error(return_code, creators_ptr)))
340 {
341 break;
342 }
343 }
344
345 if (next->post and world->runner->post)
346 {
347 (void) world->runner->post(next->post, creators_ptr);
348 }
349
350 if (failed == 0 and skipped == 0)
351 {
352 stats.collection_success++;
353 }
354 cleanup:
355
356 world->shutdown(creators_ptr);
357 }
358
359 if (__shutdown == SHUTDOWN_RUNNING)
360 {
361 __shutdown= SHUTDOWN_GRACEFUL;
362 }
363
364 if (__shutdown == SHUTDOWN_FORCED)
365 {
366 std::cerr << std::endl << std::endl << "Tests were aborted." << std::endl << std::endl;
367 }
368 else if (stats.collection_failed or stats.collection_skipped)
369 {
370 std::cerr << std::endl << std::endl << "Some test failures and/or skipped test occurred." << std::endl << std::endl;
371 }
372 else
373 {
374 std::cout << std::endl << std::endl << "All tests completed successfully." << std::endl << std::endl;
375 }
376
377 stats_print(&stats);
378
379 void *retval;
380 pthread_kill(thread, SIGUSR1);
381 pthread_join(thread, &retval);
382
383 delete world;
384
385 return stats.failed == 0 and __shutdown == SHUTDOWN_GRACEFUL ? EXIT_SUCCESS : EXIT_FAILURE;
386 }