Updating test framework for startup/shutdown of memcached.
[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
25 #include <libtest/stats.h>
26
27 #ifndef __INTEL_COMPILER
28 #pragma GCC diagnostic ignored "-Wold-style-cast"
29 #endif
30
31 static in_port_t global_port= 0;
32 static char global_socket[1024];
33
34 in_port_t default_port()
35 {
36 assert(global_port);
37 return global_port;
38 }
39
40 void set_default_port(in_port_t port)
41 {
42 global_port= port;
43 }
44
45 const char *default_socket()
46 {
47 assert(global_socket[0]);
48 return global_socket;
49 }
50
51 void set_default_socket(const char *socket)
52 {
53 strncpy(global_socket, socket, strlen(socket));
54 }
55
56 static void stats_print(Stats *stats)
57 {
58 std::cout << "\tTotal Collections\t\t\t\t" << stats->collection_total << std::endl;
59 std::cout << "\tFailed Collections\t\t\t\t" << stats->collection_failed << std::endl;
60 std::cout << "\tSkipped Collections\t\t\t\t" << stats->collection_skipped << std::endl;
61 std::cout << "\tSucceeded Collections\t\t\t\t" << stats->collection_success << std::endl;
62 std::cout << std::endl;
63 std::cout << "Total\t\t\t\t" << stats->total << std::endl;
64 std::cout << "\tFailed\t\t\t" << stats->failed << std::endl;
65 std::cout << "\tSkipped\t\t\t" << stats->skipped << std::endl;
66 std::cout << "\tSucceeded\t\t" << stats->success << std::endl;
67 }
68
69 static long int timedif(struct timeval a, struct timeval b)
70 {
71 long us, s;
72
73 us = (long)(a.tv_usec - b.tv_usec);
74 us /= 1000;
75 s = (long)(a.tv_sec - b.tv_sec);
76 s *= 1000;
77 return s + us;
78 }
79
80 const char *test_strerror(test_return_t code)
81 {
82 switch (code) {
83 case TEST_SUCCESS:
84 return "ok";
85
86 case TEST_FAILURE:
87 return "failed";
88
89 case TEST_MEMORY_ALLOCATION_FAILURE:
90 return "memory allocation";
91
92 case TEST_SKIPPED:
93 return "skipped";
94
95 case TEST_FATAL:
96 break;
97 }
98
99 return "failed";
100 }
101
102 void create_core(void)
103 {
104 if (getenv("LIBMEMCACHED_NO_COREDUMP") == NULL)
105 {
106 pid_t pid= fork();
107
108 if (pid == 0)
109 {
110 abort();
111 }
112 else
113 {
114 while (waitpid(pid, NULL, 0) != pid) {};
115 }
116 }
117 }
118
119
120 static test_return_t _runner_default(test_callback_fn func, void *p)
121 {
122 if (func)
123 {
124 return func(p);
125 }
126
127 return TEST_SUCCESS;
128 }
129
130 static Runner defualt_runners= {
131 _runner_default,
132 _runner_default,
133 _runner_default
134 };
135
136 static test_return_t _default_callback(void *p)
137 {
138 (void)p;
139
140 return TEST_SUCCESS;
141 }
142
143 Framework::Framework() :
144 collections(NULL),
145 _create(NULL),
146 _destroy(NULL),
147 collection_startup(_default_callback),
148 collection_shutdown(_default_callback),
149 _on_error(NULL),
150 runner(&defualt_runners)
151 {
152 }
153
154
155 int main(int argc, char *argv[])
156 {
157 Framework *world= new Framework();
158
159 if (not world)
160 {
161 return EXIT_FAILURE;
162 }
163
164 Stats stats;
165
166 get_world(world);
167
168 test_return_t error;
169 void *world_ptr= world->create(&error);
170 if (test_failed(error))
171 {
172 std::cerr << "create() failed" << std::endl;
173 return EXIT_FAILURE;
174 }
175
176 char *collection_to_run= NULL;
177 if (argc > 1)
178 {
179 collection_to_run= argv[1];
180 }
181 else if (getenv("TEST_COLLECTION"))
182 {
183 collection_to_run= getenv("TEST_COLLECTION");
184 }
185
186 if (collection_to_run)
187 {
188 std::cout << "Only testing " << collection_to_run << std::endl;
189 }
190
191 char *wildcard= NULL;
192 if (argc == 3)
193 {
194 wildcard= argv[2];
195 }
196
197 for (collection_st *next= world->collections; next->name; next++)
198 {
199 test_return_t collection_rc= TEST_SUCCESS;
200 bool failed= false;
201 bool skipped= false;
202
203 if (collection_to_run && fnmatch(collection_to_run, next->name, 0))
204 continue;
205
206 stats.collection_total++;
207
208 collection_rc= world->startup(world_ptr);
209
210 if (collection_rc == TEST_SUCCESS and next->pre)
211 {
212 collection_rc= world->runner->pre(next->pre, world_ptr);
213 }
214
215 switch (collection_rc)
216 {
217 case TEST_SUCCESS:
218 std::cerr << std::endl << next->name << std::endl << std::endl;
219 break;
220
221 case TEST_FATAL:
222 case TEST_FAILURE:
223 std::cerr << std::endl << next->name << " [ failed ]" << std::endl << std::endl;
224 stats.collection_failed++;
225 goto cleanup;
226
227 case TEST_SKIPPED:
228 std::cerr << std::endl << next->name << " [ skipping ]" << std::endl << std::endl;
229 stats.collection_skipped++;
230 goto cleanup;
231
232 case TEST_MEMORY_ALLOCATION_FAILURE:
233 test_assert(0, "Allocation failure, or unknown return");
234 }
235
236 for (test_st *run= next->tests; run->name; run++)
237 {
238 struct timeval start_time, end_time;
239 long int load_time= 0;
240
241 if (wildcard && fnmatch(wildcard, run->name, 0))
242 {
243 continue;
244 }
245
246 std::cerr << "\tTesting " << run->name;
247
248 test_return_t return_code;
249 if (test_success(return_code= world->item.startup(world_ptr)))
250 {
251 if (test_success(return_code= world->item.flush(world_ptr, run)))
252 {
253 // @note pre will fail is SKIPPED is returned
254 if (test_success(return_code= world->item.pre(world_ptr)))
255 {
256 { // Runner Code
257 gettimeofday(&start_time, NULL);
258 return_code= world->runner->run(run->test_fn, world_ptr);
259 gettimeofday(&end_time, NULL);
260 load_time= timedif(end_time, start_time);
261 }
262 }
263
264 // @todo do something if post fails
265 (void)world->item.post(world_ptr);
266 }
267 else
268 {
269 std::cerr << __FILE__ << ":" << __LINE__ << " item.flush(failure)" << std::endl;
270 }
271 }
272 else
273 {
274 std::cerr << __FILE__ << ":" << __LINE__ << " item.startup(failure)" << std::endl;
275 }
276
277 stats.total++;
278
279 std::cerr << "\t\t\t\t\t";
280
281 switch (return_code)
282 {
283 case TEST_SUCCESS:
284 std::cerr << load_time / 1000 << "." << load_time % 1000;
285 stats.success++;
286 break;
287
288 case TEST_FATAL:
289 case TEST_FAILURE:
290 stats.failed++;
291 failed= true;
292 break;
293
294 case TEST_SKIPPED:
295 stats.skipped++;
296 skipped= true;
297 break;
298
299 case TEST_MEMORY_ALLOCATION_FAILURE:
300 test_assert(0, "Memory Allocation Error");
301 }
302
303 std::cerr << "[ " << test_strerror(return_code) << " ]" << std::endl;
304
305 if (test_failed(world->on_error(return_code, world_ptr)))
306 {
307 break;
308 }
309 }
310
311 if (next->post and world->runner->post)
312 {
313 (void) world->runner->post(next->post, world_ptr);
314 }
315
316 if (failed == 0 and skipped == 0)
317 {
318 stats.collection_success++;
319 }
320 cleanup:
321
322 world->shutdown(world_ptr);
323 }
324
325 if (stats.collection_failed || stats.collection_skipped)
326 {
327 std::cerr << std::endl << std::endl << "Some test failures and/or skipped test occurred." << std::endl << std::endl;
328 #if 0
329 print_failed_test();
330 #endif
331 }
332 else
333 {
334 std::cout << std::endl << std::endl << "All tests completed successfully." << std::endl << std::endl;
335 }
336
337 if (test_failed(world->destroy(world_ptr)))
338 {
339 stats.failed++; // We do this to make our exit code return EXIT_FAILURE
340 }
341
342 stats_print(&stats);
343
344 delete world;
345
346 return stats.failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
347 }