2 Unix SMB/CIFS implementation.
3 SMB torture UI functions
5 Copyright (C) Jelmer Vernooij 2006-2008
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 3 of the License, or
10 (at your option) any later version.
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.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "source4/include/includes.h"
22 #include "../torture/torture.h"
23 #include "../lib/util/dlinklist.h"
24 #include "param/param.h"
25 #include "system/filesys.h"
26 #include "system/dir.h"
29 struct torture_results
*torture_results_init(TALLOC_CTX
*mem_ctx
, const struct torture_ui_ops
*ui_ops
)
31 struct torture_results
*results
= talloc_zero(mem_ctx
, struct torture_results
);
33 results
->ui_ops
= ui_ops
;
34 results
->returncode
= true;
37 ui_ops
->init(results
);
43 * Initialize a torture context
45 struct torture_context
*torture_context_init(struct tevent_context
*event_ctx
,
46 struct torture_results
*results
)
48 struct torture_context
*torture
= talloc_zero(event_ctx
,
49 struct torture_context
);
54 torture
->ev
= event_ctx
;
55 torture
->results
= talloc_reference(torture
, results
);
61 * Create a sub torture context
63 struct torture_context
*torture_context_child(struct torture_context
*parent
)
65 struct torture_context
*subtorture
= talloc_zero(parent
, struct torture_context
);
67 if (subtorture
== NULL
)
70 subtorture
->ev
= talloc_reference(subtorture
, parent
->ev
);
71 subtorture
->lp_ctx
= talloc_reference(subtorture
, parent
->lp_ctx
);
72 subtorture
->outputdir
= talloc_reference(subtorture
, parent
->outputdir
);
73 subtorture
->results
= talloc_reference(subtorture
, parent
->results
);
79 create a temporary directory under the output dir
81 _PUBLIC_ NTSTATUS
torture_temp_dir(struct torture_context
*tctx
,
82 const char *prefix
, char **tempdir
)
84 SMB_ASSERT(tctx
->outputdir
!= NULL
);
86 *tempdir
= talloc_asprintf(tctx
, "%s/%s.XXXXXX", tctx
->outputdir
,
88 NT_STATUS_HAVE_NO_MEMORY(*tempdir
);
90 if (mkdtemp(*tempdir
) == NULL
) {
91 return map_nt_error_from_unix_common(errno
);
97 static int local_deltree(const char *path
)
100 struct dirent
*dirent
;
101 DIR *dir
= opendir(path
);
103 char *error
= talloc_asprintf(NULL
, "Could not open directory %s", path
);
108 while ((dirent
= readdir(dir
))) {
110 if ((strcmp(dirent
->d_name
, ".") == 0) || (strcmp(dirent
->d_name
, "..") == 0)) {
113 name
= talloc_asprintf(NULL
, "%s/%s", path
,
119 DEBUG(0, ("About to remove %s\n", name
));
126 if (errno
== ENOTEMPTY
) {
127 ret
= local_deltree(name
);
134 char *error
= talloc_asprintf(NULL
, "Could not remove %s", path
);
145 _PUBLIC_ NTSTATUS
torture_deltree_outputdir(struct torture_context
*tctx
)
147 if (tctx
->outputdir
== NULL
) {
150 if ((strcmp(tctx
->outputdir
, "/") == 0)
151 || (strcmp(tctx
->outputdir
, "") == 0)) {
152 return NT_STATUS_INVALID_PARAMETER
;
155 if (local_deltree(tctx
->outputdir
) == -1) {
157 return map_nt_error_from_unix_common(errno
);
159 return NT_STATUS_UNSUCCESSFUL
;
165 * Comment on the status/progress of a test
167 void torture_comment(struct torture_context
*context
, const char *comment
, ...)
172 if (!context
->results
->ui_ops
->comment
)
175 va_start(ap
, comment
);
176 tmp
= talloc_vasprintf(context
, comment
, ap
);
179 context
->results
->ui_ops
->comment(context
, tmp
);
185 * Print a warning about the current test
187 void torture_warning(struct torture_context
*context
, const char *comment
, ...)
192 if (!context
->results
->ui_ops
->warning
)
195 va_start(ap
, comment
);
196 tmp
= talloc_vasprintf(context
, comment
, ap
);
199 context
->results
->ui_ops
->warning(context
, tmp
);
205 * Store the result of a torture test.
207 void torture_result(struct torture_context
*context
,
208 enum torture_result result
, const char *fmt
, ...)
214 if (context
->last_reason
) {
215 torture_warning(context
, "%s", context
->last_reason
);
216 talloc_free(context
->last_reason
);
219 context
->last_result
= result
;
220 context
->last_reason
= talloc_vasprintf(context
, fmt
, ap
);
225 * Create a new torture suite
227 struct torture_suite
*torture_suite_create(TALLOC_CTX
*ctx
, const char *name
)
229 struct torture_suite
*suite
= talloc_zero(ctx
, struct torture_suite
);
231 suite
->name
= talloc_strdup(suite
, name
);
232 suite
->testcases
= NULL
;
233 suite
->children
= NULL
;
239 * Set the setup() and teardown() functions for a testcase.
241 void torture_tcase_set_fixture(struct torture_tcase
*tcase
,
242 bool (*setup
) (struct torture_context
*, void **),
243 bool (*teardown
) (struct torture_context
*, void *))
245 tcase
->setup
= setup
;
246 tcase
->teardown
= teardown
;
249 static bool wrap_test_with_testcase_const(struct torture_context
*torture_ctx
,
250 struct torture_tcase
*tcase
,
251 struct torture_test
*test
)
253 bool (*fn
) (struct torture_context
*,
254 const void *tcase_data
,
255 const void *test_data
);
259 return fn(torture_ctx
, tcase
->data
, test
->data
);
263 * Add a test that uses const data to a testcase
265 struct torture_test
*torture_tcase_add_test_const(struct torture_tcase
*tcase
,
267 bool (*run
) (struct torture_context
*, const void *tcase_data
,
268 const void *test_data
),
271 struct torture_test
*test
= talloc(tcase
, struct torture_test
);
273 test
->name
= talloc_strdup(test
, name
);
274 test
->description
= NULL
;
275 test
->run
= wrap_test_with_testcase_const
;
277 test
->dangerous
= false;
280 DLIST_ADD_END(tcase
->tests
, test
);
288 bool torture_suite_init_tcase(struct torture_suite
*suite
,
289 struct torture_tcase
*tcase
,
292 tcase
->name
= talloc_strdup(tcase
, name
);
293 tcase
->description
= NULL
;
295 tcase
->teardown
= NULL
;
296 tcase
->fixture_persistent
= true;
299 DLIST_ADD_END(suite
->testcases
, tcase
);
305 struct torture_tcase
*torture_suite_add_tcase(struct torture_suite
*suite
,
308 struct torture_tcase
*tcase
= talloc(suite
, struct torture_tcase
);
310 if (!torture_suite_init_tcase(suite
, tcase
, name
))
316 int torture_suite_children_count(const struct torture_suite
*suite
)
319 struct torture_tcase
*tcase
;
320 struct torture_test
*test
;
321 struct torture_suite
*tsuite
;
322 for (tcase
= suite
->testcases
; tcase
; tcase
= tcase
->next
) {
323 for (test
= tcase
->tests
; test
; test
= test
->next
) {
327 for (tsuite
= suite
->children
; tsuite
; tsuite
= tsuite
->next
) {
334 * Run a torture test suite.
336 bool torture_run_suite(struct torture_context
*context
,
337 struct torture_suite
*suite
)
339 return torture_run_suite_restricted(context
, suite
, NULL
);
342 bool torture_run_suite_restricted(struct torture_context
*context
,
343 struct torture_suite
*suite
, const char **restricted
)
346 struct torture_tcase
*tcase
;
347 struct torture_suite
*tsuite
;
349 if (context
->results
->ui_ops
->suite_start
)
350 context
->results
->ui_ops
->suite_start(context
, suite
);
352 /* FIXME: Adjust torture_suite_children_count if restricted != NULL */
353 context
->results
->ui_ops
->progress(context
,
354 torture_suite_children_count(suite
), TORTURE_PROGRESS_SET
);
356 for (tcase
= suite
->testcases
; tcase
; tcase
= tcase
->next
) {
357 ret
&= torture_run_tcase_restricted(context
, tcase
, restricted
);
360 for (tsuite
= suite
->children
; tsuite
; tsuite
= tsuite
->next
) {
361 context
->results
->ui_ops
->progress(context
, 0, TORTURE_PROGRESS_PUSH
);
362 ret
&= torture_run_suite_restricted(context
, tsuite
, restricted
);
363 context
->results
->ui_ops
->progress(context
, 0, TORTURE_PROGRESS_POP
);
366 if (context
->results
->ui_ops
->suite_finish
)
367 context
->results
->ui_ops
->suite_finish(context
, suite
);
372 void torture_ui_test_start(struct torture_context
*context
,
373 struct torture_tcase
*tcase
,
374 struct torture_test
*test
)
376 if (context
->results
->ui_ops
->test_start
)
377 context
->results
->ui_ops
->test_start(context
, tcase
, test
);
380 void torture_ui_test_result(struct torture_context
*context
,
381 enum torture_result result
,
384 if (context
->results
->ui_ops
->test_result
)
385 context
->results
->ui_ops
->test_result(context
, result
, comment
);
387 if (result
== TORTURE_ERROR
|| result
== TORTURE_FAIL
)
388 context
->results
->returncode
= false;
391 static bool test_needs_running(const char *name
, const char **restricted
)
394 if (restricted
== NULL
)
396 for (i
= 0; restricted
[i
]; i
++) {
397 if (!strcmp(name
, restricted
[i
]))
403 static bool internal_torture_run_test(struct torture_context
*context
,
404 struct torture_tcase
*tcase
,
405 struct torture_test
*test
,
407 const char **restricted
)
410 char *subunit_testname
= NULL
;
412 if (tcase
== NULL
|| strcmp(test
->name
, tcase
->name
) != 0) {
413 subunit_testname
= talloc_asprintf(context
, "%s.%s", tcase
->name
, test
->name
);
415 subunit_testname
= talloc_strdup(context
, test
->name
);
418 if (!test_needs_running(subunit_testname
, restricted
))
421 context
->active_tcase
= tcase
;
422 context
->active_test
= test
;
424 torture_ui_test_start(context
, tcase
, test
);
426 context
->last_reason
= NULL
;
427 context
->last_result
= TORTURE_OK
;
429 if (!already_setup
&& tcase
->setup
&&
430 !tcase
->setup(context
, &(tcase
->data
))) {
431 if (context
->last_reason
== NULL
)
432 context
->last_reason
= talloc_strdup(context
, "Setup failure");
433 context
->last_result
= TORTURE_ERROR
;
435 } else if (test
->dangerous
&&
436 !torture_setting_bool(context
, "dangerous", false)) {
437 context
->last_result
= TORTURE_SKIP
;
438 context
->last_reason
= talloc_asprintf(context
,
439 "disabled %s - enable dangerous tests to use", test
->name
);
442 success
= test
->run(context
, tcase
, test
);
444 if (!success
&& context
->last_result
== TORTURE_OK
) {
445 if (context
->last_reason
== NULL
)
446 context
->last_reason
= talloc_strdup(context
,
447 "Unknown error/failure. Missing torture_fail() or torture_assert_*() call?");
448 context
->last_result
= TORTURE_ERROR
;
452 if (!already_setup
&& tcase
->teardown
&& !tcase
->teardown(context
, tcase
->data
)) {
453 if (context
->last_reason
== NULL
)
454 context
->last_reason
= talloc_strdup(context
, "Setup failure");
455 context
->last_result
= TORTURE_ERROR
;
459 torture_ui_test_result(context
, context
->last_result
,
460 context
->last_reason
);
462 talloc_free(context
->last_reason
);
463 context
->last_reason
= NULL
;
465 context
->active_test
= NULL
;
466 context
->active_tcase
= NULL
;
471 bool torture_run_tcase(struct torture_context
*context
,
472 struct torture_tcase
*tcase
)
474 return torture_run_tcase_restricted(context
, tcase
, NULL
);
477 bool torture_run_tcase_restricted(struct torture_context
*context
,
478 struct torture_tcase
*tcase
, const char **restricted
)
481 struct torture_test
*test
;
482 bool setup_succeeded
= true;
483 const char * setup_reason
= "Setup failed";
485 context
->active_tcase
= tcase
;
486 if (context
->results
->ui_ops
->tcase_start
)
487 context
->results
->ui_ops
->tcase_start(context
, tcase
);
489 if (tcase
->fixture_persistent
&& tcase
->setup
) {
490 setup_succeeded
= tcase
->setup(context
, &tcase
->data
);
493 if (!setup_succeeded
) {
494 /* Uh-oh. The setup failed, so we can't run any of the tests
495 * in this testcase. The subunit format doesn't specify what
496 * to do here, so we keep the failure reason, and manually
497 * use it to fail every test.
499 if (context
->last_reason
!= NULL
) {
500 setup_reason
= talloc_asprintf(context
,
501 "Setup failed: %s", context
->last_reason
);
505 for (test
= tcase
->tests
; test
; test
= test
->next
) {
506 if (setup_succeeded
) {
507 ret
&= internal_torture_run_test(context
, tcase
, test
,
508 tcase
->fixture_persistent
, restricted
);
510 context
->active_tcase
= tcase
;
511 context
->active_test
= test
;
512 torture_ui_test_start(context
, tcase
, test
);
513 torture_ui_test_result(context
, TORTURE_FAIL
, setup_reason
);
517 if (setup_succeeded
&& tcase
->fixture_persistent
&& tcase
->teardown
&&
518 !tcase
->teardown(context
, tcase
->data
)) {
522 context
->active_tcase
= NULL
;
523 context
->active_test
= NULL
;
525 if (context
->results
->ui_ops
->tcase_finish
)
526 context
->results
->ui_ops
->tcase_finish(context
, tcase
);
528 return (!setup_succeeded
) ? false : ret
;
531 bool torture_run_test(struct torture_context
*context
,
532 struct torture_tcase
*tcase
,
533 struct torture_test
*test
)
535 return internal_torture_run_test(context
, tcase
, test
, false, NULL
);
538 bool torture_run_test_restricted(struct torture_context
*context
,
539 struct torture_tcase
*tcase
,
540 struct torture_test
*test
,
541 const char **restricted
)
543 return internal_torture_run_test(context
, tcase
, test
, false, restricted
);
546 int torture_setting_int(struct torture_context
*test
, const char *name
,
549 return lpcfg_parm_int(test
->lp_ctx
, NULL
, "torture", name
, default_value
);
552 unsigned long torture_setting_ulong(struct torture_context
*test
,
554 unsigned long default_value
)
556 return lpcfg_parm_ulong(test
->lp_ctx
, NULL
, "torture", name
,
560 double torture_setting_double(struct torture_context
*test
, const char *name
,
561 double default_value
)
563 return lpcfg_parm_double(test
->lp_ctx
, NULL
, "torture", name
, default_value
);
566 bool torture_setting_bool(struct torture_context
*test
, const char *name
,
569 return lpcfg_parm_bool(test
->lp_ctx
, NULL
, "torture", name
, default_value
);
572 const char *torture_setting_string(struct torture_context
*test
,
574 const char *default_value
)
578 SMB_ASSERT(test
!= NULL
);
579 SMB_ASSERT(test
->lp_ctx
!= NULL
);
581 ret
= lpcfg_parm_string(test
->lp_ctx
, NULL
, "torture", name
);
584 return default_value
;
589 static bool wrap_test_with_simple_tcase_const (
590 struct torture_context
*torture_ctx
,
591 struct torture_tcase
*tcase
,
592 struct torture_test
*test
)
594 bool (*fn
) (struct torture_context
*, const void *tcase_data
);
598 return fn(torture_ctx
, test
->data
);
601 struct torture_tcase
*torture_suite_add_simple_tcase_const(
602 struct torture_suite
*suite
, const char *name
,
603 bool (*run
) (struct torture_context
*test
, const void *),
606 struct torture_tcase
*tcase
;
607 struct torture_test
*test
;
609 tcase
= torture_suite_add_tcase(suite
, name
);
611 test
= talloc(tcase
, struct torture_test
);
613 test
->name
= talloc_strdup(test
, name
);
614 test
->description
= NULL
;
615 test
->run
= wrap_test_with_simple_tcase_const
;
618 test
->dangerous
= false;
620 DLIST_ADD_END(tcase
->tests
, test
);
625 static bool wrap_simple_test(struct torture_context
*torture_ctx
,
626 struct torture_tcase
*tcase
,
627 struct torture_test
*test
)
629 bool (*fn
) (struct torture_context
*);
633 return fn(torture_ctx
);
636 struct torture_tcase
*torture_suite_add_simple_test(
637 struct torture_suite
*suite
,
639 bool (*run
) (struct torture_context
*test
))
641 struct torture_test
*test
;
642 struct torture_tcase
*tcase
;
644 tcase
= torture_suite_add_tcase(suite
, name
);
646 test
= talloc(tcase
, struct torture_test
);
648 test
->name
= talloc_strdup(test
, name
);
649 test
->description
= NULL
;
650 test
->run
= wrap_simple_test
;
652 test
->dangerous
= false;
654 DLIST_ADD_END(tcase
->tests
, test
);
660 * Add a child testsuite to a testsuite.
662 bool torture_suite_add_suite(struct torture_suite
*suite
,
663 struct torture_suite
*child
)
668 DLIST_ADD_END(suite
->children
, child
);
670 /* FIXME: Check for duplicates and return false if the
671 * added suite already exists as a child */
677 * Find the child testsuite with the specified name.
679 struct torture_suite
*torture_find_suite(struct torture_suite
*parent
,
682 struct torture_suite
*child
;
684 for (child
= parent
->children
; child
; child
= child
->next
)
685 if (!strcmp(child
->name
, name
))
691 static bool wrap_test_with_simple_test_const(struct torture_context
*torture_ctx
,
692 struct torture_tcase
*tcase
,
693 struct torture_test
*test
)
695 bool (*fn
) (struct torture_context
*, const void *tcase_data
);
699 return fn(torture_ctx
, tcase
->data
);
702 struct torture_test
*torture_tcase_add_simple_test_const(
703 struct torture_tcase
*tcase
,
705 bool (*run
) (struct torture_context
*test
,
706 const void *tcase_data
))
708 struct torture_test
*test
;
710 test
= talloc(tcase
, struct torture_test
);
712 test
->name
= talloc_strdup(test
, name
);
713 test
->description
= NULL
;
714 test
->run
= wrap_test_with_simple_test_const
;
717 test
->dangerous
= false;
719 DLIST_ADD_END(tcase
->tests
, test
);
724 static bool wrap_test_with_simple_test(struct torture_context
*torture_ctx
,
725 struct torture_tcase
*tcase
,
726 struct torture_test
*test
)
728 bool (*fn
) (struct torture_context
*, void *tcase_data
);
732 return fn(torture_ctx
, tcase
->data
);
735 struct torture_test
*torture_tcase_add_simple_test(struct torture_tcase
*tcase
,
737 bool (*run
) (struct torture_context
*test
, void *tcase_data
))
739 struct torture_test
*test
;
741 test
= talloc(tcase
, struct torture_test
);
743 test
->name
= talloc_strdup(test
, name
);
744 test
->description
= NULL
;
745 test
->run
= wrap_test_with_simple_test
;
748 test
->dangerous
= false;
750 DLIST_ADD_END(tcase
->tests
, test
);
755 void torture_ui_report_time(struct torture_context
*context
)
757 if (context
->results
->ui_ops
->report_time
)
758 context
->results
->ui_ops
->report_time(context
);