r19339: Merge my 4.0-unittest branch. This adds an API for more fine-grained
[sfrench/samba-autobuild/.git] / source4 / 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 "lib/util/dlinklist.h"
25
26 void torture_comment(struct torture_context *context, 
27                                                  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_fail_ext(struct torture_context *context, 
44                                           const char *fmt, ...)
45 {
46         va_list ap;
47
48         va_start(ap, fmt);
49         context->last_reason = talloc_vasprintf(context, fmt, ap);
50         /* make sure the reason for the failure is displayed */
51         context->ui_ops->comment(context, context->last_reason);
52         va_end(ap);
53         context->last_result = TORTURE_FAIL;
54 }
55
56 void _torture_skip_ext(struct torture_context *context, 
57                                           const char *fmt, ...)
58 {
59         va_list ap;
60         context->skipped++;
61
62         va_start(ap, fmt);
63         context->last_result = TORTURE_SKIP;
64         context->last_reason = talloc_vasprintf(context, fmt, ap);
65         va_end(ap);
66 }
67
68 struct torture_suite *torture_suite_create(TALLOC_CTX *ctx, const char *name)
69 {
70         struct torture_suite *suite = talloc(ctx, struct torture_suite);
71
72         suite->name = talloc_strdup(suite, name);
73         suite->testcases = NULL;
74         suite->children = NULL;
75
76         return suite;
77 }
78
79 void torture_tcase_set_fixture(struct torture_tcase *tcase, 
80                 BOOL (*setup) (struct torture_context *, void **),
81                 BOOL (*teardown) (struct torture_context *, void *))
82 {
83         tcase->setup = setup;
84         tcase->teardown = teardown;
85 }
86
87 static bool wrap_test_with_testcase(struct torture_context *torture_ctx,
88                                                                         struct torture_tcase *tcase,
89                                                                         struct torture_test *test)
90 {
91         bool (*fn) (struct torture_context *, 
92                                  const void *tcase_data,
93                                  const void *test_data);
94
95         fn = test->fn;
96
97         return fn(torture_ctx, tcase->data, test->data);
98 }
99
100 struct torture_test *torture_tcase_add_test(struct torture_tcase *tcase, 
101                                                 const char *name, 
102                                                 bool (*run) (struct torture_context *, 
103                                                                          const void *tcase_data,
104                                                                          const void *test_data),
105                                                 const void *data)
106 {
107         struct torture_test *test = talloc(tcase, struct torture_test);
108
109         test->name = talloc_strdup(test, name);
110         test->description = NULL;
111         test->run = wrap_test_with_testcase;
112         test->fn = run;
113         test->dangerous = False;
114         test->data = data;
115
116         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
117
118         return test;
119 }
120
121 struct torture_tcase *torture_suite_add_tcase(struct torture_suite *suite, 
122                                                          const char *name)
123 {
124         struct torture_tcase *tcase = talloc(suite, struct torture_tcase);
125
126         tcase->name = talloc_strdup(tcase, name);
127         tcase->description = NULL;
128         tcase->setup = NULL;
129         tcase->teardown = NULL;
130         tcase->fixture_persistent = True;
131         tcase->tests = NULL;
132
133         DLIST_ADD_END(suite->testcases, tcase, struct torture_tcase *);
134
135         return tcase;
136 }
137
138 BOOL torture_run_suite(struct torture_context *context, 
139                                            struct torture_suite *suite)
140 {
141         BOOL ret = True;
142         struct torture_tcase *tcase;
143         struct torture_suite *tsuite;
144
145         context->level++;
146         if (context->ui_ops->suite_start)
147                 context->ui_ops->suite_start(context, suite);
148
149         for (tcase = suite->testcases; tcase; tcase = tcase->next) {
150                 ret &= torture_run_tcase(context, tcase);
151         }
152
153         for (tsuite = suite->children; tsuite; tsuite = tsuite->next) {
154                 ret &= torture_run_suite(context, tsuite);
155         }
156
157         if (context->ui_ops->suite_finish)
158                 context->ui_ops->suite_finish(context, suite);
159
160         context->level--;
161         
162         return ret;
163 }
164
165 static BOOL internal_torture_run_test(struct torture_context *context, 
166                                           struct torture_tcase *tcase,
167                                           struct torture_test *test,
168                                           BOOL already_setup)
169 {
170         BOOL ret;
171
172         if (test->dangerous && !torture_setting_bool(context, "dangerous", False)) {
173                 _torture_skip_ext(context, 
174                                 "disabled %s - enable dangerous tests to use", test->name);
175                 return True;
176         }
177
178         if (!already_setup && tcase->setup && 
179                 !tcase->setup(context, &(tcase->data)))
180                 return False;
181
182         context->active_tcase = tcase;
183         context->active_test = test;
184
185         if (context->ui_ops->test_start)
186                 context->ui_ops->test_start(context, tcase, test);
187
188         context->last_reason = NULL;
189         context->last_result = TORTURE_OK;
190
191         ret = test->run(context, tcase, test);
192         if (!ret) {
193                 if (context->last_reason == NULL)
194                         context->last_reason = talloc_strdup(context, "...");
195                 context->last_result = TORTURE_FAIL;
196         }
197
198         if (context->ui_ops->test_result)
199                 context->ui_ops->test_result(context, 
200                                                                          context->last_result, 
201                                                                          context->last_reason);
202
203
204         switch (context->last_result) {
205                 case TORTURE_SKIP: context->success++; break;
206                 case TORTURE_FAIL: context->failed++; break;
207                 case TORTURE_TODO: context->todo++; break;
208                 case TORTURE_OK: context->success++; break;
209         }
210
211         talloc_free(context->last_reason);
212
213         context->active_test = NULL;
214         context->active_tcase = NULL;
215
216         if (!already_setup && tcase->teardown && !tcase->teardown(context, tcase->data))
217                 return False;
218
219         return ret;
220 }
221
222 BOOL torture_run_tcase(struct torture_context *context, 
223                                            struct torture_tcase *tcase)
224 {
225         BOOL ret = True;
226         struct torture_test *test;
227
228         context->level++;
229
230         context->active_tcase = tcase;
231         if (context->ui_ops->tcase_start)
232                 context->ui_ops->tcase_start(context, tcase);
233
234         if (tcase->fixture_persistent && tcase->setup 
235                 && !tcase->setup(context, &tcase->data)) {
236                 ret = False;
237                 goto done;
238         }
239
240         for (test = tcase->tests; test; test = test->next) {
241                 ret &= internal_torture_run_test(context, tcase, test, 
242                                 tcase->fixture_persistent);
243         }
244
245         if (tcase->fixture_persistent && tcase->teardown &&
246                 !tcase->teardown(context, tcase->data))
247                 ret = False;
248
249 done:
250         context->active_tcase = NULL;
251
252         if (context->ui_ops->tcase_finish)
253                 context->ui_ops->tcase_finish(context, tcase);
254
255         context->level--;
256
257         return ret;
258 }
259
260 BOOL torture_run_test(struct torture_context *context, 
261                                           struct torture_tcase *tcase,
262                                           struct torture_test *test)
263 {
264         return internal_torture_run_test(context, tcase, test, False);
265 }
266
267 int torture_setting_int(struct torture_context *test, const char *name, 
268                                                         int default_value)
269 {
270         return lp_parm_int(-1, "torture", name, default_value);
271 }
272
273 bool torture_setting_bool(struct torture_context *test, const char *name, 
274                                                         bool default_value)
275 {
276         return lp_parm_bool(-1, "torture", name, default_value);
277 }
278
279 const char *torture_setting_string(struct torture_context *test, const char *name, 
280                                                         const char *default_value)
281 {
282         const char *ret = lp_parm_string(-1, "torture", name);
283
284         if (ret == NULL)
285                 return default_value;
286
287         return ret;
288 }
289
290 static bool wrap_test_with_simple_tcase(struct torture_context *torture_ctx,
291                                                                         struct torture_tcase *tcase,
292                                                                         struct torture_test *test)
293 {
294         bool (*fn) (struct torture_context *, const void *tcase_data);
295
296         fn = test->fn;
297
298         return fn(torture_ctx, test->data);
299 }
300
301 struct torture_tcase *torture_suite_add_simple_tcase(
302                                         struct torture_suite *suite, 
303                                         const char *name,
304                                         bool (*run) (struct torture_context *test, const void *),
305                                         const void *data)
306 {
307         struct torture_tcase *tcase;
308         struct torture_test *test; 
309         
310         tcase = torture_suite_add_tcase(suite, name);
311
312         test = talloc(tcase, struct torture_test);
313
314         test->name = talloc_strdup(test, name);
315         test->description = NULL;
316         test->run = wrap_test_with_simple_tcase;
317         test->fn = run;
318         test->data = data;
319         test->dangerous = False;
320
321         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
322
323         return tcase;
324 }
325
326 static bool wrap_simple_test(struct torture_context *torture_ctx,
327                                                                         struct torture_tcase *tcase,
328                                                                         struct torture_test *test)
329 {
330         bool (*fn) (struct torture_context *);
331
332         fn = test->fn;
333
334         return fn(torture_ctx);
335 }
336
337 struct torture_tcase *torture_suite_add_simple_test(
338                                         struct torture_suite *suite, 
339                                         const char *name,
340                                         bool (*run) (struct torture_context *test))
341 {
342         struct torture_test *test; 
343         struct torture_tcase *tcase;
344         
345         tcase = torture_suite_add_tcase(suite, name);
346
347         test = talloc(tcase, struct torture_test);
348
349         test->name = talloc_strdup(test, name);
350         test->description = NULL;
351         test->run = wrap_simple_test;
352         test->fn = run;
353         test->dangerous = false;
354
355         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
356
357         return tcase;
358 }
359
360 BOOL torture_teardown_free(struct torture_context *torture, void *data)
361 {
362         return talloc_free(data);
363 }
364
365
366 bool torture_suite_add_suite(struct torture_suite *suite, 
367                                                          struct torture_suite *child)
368 {
369         if (child == NULL)
370                 return false;
371
372         DLIST_ADD_END(suite->children, child, struct torture_suite *);
373
374         /* FIXME: Check for duplicates and return false if the 
375          * added suite already exists as a child */
376
377         return true;
378 }
379
380
381 struct torture_suite *torture_find_suite(const char *name)
382 {
383         /* FIXME */
384
385         return NULL;
386 }