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