Merge in basic unittest for test framework.
[awesomized/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 bool test_is_local()
55 {
56 return (getenv("LIBTEST_LOCAL"));
57 }
58
59 void set_default_socket(const char *socket)
60 {
61 strncpy(global_socket, socket, strlen(socket));
62 }
63
64 static void stats_print(Stats *stats)
65 {
66 std::cout << "\tTotal Collections\t\t\t\t" << stats->collection_total << std::endl;
67 std::cout << "\tFailed Collections\t\t\t\t" << stats->collection_failed << std::endl;
68 std::cout << "\tSkipped Collections\t\t\t\t" << stats->collection_skipped << std::endl;
69 std::cout << "\tSucceeded Collections\t\t\t\t" << stats->collection_success << std::endl;
70 std::cout << std::endl;
71 std::cout << "Total\t\t\t\t" << stats->total << std::endl;
72 std::cout << "\tFailed\t\t\t" << stats->failed << std::endl;
73 std::cout << "\tSkipped\t\t\t" << stats->skipped << std::endl;
74 std::cout << "\tSucceeded\t\t" << stats->success << std::endl;
75 }
76
77 static long int timedif(struct timeval a, struct timeval b)
78 {
79 long us, s;
80
81 us = (long)(a.tv_usec - b.tv_usec);
82 us /= 1000;
83 s = (long)(a.tv_sec - b.tv_sec);
84 s *= 1000;
85 return s + us;
86 }
87
88 const char *test_strerror(test_return_t code)
89 {
90 switch (code) {
91 case TEST_SUCCESS:
92 return "ok";
93
94 case TEST_FAILURE:
95 return "failed";
96
97 case TEST_MEMORY_ALLOCATION_FAILURE:
98 return "memory allocation";
99
100 case TEST_SKIPPED:
101 return "skipped";
102
103 case TEST_FATAL:
104 break;
105 }
106
107 return "failed";
108 }
109
110 void create_core(void)
111 {
112 if (getenv("LIBMEMCACHED_NO_COREDUMP") == NULL)
113 {
114 pid_t pid= fork();
115
116 if (pid == 0)
117 {
118 abort();
119 }
120 else
121 {
122 while (waitpid(pid, NULL, 0) != pid) {};
123 }
124 }
125 }
126
127 enum shutdown_t {
128 SHUTDOWN_RUNNING,
129 SHUTDOWN_GRACEFUL,
130 SHUTDOWN_FORCED
131 };
132
133 static Framework *world= NULL;
134 static volatile shutdown_t __shutdown= SHUTDOWN_RUNNING;
135
136 static void *sig_thread(void *arg)
137 {
138 sigset_t *set= (sigset_t *) arg;
139
140 for (;__shutdown == SHUTDOWN_RUNNING;)
141 {
142 int sig;
143 int error;
144 while ((error= sigwait(set, &sig)) == EINTR) ;
145
146 switch (sig)
147 {
148 case SIGSEGV:
149 case SIGINT:
150 case SIGABRT:
151 std::cerr << std::endl << "Signal handling thread got signal " << strsignal(sig) << std::endl;
152 __shutdown= SHUTDOWN_FORCED;
153 break;
154
155 default:
156 std::cerr << std::endl << "Signal handling thread got unexpected signal " << strsignal(sig) << std::endl;
157 case SIGUSR1:
158 break;
159 }
160 }
161
162 return NULL;
163 }
164
165
166 static void setup_signals(pthread_t& thread)
167 {
168 sigset_t set;
169
170 sigemptyset(&set);
171 sigaddset(&set, SIGSEGV);
172 sigaddset(&set, SIGABRT);
173 sigaddset(&set, SIGINT);
174 sigaddset(&set, SIGUSR1);
175
176 int error;
177 if ((error= pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0)
178 {
179 std::cerr << __FILE__ << ":" << __LINE__ << " died during pthread_sigmask(" << strerror(error) << ")" << std::endl;
180 exit(EXIT_FAILURE);
181 }
182
183 if ((error= pthread_create(&thread, NULL, &sig_thread, (void *) &set)) != 0)
184 {
185 std::cerr << __FILE__ << ":" << __LINE__ << " died during pthread_create(" << strerror(error) << ")" << std::endl;
186 exit(EXIT_FAILURE);
187 }
188 }
189
190
191 int main(int argc, char *argv[])
192 {
193 world= new Framework();
194
195 if (not world)
196 {
197 return EXIT_FAILURE;
198 }
199
200 pthread_t thread;
201 setup_signals(thread);
202
203 Stats stats;
204
205 get_world(world);
206
207 test_return_t error;
208 void *creators_ptr= world->create(error);
209 if (test_failed(error))
210 {
211 std::cerr << "create() failed" << std::endl;
212 return EXIT_FAILURE;
213 }
214
215 char *collection_to_run= NULL;
216 if (argc > 1)
217 {
218 collection_to_run= argv[1];
219 }
220 else if (getenv("TEST_COLLECTION"))
221 {
222 collection_to_run= getenv("TEST_COLLECTION");
223 }
224
225 if (collection_to_run)
226 {
227 std::cout << "Only testing " << collection_to_run << std::endl;
228 }
229
230 char *wildcard= NULL;
231 if (argc == 3)
232 {
233 wildcard= argv[2];
234 }
235
236 for (collection_st *next= world->collections; next->name and __shutdown == SHUTDOWN_RUNNING; next++)
237 {
238 test_return_t collection_rc= TEST_SUCCESS;
239 bool failed= false;
240 bool skipped= false;
241
242 if (collection_to_run && fnmatch(collection_to_run, next->name, 0))
243 continue;
244
245 stats.collection_total++;
246
247 collection_rc= world->startup(creators_ptr);
248
249 if (collection_rc == TEST_SUCCESS and next->pre)
250 {
251 collection_rc= world->runner->pre(next->pre, creators_ptr);
252 }
253
254 switch (collection_rc)
255 {
256 case TEST_SUCCESS:
257 std::cerr << std::endl << next->name << std::endl << std::endl;
258 break;
259
260 case TEST_FATAL:
261 case TEST_FAILURE:
262 std::cerr << std::endl << next->name << " [ failed ]" << std::endl << std::endl;
263 stats.collection_failed++;
264 goto cleanup;
265
266 case TEST_SKIPPED:
267 std::cerr << std::endl << next->name << " [ skipping ]" << std::endl << std::endl;
268 stats.collection_skipped++;
269 goto cleanup;
270
271 case TEST_MEMORY_ALLOCATION_FAILURE:
272 test_assert(0, "Allocation failure, or unknown return");
273 }
274
275 for (test_st *run= next->tests; run->name; run++)
276 {
277 struct timeval start_time, end_time;
278 long int load_time= 0;
279
280 if (wildcard && fnmatch(wildcard, run->name, 0))
281 {
282 continue;
283 }
284
285 std::cerr << "\tTesting " << run->name;
286
287 test_return_t return_code;
288 if (test_success(return_code= world->item.startup(creators_ptr)))
289 {
290 if (test_success(return_code= world->item.flush(creators_ptr, run)))
291 {
292 // @note pre will fail is SKIPPED is returned
293 if (test_success(return_code= world->item.pre(creators_ptr)))
294 {
295 { // Runner Code
296 gettimeofday(&start_time, NULL);
297 return_code= world->runner->run(run->test_fn, creators_ptr);
298 gettimeofday(&end_time, NULL);
299 load_time= timedif(end_time, start_time);
300 }
301 }
302
303 // @todo do something if post fails
304 (void)world->item.post(creators_ptr);
305 }
306 else
307 {
308 std::cerr << __FILE__ << ":" << __LINE__ << " item.flush(failure)" << std::endl;
309 }
310 }
311 else
312 {
313 std::cerr << __FILE__ << ":" << __LINE__ << " item.startup(failure)" << std::endl;
314 }
315
316 stats.total++;
317
318 std::cerr << "\t\t\t\t\t";
319
320 switch (return_code)
321 {
322 case TEST_SUCCESS:
323 std::cerr << load_time / 1000 << "." << load_time % 1000;
324 stats.success++;
325 break;
326
327 case TEST_FATAL:
328 case TEST_FAILURE:
329 stats.failed++;
330 failed= true;
331 break;
332
333 case TEST_SKIPPED:
334 stats.skipped++;
335 skipped= true;
336 break;
337
338 case TEST_MEMORY_ALLOCATION_FAILURE:
339 test_assert(0, "Memory Allocation Error");
340 }
341
342 std::cerr << "[ " << test_strerror(return_code) << " ]" << std::endl;
343
344 if (test_failed(world->on_error(return_code, creators_ptr)))
345 {
346 break;
347 }
348 }
349
350 if (next->post and world->runner->post)
351 {
352 (void) world->runner->post(next->post, creators_ptr);
353 }
354
355 if (failed == 0 and skipped == 0)
356 {
357 stats.collection_success++;
358 }
359 cleanup:
360
361 world->shutdown(creators_ptr);
362 }
363
364 if (__shutdown == SHUTDOWN_RUNNING)
365 {
366 __shutdown= SHUTDOWN_GRACEFUL;
367 }
368
369 if (__shutdown == SHUTDOWN_FORCED)
370 {
371 std::cerr << std::endl << std::endl << "Tests were aborted." << std::endl << std::endl;
372 }
373 else if (stats.collection_failed or stats.collection_skipped)
374 {
375 std::cerr << std::endl << std::endl << "Some test failures and/or skipped test occurred." << std::endl << std::endl;
376 }
377 else
378 {
379 std::cout << std::endl << std::endl << "All tests completed successfully." << std::endl << std::endl;
380 }
381
382 stats_print(&stats);
383
384 void *retval;
385 pthread_kill(thread, SIGUSR1);
386 pthread_join(thread, &retval);
387
388 delete world;
389
390 return stats.failed == 0 and __shutdown == SHUTDOWN_GRACEFUL ? EXIT_SUCCESS : EXIT_FAILURE;
391 }