r23531: added optional latency reporting in BENCH-NBENCH. To use it, you need
[samba.git] / source / torture / ui.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB torture UI functions
4
5    Copyright (C) Jelmer Vernooij 2006
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program 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
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "torture/ui.h"
24 #include "torture/torture.h"
25 #include "lib/util/dlinklist.h"
26
27 void torture_comment(struct torture_context *context, const char *comment, ...)
28 {
29         va_list ap;
30         char *tmp;
31
32         if (!context->ui_ops->comment)
33                 return;
34
35         va_start(ap, comment);
36         tmp = talloc_vasprintf(context, comment, ap);
37                 
38         context->ui_ops->comment(context, tmp);
39         
40         talloc_free(tmp);
41 }
42
43 void torture_warning(struct torture_context *context, const char *comment, ...)
44 {
45         va_list ap;
46         char *tmp;
47
48         if (!context->ui_ops->warning)
49                 return;
50
51         va_start(ap, comment);
52         tmp = talloc_vasprintf(context, comment, ap);
53
54         context->ui_ops->warning(context, tmp);
55
56         talloc_free(tmp);
57 }
58
59 void torture_result(struct torture_context *context, 
60                                         enum torture_result result, const char *fmt, ...)
61 {
62         va_list ap;
63
64         va_start(ap, fmt);
65
66         context->last_result = result;
67         context->last_reason = talloc_vasprintf(context, fmt, ap);
68         va_end(ap);
69 }
70
71 struct torture_suite *torture_suite_create(TALLOC_CTX *ctx, const char *name)
72 {
73         struct torture_suite *suite = talloc_zero(ctx, struct torture_suite);
74
75         suite->name = talloc_strdup(suite, name);
76         suite->testcases = NULL;
77         suite->children = NULL;
78
79         return suite;
80 }
81
82 void torture_tcase_set_fixture(struct torture_tcase *tcase, 
83                 BOOL (*setup) (struct torture_context *, void **),
84                 BOOL (*teardown) (struct torture_context *, void *))
85 {
86         tcase->setup = setup;
87         tcase->teardown = teardown;
88 }
89
90 static bool wrap_test_with_testcase(struct torture_context *torture_ctx,
91                                                                         struct torture_tcase *tcase,
92                                                                         struct torture_test *test)
93 {
94         bool (*fn) (struct torture_context *, 
95                                  const void *tcase_data,
96                                  const void *test_data);
97
98         fn = test->fn;
99
100         return fn(torture_ctx, tcase->data, test->data);
101 }
102
103 struct torture_test *torture_tcase_add_test(struct torture_tcase *tcase, 
104                                                 const char *name, 
105                                                 bool (*run) (struct torture_context *, 
106                                                                          const void *tcase_data,
107                                                                          const void *test_data),
108                                                 const void *data)
109 {
110         struct torture_test *test = talloc(tcase, struct torture_test);
111
112         test->name = talloc_strdup(test, name);
113         test->description = NULL;
114         test->run = wrap_test_with_testcase;
115         test->fn = run;
116         test->dangerous = False;
117         test->data = data;
118
119         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
120
121         return test;
122 }
123
124 struct torture_tcase *torture_suite_add_tcase(struct torture_suite *suite, 
125                                                          const char *name)
126 {
127         struct torture_tcase *tcase = talloc(suite, struct torture_tcase);
128
129         tcase->name = talloc_strdup(tcase, name);
130         tcase->description = NULL;
131         tcase->setup = NULL;
132         tcase->teardown = NULL;
133         tcase->fixture_persistent = True;
134         tcase->tests = NULL;
135
136         DLIST_ADD_END(suite->testcases, tcase, struct torture_tcase *);
137
138         return tcase;
139 }
140
141 BOOL torture_run_suite(struct torture_context *context, 
142                                            struct torture_suite *suite)
143 {
144         BOOL ret = True;
145         struct torture_tcase *tcase;
146         struct torture_suite *tsuite;
147         char *old_testname;
148
149         context->level++;
150         if (context->ui_ops->suite_start)
151                 context->ui_ops->suite_start(context, suite);
152
153         old_testname = context->active_testname;
154         if (old_testname != NULL)
155                 context->active_testname = talloc_asprintf(context, "%s-%s", 
156                                                                                            old_testname, suite->name);
157         else
158                 context->active_testname = talloc_strdup(context, suite->name);
159
160         for (tcase = suite->testcases; tcase; tcase = tcase->next) {
161                 ret &= torture_run_tcase(context, tcase);
162         }
163
164         for (tsuite = suite->children; tsuite; tsuite = tsuite->next) {
165                 ret &= torture_run_suite(context, tsuite);
166         }
167
168         talloc_free(context->active_testname);
169         context->active_testname = old_testname;
170
171         if (context->ui_ops->suite_finish)
172                 context->ui_ops->suite_finish(context, suite);
173
174         context->level--;
175         
176         return ret;
177 }
178
179 void torture_ui_test_start(struct torture_context *context,
180                                                            struct torture_tcase *tcase,
181                                                            struct torture_test *test)
182 {
183         if (context->ui_ops->test_start)
184                 context->ui_ops->test_start(context, tcase, test);
185 }
186
187 int str_list_match(const char *name, char **list)
188 {
189         int i, ret = 0;
190         if (list == NULL)
191                 return 0;
192
193         for (i = 0; list[i]; i++) {
194                 if (gen_fnmatch(list[i], name) == 0)
195                         ret++;
196         }
197         return ret;
198 }
199
200 void torture_ui_test_result(struct torture_context *context,
201                                                                 enum torture_result result,
202                                                                 const char *comment)
203 {
204         if (context->ui_ops->test_result)
205                 context->ui_ops->test_result(context, result, comment);
206
207         if (result == TORTURE_ERROR || result == TORTURE_FAIL)
208                 context->returncode = false;
209 }
210
211 static BOOL internal_torture_run_test(struct torture_context *context, 
212                                           struct torture_tcase *tcase,
213                                           struct torture_test *test,
214                                           BOOL already_setup)
215 {
216         BOOL ret;
217         char *old_testname;
218
219         if (test->dangerous && !torture_setting_bool(context, "dangerous", False)) {
220                 torture_result(context, TORTURE_SKIP,
221                                 "disabled %s - enable dangerous tests to use", test->name);
222                 return True;
223         }
224
225         if (!already_setup && tcase->setup && 
226                 !tcase->setup(context, &(tcase->data)))
227                 return False;
228
229         if (tcase == NULL || strcmp(test->name, tcase->name) != 0) { 
230                 old_testname = context->active_testname;
231                 context->active_testname = talloc_asprintf(context, "%s-%s", 
232                                                                                            old_testname, test->name);
233         }
234         context->active_tcase = tcase;
235         context->active_test = test;
236
237         torture_ui_test_start(context, tcase, test);
238
239
240         context->last_reason = NULL;
241         context->last_result = TORTURE_OK;
242
243         ret = test->run(context, tcase, test);
244         if (!ret && context->last_result == TORTURE_OK) {
245                 if (context->last_reason == NULL)
246                         context->last_reason = talloc_strdup(context, "Unknown error/failure");
247                 context->last_result = TORTURE_ERROR;
248         }
249
250         torture_ui_test_result(context, context->last_result, context->last_reason);
251         
252         talloc_free(context->last_reason);
253
254         if (tcase == NULL || strcmp(test->name, tcase->name) != 0) { 
255                 talloc_free(context->active_testname);
256                 context->active_testname = old_testname;
257         }
258         context->active_test = NULL;
259         context->active_tcase = NULL;
260
261         if (!already_setup && tcase->teardown && !tcase->teardown(context, tcase->data))
262                 return False;
263
264         return ret;
265 }
266
267 BOOL torture_run_tcase(struct torture_context *context, 
268                                            struct torture_tcase *tcase)
269 {
270         BOOL ret = True;
271         char *old_testname;
272         struct torture_test *test;
273
274         context->level++;
275
276         context->active_tcase = tcase;
277         if (context->ui_ops->tcase_start)
278                 context->ui_ops->tcase_start(context, tcase);
279
280         if (tcase->fixture_persistent && tcase->setup 
281                 && !tcase->setup(context, &tcase->data)) {
282                 ret = False;
283                 goto done;
284         }
285
286         old_testname = context->active_testname;
287         context->active_testname = talloc_asprintf(context, "%s-%s", 
288                                                                                            old_testname, tcase->name);
289         for (test = tcase->tests; test; test = test->next) {
290                 ret &= internal_torture_run_test(context, tcase, test, 
291                                 tcase->fixture_persistent);
292         }
293         talloc_free(context->active_testname);
294         context->active_testname = old_testname;
295
296         if (tcase->fixture_persistent && tcase->teardown &&
297                 !tcase->teardown(context, tcase->data))
298                 ret = False;
299
300 done:
301         context->active_tcase = NULL;
302
303         if (context->ui_ops->tcase_finish)
304                 context->ui_ops->tcase_finish(context, tcase);
305
306         context->level--;
307
308         return ret;
309 }
310
311 BOOL torture_run_test(struct torture_context *context, 
312                                           struct torture_tcase *tcase,
313                                           struct torture_test *test)
314 {
315         return internal_torture_run_test(context, tcase, test, False);
316 }
317
318 int torture_setting_int(struct torture_context *test, const char *name, 
319                                                         int default_value)
320 {
321         return lp_parm_int(-1, "torture", name, default_value);
322 }
323
324 bool torture_setting_bool(struct torture_context *test, const char *name, 
325                                                         bool default_value)
326 {
327         return lp_parm_bool(-1, "torture", name, default_value);
328 }
329
330 const char *torture_setting_string(struct torture_context *test, const char *name, 
331                                                         const char *default_value)
332 {
333         const char *ret = lp_parm_string(-1, "torture", name);
334
335         if (ret == NULL)
336                 return default_value;
337
338         return ret;
339 }
340
341 static bool wrap_test_with_simple_tcase(struct torture_context *torture_ctx,
342                                                                         struct torture_tcase *tcase,
343                                                                         struct torture_test *test)
344 {
345         bool (*fn) (struct torture_context *, const void *tcase_data);
346
347         fn = test->fn;
348
349         return fn(torture_ctx, test->data);
350 }
351
352 struct torture_tcase *torture_suite_add_simple_tcase(
353                                         struct torture_suite *suite, 
354                                         const char *name,
355                                         bool (*run) (struct torture_context *test, const void *),
356                                         const void *data)
357 {
358         struct torture_tcase *tcase;
359         struct torture_test *test; 
360         
361         tcase = torture_suite_add_tcase(suite, name);
362
363         test = talloc(tcase, struct torture_test);
364
365         test->name = talloc_strdup(test, name);
366         test->description = NULL;
367         test->run = wrap_test_with_simple_tcase;
368         test->fn = run;
369         test->data = data;
370         test->dangerous = False;
371
372         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
373
374         return tcase;
375 }
376
377 static bool wrap_simple_test(struct torture_context *torture_ctx,
378                                                                         struct torture_tcase *tcase,
379                                                                         struct torture_test *test)
380 {
381         bool (*fn) (struct torture_context *);
382
383         fn = test->fn;
384
385         return fn(torture_ctx);
386 }
387
388 struct torture_tcase *torture_suite_add_simple_test(
389                                         struct torture_suite *suite, 
390                                         const char *name,
391                                         bool (*run) (struct torture_context *test))
392 {
393         struct torture_test *test; 
394         struct torture_tcase *tcase;
395         
396         tcase = torture_suite_add_tcase(suite, name);
397
398         test = talloc(tcase, struct torture_test);
399
400         test->name = talloc_strdup(test, name);
401         test->description = NULL;
402         test->run = wrap_simple_test;
403         test->fn = run;
404         test->dangerous = false;
405
406         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
407
408         return tcase;
409 }
410
411 bool torture_suite_add_suite(struct torture_suite *suite, 
412                                                          struct torture_suite *child)
413 {
414         if (child == NULL)
415                 return false;
416
417         DLIST_ADD_END(suite->children, child, struct torture_suite *);
418
419         /* FIXME: Check for duplicates and return false if the 
420          * added suite already exists as a child */
421
422         return true;
423 }
424
425
426 struct torture_suite *torture_find_suite(struct torture_suite *parent, 
427                                                                                  const char *name)
428 {
429         struct torture_suite *child;
430
431         for (child = parent->children; child; child = child->next) 
432                 if (!strcmp(child->name, name))
433                         return child;
434
435         return NULL;
436 }