Adding additional tests for namespace.
[m6w6/libmemcached] / libtest / test.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * uTest, libtest
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 * Copyright (C) 2006-2009 Brian Aker
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38
39
40
41 #include <libtest/common.h>
42
43 #include <cassert>
44 #include <cstdlib>
45 #include <cstring>
46 #include <sys/time.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/wait.h>
50 #include <unistd.h>
51 #include <ctime>
52 #include <fnmatch.h>
53 #include <iostream>
54
55 #include <signal.h>
56
57 #include <libtest/stats.h>
58
59 #ifndef __INTEL_COMPILER
60 #pragma GCC diagnostic ignored "-Wold-style-cast"
61 #endif
62
63 static in_port_t global_port= 0;
64 static char global_socket[1024];
65
66 in_port_t default_port()
67 {
68 assert(global_port);
69 return global_port;
70 }
71
72 void set_default_port(in_port_t port)
73 {
74 global_port= port;
75 }
76
77 const char *default_socket()
78 {
79 assert(global_socket[0]);
80 return global_socket;
81 }
82
83 bool test_is_local()
84 {
85 return (getenv("LIBTEST_LOCAL"));
86 }
87
88 void set_default_socket(const char *socket)
89 {
90 strncpy(global_socket, socket, strlen(socket));
91 }
92
93 static void stats_print(Stats *stats)
94 {
95 std::cout << "\tTotal Collections\t\t\t\t" << stats->collection_total << std::endl;
96 std::cout << "\tFailed Collections\t\t\t\t" << stats->collection_failed << std::endl;
97 std::cout << "\tSkipped Collections\t\t\t\t" << stats->collection_skipped << std::endl;
98 std::cout << "\tSucceeded Collections\t\t\t\t" << stats->collection_success << std::endl;
99 std::cout << std::endl;
100 std::cout << "Total\t\t\t\t" << stats->total << std::endl;
101 std::cout << "\tFailed\t\t\t" << stats->failed << std::endl;
102 std::cout << "\tSkipped\t\t\t" << stats->skipped << std::endl;
103 std::cout << "\tSucceeded\t\t" << stats->success << std::endl;
104 }
105
106 static long int timedif(struct timeval a, struct timeval b)
107 {
108 long us, s;
109
110 us = (long)(a.tv_usec - b.tv_usec);
111 us /= 1000;
112 s = (long)(a.tv_sec - b.tv_sec);
113 s *= 1000;
114 return s + us;
115 }
116
117 const char *test_strerror(test_return_t code)
118 {
119 switch (code) {
120 case TEST_SUCCESS:
121 return "ok";
122
123 case TEST_FAILURE:
124 return "failed";
125
126 case TEST_MEMORY_ALLOCATION_FAILURE:
127 return "memory allocation";
128
129 case TEST_SKIPPED:
130 return "skipped";
131
132 case TEST_FATAL:
133 break;
134 }
135
136 return "failed";
137 }
138
139 void create_core(void)
140 {
141 if (getenv("LIBMEMCACHED_NO_COREDUMP") == NULL)
142 {
143 pid_t pid= fork();
144
145 if (pid == 0)
146 {
147 abort();
148 }
149 else
150 {
151 while (waitpid(pid, NULL, 0) != pid) {};
152 }
153 }
154 }
155
156 enum shutdown_t {
157 SHUTDOWN_RUNNING,
158 SHUTDOWN_GRACEFUL,
159 SHUTDOWN_FORCED
160 };
161
162 static Framework *world= NULL;
163 static volatile shutdown_t __shutdown= SHUTDOWN_RUNNING;
164 pthread_mutex_t shutdown_mutex= PTHREAD_MUTEX_INITIALIZER;
165
166 static bool is_shutdown()
167 {
168 bool ret;
169 pthread_mutex_lock(&shutdown_mutex);
170 ret= bool(__shutdown != SHUTDOWN_RUNNING);
171 pthread_mutex_unlock(&shutdown_mutex);
172
173 return ret;
174 }
175
176 static void set_shutdown(shutdown_t arg)
177 {
178 pthread_mutex_lock(&shutdown_mutex);
179 __shutdown= arg;
180 pthread_mutex_unlock(&shutdown_mutex);
181 }
182
183 static void *sig_thread(void *arg)
184 {
185 sigset_t *set= (sigset_t *) arg;
186
187 while (is_shutdown())
188 {
189 int sig;
190 int error;
191 while ((error= sigwait(set, &sig)) == EINTR) ;
192
193 switch (sig)
194 {
195 case SIGSEGV:
196 case SIGINT:
197 case SIGABRT:
198 Error << "Signal handling thread got signal " << strsignal(sig);
199 set_shutdown(SHUTDOWN_FORCED);
200 break;
201
202 default:
203 Error << "Signal handling thread got unexpected signal " << strsignal(sig);
204 case SIGUSR1:
205 break;
206 }
207 }
208
209 return NULL;
210 }
211
212
213 static void setup_signals(pthread_t& thread)
214 {
215 sigset_t set;
216
217 sigemptyset(&set);
218 sigaddset(&set, SIGSEGV);
219 sigaddset(&set, SIGABRT);
220 sigaddset(&set, SIGINT);
221 sigaddset(&set, SIGUSR1);
222
223 int error;
224 if ((error= pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0)
225 {
226 Error << " died during pthread_sigmask(" << strerror(error) << ")";
227 exit(EXIT_FAILURE);
228 }
229
230 if ((error= pthread_create(&thread, NULL, &sig_thread, (void *) &set)) != 0)
231 {
232 Error << " died during pthread_create(" << strerror(error) << ")";
233 exit(EXIT_FAILURE);
234 }
235 }
236
237
238 int main(int argc, char *argv[])
239 {
240 world= new Framework();
241
242 if (not world)
243 {
244 return EXIT_FAILURE;
245 }
246
247 pthread_t thread;
248 setup_signals(thread);
249
250 Stats stats;
251
252 get_world(world);
253
254 test_return_t error;
255 void *creators_ptr= world->create(error);
256 if (test_failed(error))
257 {
258 Error << "create() failed";
259 return EXIT_FAILURE;
260 }
261
262 char *collection_to_run= NULL;
263 if (argc > 1)
264 {
265 collection_to_run= argv[1];
266 }
267 else if (getenv("TEST_COLLECTION"))
268 {
269 collection_to_run= getenv("TEST_COLLECTION");
270 }
271
272 if (collection_to_run)
273 {
274 std::cout << "Only testing " << collection_to_run << std::endl;
275 }
276
277 char *wildcard= NULL;
278 if (argc == 3)
279 {
280 wildcard= argv[2];
281 }
282
283 for (collection_st *next= world->collections; next->name and (not is_shutdown()); next++)
284 {
285 test_return_t collection_rc= TEST_SUCCESS;
286 bool failed= false;
287 bool skipped= false;
288
289 if (collection_to_run && fnmatch(collection_to_run, next->name, 0))
290 continue;
291
292 stats.collection_total++;
293
294 collection_rc= world->startup(creators_ptr);
295
296 if (collection_rc == TEST_SUCCESS and next->pre)
297 {
298 collection_rc= world->runner->pre(next->pre, creators_ptr);
299 }
300
301 switch (collection_rc)
302 {
303 case TEST_SUCCESS:
304 break;
305
306 case TEST_FATAL:
307 case TEST_FAILURE:
308 Error << next->name << " [ failed ]";
309 stats.collection_failed++;
310 goto cleanup;
311
312 case TEST_SKIPPED:
313 Log << next->name << " [ skipping ]";
314 stats.collection_skipped++;
315 goto cleanup;
316
317 case TEST_MEMORY_ALLOCATION_FAILURE:
318 test_assert(0, "Allocation failure, or unknown return");
319 }
320
321 Log << "Collection: " << next->name;
322
323 for (test_st *run= next->tests; run->name; run++)
324 {
325 struct timeval start_time, end_time;
326 long int load_time= 0;
327
328 if (wildcard && fnmatch(wildcard, run->name, 0))
329 {
330 continue;
331 }
332
333 test_return_t return_code;
334 if (test_success(return_code= world->item.startup(creators_ptr)))
335 {
336 if (test_success(return_code= world->item.flush(creators_ptr, run)))
337 {
338 // @note pre will fail is SKIPPED is returned
339 if (test_success(return_code= world->item.pre(creators_ptr)))
340 {
341 { // Runner Code
342 gettimeofday(&start_time, NULL);
343 return_code= world->runner->run(run->test_fn, creators_ptr);
344 gettimeofday(&end_time, NULL);
345 load_time= timedif(end_time, start_time);
346 }
347 }
348
349 // @todo do something if post fails
350 (void)world->item.post(creators_ptr);
351 }
352 else
353 {
354 Error << " item.flush(failure)";
355 }
356 }
357 else
358 {
359 Error << " item.startup(failure)";
360 }
361
362 stats.total++;
363
364 switch (return_code)
365 {
366 case TEST_SUCCESS:
367 Log << "\tTesting " << run->name << "\t\t\t\t\t" << load_time / 1000 << "." << load_time % 1000 << "[ " << test_strerror(return_code) << " ]";
368 stats.success++;
369 break;
370
371 case TEST_FATAL:
372 case TEST_FAILURE:
373 stats.failed++;
374 failed= true;
375 Error << "\tTesting " << run->name << "\t\t\t\t\t" << "[ " << test_strerror(return_code) << " ]";
376 break;
377
378 case TEST_SKIPPED:
379 stats.skipped++;
380 skipped= true;
381 Log << "\tTesting " << run->name << "\t\t\t\t\t" << "[ " << test_strerror(return_code) << " ]";
382 break;
383
384 case TEST_MEMORY_ALLOCATION_FAILURE:
385 test_assert(0, "Memory Allocation Error");
386 }
387
388 if (test_failed(world->on_error(return_code, creators_ptr)))
389 {
390 Error << "Failed while running on_error()";
391 break;
392 }
393 }
394
395 if (next->post and world->runner->post)
396 {
397 (void) world->runner->post(next->post, creators_ptr);
398 }
399
400 if (failed == 0 and skipped == 0)
401 {
402 stats.collection_success++;
403 }
404 cleanup:
405
406 world->shutdown(creators_ptr);
407 Logn();
408 }
409
410 if (not is_shutdown())
411 {
412 set_shutdown(SHUTDOWN_GRACEFUL);
413 pthread_kill(thread, SIGUSR1);
414 }
415
416 if (__shutdown == SHUTDOWN_FORCED)
417 {
418 Error << "Tests were aborted.";
419 }
420 else if (stats.collection_failed or stats.collection_skipped)
421 {
422 Error << "Some test failures and/or skipped test occurred.";
423 }
424 else
425 {
426 Log << "All tests completed successfully.";
427 }
428
429 stats_print(&stats);
430
431 void *retval;
432 pthread_join(thread, &retval);
433
434 delete world;
435
436 return stats.failed == 0 and __shutdown == SHUTDOWN_GRACEFUL ? EXIT_SUCCESS : EXIT_FAILURE;
437 }