Refactor test framework to run pre/post just once.
[awesomized/libmemcached] / tests / test.c
1 /* uTest
2 * Copyright (C) 2006-2009 Brian Aker
3 * All rights reserved.
4 *
5 * Use and distribution licensed under the BSD license. See
6 * the COPYING file in the parent directory for full text.
7 */
8
9 /*
10 Sample test application.
11 */
12 #include <assert.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <sys/wait.h>
19 #include <unistd.h>
20 #include <time.h>
21 #include <fnmatch.h>
22 #include <stdint.h>
23
24 #include "test.h"
25
26 static void world_stats_print(world_stats_st *stats)
27 {
28 fputc('\n', stderr);
29 fprintf(stderr, "Total Collections\t\t\t\t%u\n", stats->collection_total);
30 fprintf(stderr, "\tFailed Collections\t\t\t%u\n", stats->collection_failed);
31 fprintf(stderr, "\tSkipped Collections\t\t\t%u\n", stats->collection_skipped);
32 fprintf(stderr, "\tSucceeded Collections\t\t%u\n", stats->collection_success);
33 fputc('\n', stderr);
34 fprintf(stderr, "Total\t\t\t\t%u\n", stats->total);
35 fprintf(stderr, "\tFailed\t\t\t%u\n", stats->failed);
36 fprintf(stderr, "\tSkipped\t\t\t%u\n", stats->skipped);
37 fprintf(stderr, "\tSucceeded\t\t%u\n", stats->success);
38 }
39
40 static long int timedif(struct timeval a, struct timeval b)
41 {
42 long us, s;
43
44 us = (int)(a.tv_usec - b.tv_usec);
45 us /= 1000;
46 s = (int)(a.tv_sec - b.tv_sec);
47 s *= 1000;
48 return s + us;
49 }
50
51 const char *test_strerror(test_return_t code)
52 {
53 switch (code) {
54 case TEST_SUCCESS:
55 return "ok";
56 case TEST_FAILURE:
57 return "failed";
58 case TEST_MEMORY_ALLOCATION_FAILURE:
59 return "memory allocation";
60 case TEST_SKIPPED:
61 return "skipped";
62 case TEST_MAXIMUM_RETURN:
63 default:
64 fprintf(stderr, "Unknown return value\n");
65 abort();
66 }
67 }
68
69 void create_core(void)
70 {
71 if (getenv("LIBMEMCACHED_NO_COREDUMP") == NULL)
72 {
73 pid_t pid= fork();
74
75 if (pid == 0)
76 {
77 abort();
78 }
79 else
80 {
81 while (waitpid(pid, NULL, 0) != pid)
82 {
83 ;
84 }
85 }
86 }
87 }
88
89
90 static test_return_t _runner_default(test_callback_fn func, void *p)
91 {
92 if (func)
93 {
94 return func(p);
95 }
96 else
97 {
98 return TEST_SUCCESS;
99 }
100 }
101
102 static world_runner_st defualt_runners= {
103 _runner_default,
104 _runner_default,
105 _runner_default
106 };
107
108 static test_return_t _default_callback(void *p)
109 {
110 (void)p;
111
112 return TEST_SUCCESS;
113 }
114
115 static inline void set_default_fn(test_callback_fn *fn)
116 {
117 if (*fn == NULL)
118 {
119 *fn= _default_callback;
120 }
121 }
122
123 static collection_st *init_world(world_st *world)
124 {
125 if (! world->runner)
126 {
127 world->runner= &defualt_runners;
128 }
129
130 set_default_fn(&world->collection.startup);
131 set_default_fn(&world->collection.shutdown);
132
133 return world->collections;
134 }
135
136
137 int main(int argc, char *argv[])
138 {
139 test_return_t return_code;
140 unsigned int x;
141 char *collection_to_run= NULL;
142 char *wildcard= NULL;
143 world_st world;
144 collection_st *collection;
145 collection_st *next;
146 void *world_ptr;
147
148 world_stats_st stats;
149
150 memset(&stats, 0, sizeof(stats));
151 memset(&world, 0, sizeof(world));
152 get_world(&world);
153
154 collection= init_world(&world);
155
156 if (world.create)
157 {
158 test_return_t error;
159 world_ptr= world.create(&error);
160 if (error != TEST_SUCCESS)
161 exit(1);
162 }
163 else
164 {
165 world_ptr= NULL;
166 }
167
168 if (argc > 1)
169 collection_to_run= argv[1];
170
171 if (argc == 3)
172 wildcard= argv[2];
173
174 for (next= collection; next->name; next++)
175 {
176 test_return_t collection_rc= TEST_SUCCESS;
177 test_st *run;
178 bool failed= false;
179 bool skipped= false;
180
181 run= next->tests;
182 if (collection_to_run && fnmatch(collection_to_run, next->name, 0))
183 continue;
184
185 stats.collection_total++;
186
187 collection_rc= world.collection.startup(world_ptr);
188
189 if (collection_rc != TEST_SUCCESS)
190 goto skip_pre;
191
192 if (next->pre)
193 {
194 collection_rc= world.runner->pre(next->pre, world_ptr);
195 }
196
197 skip_pre:
198 switch (collection_rc)
199 {
200 case TEST_SUCCESS:
201 fprintf(stderr, "\n%s\n\n", next->name);
202 break;
203 case TEST_FAILURE:
204 fprintf(stderr, "\n%s [ failed ]\n\n", next->name);
205 stats.collection_failed++;
206 goto cleanup;
207 case TEST_SKIPPED:
208 fprintf(stderr, "\n%s [ skipping ]\n\n", next->name);
209 stats.collection_skipped++;
210 goto cleanup;
211 case TEST_MEMORY_ALLOCATION_FAILURE:
212 case TEST_MAXIMUM_RETURN:
213 default:
214 assert(0);
215 break;
216 }
217
218
219 for (x= 0; run->name; run++)
220 {
221 struct timeval start_time, end_time;
222 long int load_time= 0;
223
224 if (wildcard && fnmatch(wildcard, run->name, 0))
225 continue;
226
227 fprintf(stderr, "Testing %s", run->name);
228
229 if (world.test.startup)
230 {
231 world.test.startup(world_ptr);
232 }
233
234 if (run->requires_flush && world.test.flush)
235 {
236 world.test.flush(world_ptr);
237 }
238
239 if (world.test.pre_run)
240 {
241 world.test.pre_run(world_ptr);
242 }
243
244
245 // Runner code
246 {
247 #if 0
248 if (next->pre && world.runner->pre)
249 {
250 return_code= world.runner->pre(next->pre, world_ptr);
251
252 if (return_code != TEST_SUCCESS)
253 {
254 goto error;
255 }
256 }
257 #endif
258
259 gettimeofday(&start_time, NULL);
260 return_code= world.runner->run(run->test_fn, world_ptr);
261 gettimeofday(&end_time, NULL);
262 load_time= timedif(end_time, start_time);
263
264 #if 0
265 if (next->post && world.runner->post)
266 {
267 (void) world.runner->post(next->post, world_ptr);
268 }
269 #endif
270 }
271
272 if (world.test.post_run)
273 {
274 world.test.post_run(world_ptr);
275 }
276
277 stats.total++;
278
279 fprintf(stderr, "\t\t\t\t\t");
280
281 switch (return_code)
282 {
283 case TEST_SUCCESS:
284 fprintf(stderr, "%ld.%03ld ", load_time / 1000, load_time % 1000);
285 stats.success++;
286 break;
287 case TEST_FAILURE:
288 stats.failed++;
289 failed= true;
290 break;
291 case TEST_SKIPPED:
292 stats.skipped++;
293 skipped= true;
294 break;
295 case TEST_MEMORY_ALLOCATION_FAILURE:
296 fprintf(stderr, "Exhausted memory, quitting\n");
297 abort();
298 case TEST_MAXIMUM_RETURN:
299 default:
300 assert(0); // Coding error.
301 break;
302 }
303
304 fprintf(stderr, "[ %s ]\n", test_strerror(return_code));
305
306 if (world.test.on_error)
307 {
308 test_return_t rc;
309 rc= world.test.on_error(return_code, world_ptr);
310
311 if (rc != TEST_SUCCESS)
312 break;
313 }
314 }
315
316 if (next->post && world.runner->post)
317 {
318 (void) world.runner->post(next->post, world_ptr);
319 }
320
321 if (! failed && ! skipped)
322 {
323 stats.collection_success++;
324 }
325 cleanup:
326
327 world.collection.shutdown(world_ptr);
328 }
329
330 if (stats.collection_failed || stats.collection_skipped)
331 {
332 fprintf(stderr, "Some test failures and/or skipped test occurred.\n\n");
333 }
334 else
335 {
336 fprintf(stderr, "All tests completed successfully\n\n");
337 }
338
339 if (world.destroy)
340 {
341 test_return_t error;
342 error= world.destroy(world_ptr);
343
344 if (error != TEST_SUCCESS)
345 {
346 fprintf(stderr, "Failure during shutdown.\n");
347 stats.failed++; // We do this to make our exit code return 1
348 }
349 }
350
351 world_stats_print(&stats);
352
353 return stats.failed == 0 ? 0 : 1;
354 }