OSX fix
[awesomized/libmemcached] / libtest / test.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * libtest
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 3 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include <libtest/common.h>
23
24 #include <cassert>
25 #include <cstdlib>
26 #include <cstring>
27 #include <sys/time.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #include <unistd.h>
32 #include <ctime>
33 #include <fnmatch.h>
34 #include <iostream>
35
36 #include <signal.h>
37
38 #if defined(HAVE_CURL_CURL_H) && HAVE_CURL_CURL_H
39 #include <curl/curl.h>
40 #endif
41
42 #ifndef __INTEL_COMPILER
43 #pragma GCC diagnostic ignored "-Wold-style-cast"
44 #endif
45
46 using namespace libtest;
47
48 static void stats_print(Stats *stats)
49 {
50 if (stats->collection_failed == 0 and stats->collection_success == 0)
51 {
52 return;
53 }
54
55 Out << "\tTotal Collections\t\t\t\t" << stats->collection_total;
56 Out << "\tFailed Collections\t\t\t\t" << stats->collection_failed;
57 Out << "\tSkipped Collections\t\t\t\t" << stats->collection_skipped;
58 Out << "\tSucceeded Collections\t\t\t\t" << stats->collection_success;
59 Outn();
60 Out << "Total\t\t\t\t" << stats->total;
61 Out << "\tFailed\t\t\t" << stats->failed;
62 Out << "\tSkipped\t\t\t" << stats->skipped;
63 Out << "\tSucceeded\t\t" << stats->success;
64 }
65
66 static long int timedif(struct timeval a, struct timeval b)
67 {
68 long us, s;
69
70 us = (long)(a.tv_usec - b.tv_usec);
71 us /= 1000;
72 s = (long)(a.tv_sec - b.tv_sec);
73 s *= 1000;
74 return s + us;
75 }
76
77 static void cleanup_curl(void)
78 {
79 #if defined(HAVE_CURL_CURL_H) && HAVE_CURL_CURL_H
80 curl_global_cleanup();
81 #endif
82 }
83
84 #include <getopt.h>
85 #include <unistd.h>
86
87 int main(int argc, char *argv[])
88 {
89 #if defined(HAVE_CURL_CURL_H) && HAVE_CURL_CURL_H
90 if (curl_global_init(CURL_GLOBAL_ALL))
91 {
92 Error << "curl_global_init(CURL_GLOBAL_ALL) failed";
93 return EXIT_FAILURE;
94 }
95 #endif
96
97 if (atexit(cleanup_curl))
98 {
99 Error << "atexit() failed";
100 return EXIT_FAILURE;
101 }
102
103 bool opt_repeat= false;
104 std::string collection_to_run;
105
106 // Options parsing
107 {
108 enum long_option_t {
109 OPT_LIBYATL_VERSION,
110 OPT_LIBYATL_MATCH_COLLECTION,
111 OPT_LIBYATL_REPEAT
112 };
113
114 static struct option long_options[]=
115 {
116 {"repeat", no_argument, NULL, OPT_LIBYATL_REPEAT},
117 {"collection", required_argument, NULL, OPT_LIBYATL_MATCH_COLLECTION},
118 {0, 0, 0, 0}
119 };
120
121 int option_index= 0;
122 while (1)
123 {
124 int option_rv= getopt_long(argc, argv, "", long_options, &option_index);
125 if (option_rv == -1)
126 {
127 break;
128 }
129
130 switch (option_rv)
131 {
132 case OPT_LIBYATL_VERSION:
133 break;
134
135 case OPT_LIBYATL_REPEAT:
136 opt_repeat= true;
137 break;
138
139 case OPT_LIBYATL_MATCH_COLLECTION:
140 collection_to_run= optarg;
141 break;
142
143 case '?':
144 /* getopt_long already printed an error message. */
145 Error << "unknown option to getopt_long()";
146 exit(EXIT_FAILURE);
147
148 default:
149 break;
150 }
151 }
152 }
153
154 srandom((unsigned int)time(NULL));
155
156 if (getenv("LIBTEST_QUIET") and strcmp(getenv("LIBTEST_QUIET"), "0") == 0)
157 {
158 close(STDOUT_FILENO);
159 }
160 else if (getenv("JENKINS_URL"))
161 {
162 close(STDOUT_FILENO);
163 }
164
165 char buffer[1024];
166 if (getenv("LIBTEST_TMP"))
167 {
168 snprintf(buffer, sizeof(buffer), "%s", getenv("LIBTEST_TMP"));
169 }
170 else
171 {
172 snprintf(buffer, sizeof(buffer), "%s", LIBTEST_TEMP);
173 }
174
175 if (chdir(buffer) == -1)
176 {
177 char getcwd_buffer[1024];
178 char *dir= getcwd(getcwd_buffer, sizeof(getcwd_buffer));
179
180 Error << "Unable to chdir() from " << dir << " to " << buffer << " errno:" << strerror(errno);
181 return EXIT_FAILURE;
182 }
183
184 if (libtest::libtool() == NULL)
185 {
186 Error << "Failed to locate libtool";
187 return EXIT_FAILURE;
188 }
189
190 int exit_code;
191
192 try {
193 do {
194 exit_code= EXIT_SUCCESS;
195 Framework *world= new Framework();
196
197 if (world == NULL)
198 {
199 Error << "Failed to create Framework()";
200 return EXIT_FAILURE;
201 }
202
203 assert(sigignore(SIGPIPE) == 0);
204
205 libtest::SignalThread signal;
206 if (not signal.setup())
207 {
208 Error << "Failed to setup signals";
209 return EXIT_FAILURE;
210 }
211
212 Stats stats;
213
214 get_world(world);
215
216 test_return_t error;
217 void *creators_ptr= world->create(error);
218
219 switch (error)
220 {
221 case TEST_SUCCESS:
222 break;
223
224 case TEST_SKIPPED:
225 Out << "SKIP " << argv[0];
226 delete world;
227 return EXIT_SUCCESS;
228
229 case TEST_FAILURE:
230 delete world;
231 return EXIT_FAILURE;
232 }
233
234 if (getenv("TEST_COLLECTION"))
235 {
236 if (strlen(getenv("TEST_COLLECTION")))
237 {
238 collection_to_run= getenv("TEST_COLLECTION");
239 }
240 }
241
242 if (collection_to_run.empty() == false)
243 {
244 Out << "Only testing " << collection_to_run;
245 }
246
247 char *wildcard= NULL;
248 if (argc == 3)
249 {
250 wildcard= argv[2];
251 }
252
253 for (collection_st *next= world->collections; next and next->name and (not signal.is_shutdown()); next++)
254 {
255 bool failed= false;
256 bool skipped= false;
257
258 if (collection_to_run.empty() == false and fnmatch(collection_to_run.c_str(), next->name, 0))
259 {
260 continue;
261 }
262
263 stats.collection_total++;
264
265 test_return_t collection_rc= world->startup(creators_ptr);
266
267 if (collection_rc == TEST_SUCCESS and next->pre)
268 {
269 collection_rc= world->runner()->pre(next->pre, creators_ptr);
270 }
271
272 switch (collection_rc)
273 {
274 case TEST_SUCCESS:
275 break;
276
277 case TEST_FAILURE:
278 Out << next->name << " [ failed ]";
279 failed= true;
280 signal.set_shutdown(SHUTDOWN_GRACEFUL);
281 goto cleanup;
282
283 case TEST_SKIPPED:
284 Out << next->name << " [ skipping ]";
285 skipped= true;
286 goto cleanup;
287
288 default:
289 throw fatal_message("invalid return code");
290 }
291
292 Out << "Collection: " << next->name;
293
294 for (test_st *run= next->tests; run->name; run++)
295 {
296 struct timeval start_time, end_time;
297 long int load_time= 0;
298
299 if (wildcard && fnmatch(wildcard, run->name, 0))
300 {
301 continue;
302 }
303
304 test_return_t return_code;
305 try {
306 if (test_success(return_code= world->item.startup(creators_ptr)))
307 {
308 if (test_success(return_code= world->item.flush(creators_ptr, run)))
309 {
310 // @note pre will fail is SKIPPED is returned
311 if (test_success(return_code= world->item.pre(creators_ptr)))
312 {
313 { // Runner Code
314 gettimeofday(&start_time, NULL);
315 assert(world->runner());
316 assert(run->test_fn);
317 try
318 {
319 return_code= world->runner()->run(run->test_fn, creators_ptr);
320 }
321 // Special case where check for the testing of the exception
322 // system.
323 catch (libtest::fatal &e)
324 {
325 if (fatal::is_disabled())
326 {
327 fatal::increment_disabled_counter();
328 return_code= TEST_SUCCESS;
329 }
330 else
331 {
332 throw;
333 }
334 }
335
336 gettimeofday(&end_time, NULL);
337 load_time= timedif(end_time, start_time);
338 }
339 }
340
341 // @todo do something if post fails
342 (void)world->item.post(creators_ptr);
343 }
344 else if (return_code == TEST_SKIPPED)
345 { }
346 else if (return_code == TEST_FAILURE)
347 {
348 Error << " item.flush(failure)";
349 signal.set_shutdown(SHUTDOWN_GRACEFUL);
350 }
351 }
352 else if (return_code == TEST_SKIPPED)
353 { }
354 else if (return_code == TEST_FAILURE)
355 {
356 Error << " item.startup(failure)";
357 signal.set_shutdown(SHUTDOWN_GRACEFUL);
358 }
359 }
360
361 catch (std::exception &e)
362 {
363 Error << "Exception was thrown: " << e.what();
364 return_code= TEST_FAILURE;
365 }
366 catch (...)
367 {
368 Error << "Unknown exception occurred";
369 return_code= TEST_FAILURE;
370 }
371
372 stats.total++;
373
374 switch (return_code)
375 {
376 case TEST_SUCCESS:
377 Out << "\tTesting " << run->name << "\t\t\t\t\t" << load_time / 1000 << "." << load_time % 1000 << "[ " << test_strerror(return_code) << " ]";
378 stats.success++;
379 break;
380
381 case TEST_FAILURE:
382 stats.failed++;
383 failed= true;
384 Out << "\tTesting " << run->name << "\t\t\t\t\t" << "[ " << test_strerror(return_code) << " ]";
385 break;
386
387 case TEST_SKIPPED:
388 stats.skipped++;
389 skipped= true;
390 Out << "\tTesting " << run->name << "\t\t\t\t\t" << "[ " << test_strerror(return_code) << " ]";
391 break;
392
393 default:
394 throw fatal_message("invalid return code");
395 }
396
397 if (test_failed(world->on_error(return_code, creators_ptr)))
398 {
399 Error << "Failed while running on_error()";
400 signal.set_shutdown(SHUTDOWN_GRACEFUL);
401 break;
402 }
403 }
404
405 (void) world->runner()->post(next->post, creators_ptr);
406
407 cleanup:
408 if (failed == false and skipped == false)
409 {
410 stats.collection_success++;
411 }
412
413 if (failed)
414 {
415 stats.collection_failed++;
416 }
417
418 if (skipped)
419 {
420 stats.collection_skipped++;
421 }
422
423 world->shutdown(creators_ptr);
424 Outn();
425 }
426
427 if (not signal.is_shutdown())
428 {
429 signal.set_shutdown(SHUTDOWN_GRACEFUL);
430 }
431
432 shutdown_t status= signal.get_shutdown();
433 if (status == SHUTDOWN_FORCED)
434 {
435 Out << "Tests were aborted.";
436 exit_code= EXIT_FAILURE;
437 }
438 else if (stats.collection_failed)
439 {
440 Out << "Some test failed.";
441 exit_code= EXIT_FAILURE;
442 }
443 else if (stats.collection_skipped and stats.collection_failed and stats.collection_success)
444 {
445 Out << "Some tests were skipped.";
446 }
447 else if (stats.collection_success and stats.collection_failed == 0)
448 {
449 Out << "All tests completed successfully.";
450 }
451
452 stats_print(&stats);
453
454 delete world;
455
456 Outn(); // Generate a blank to break up the messages if make check/test has been run
457 } while (exit_code == EXIT_SUCCESS and opt_repeat);
458 }
459 catch (libtest::fatal& e)
460 {
461 std::cerr << e.what() << std::endl;
462 }
463 catch (std::bad_alloc& e)
464 {
465 std::cerr << e.what() << std::endl;
466 }
467 catch (...)
468 {
469 std::cerr << "Unknown exception halted execution" << std::endl;
470 }
471
472 return exit_code;
473 }