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
, struct torture_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
, struct torture_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
, "Unknown error/failure");
447 context
->last_result
= TORTURE_ERROR
;
451 if (!already_setup
&& tcase
->teardown
&& !tcase
->teardown(context
, tcase
->data
)) {
452 if (context
->last_reason
== NULL
)
453 context
->last_reason
= talloc_strdup(context
, "Setup failure");
454 context
->last_result
= TORTURE_ERROR
;
458 torture_ui_test_result(context
, context
->last_result
,
459 context
->last_reason
);
461 talloc_free(context
->last_reason
);
463 context
->active_test
= NULL
;
464 context
->active_tcase
= NULL
;
469 bool torture_run_tcase(struct torture_context
*context
,
470 struct torture_tcase
*tcase
)
472 return torture_run_tcase_restricted(context
, tcase
, NULL
);
475 bool torture_run_tcase_restricted(struct torture_context
*context
,
476 struct torture_tcase
*tcase
, const char **restricted
)
479 struct torture_test
*test
;
480 bool setup_succeeded
= true;
481 const char * setup_reason
= "Setup failed";
483 context
->active_tcase
= tcase
;
484 if (context
->results
->ui_ops
->tcase_start
)
485 context
->results
->ui_ops
->tcase_start(context
, tcase
);
487 if (tcase
->fixture_persistent
&& tcase
->setup
) {
488 setup_succeeded
= tcase
->setup(context
, &tcase
->data
);
491 if (!setup_succeeded
) {
492 /* Uh-oh. The setup failed, so we can't run any of the tests
493 * in this testcase. The subunit format doesn't specify what
494 * to do here, so we keep the failure reason, and manually
495 * use it to fail every test.
497 if (context
->last_reason
!= NULL
) {
498 setup_reason
= talloc_asprintf(context
,
499 "Setup failed: %s", context
->last_reason
);
503 for (test
= tcase
->tests
; test
; test
= test
->next
) {
504 if (setup_succeeded
) {
505 ret
&= internal_torture_run_test(context
, tcase
, test
,
506 tcase
->fixture_persistent
, restricted
);
508 context
->active_tcase
= tcase
;
509 context
->active_test
= test
;
510 torture_ui_test_start(context
, tcase
, test
);
511 torture_ui_test_result(context
, TORTURE_FAIL
, setup_reason
);
515 if (setup_succeeded
&& tcase
->fixture_persistent
&& tcase
->teardown
&&
516 !tcase
->teardown(context
, tcase
->data
)) {
520 context
->active_tcase
= NULL
;
521 context
->active_test
= NULL
;
523 if (context
->results
->ui_ops
->tcase_finish
)
524 context
->results
->ui_ops
->tcase_finish(context
, tcase
);
526 return (!setup_succeeded
) ? false : ret
;
529 bool torture_run_test(struct torture_context
*context
,
530 struct torture_tcase
*tcase
,
531 struct torture_test
*test
)
533 return internal_torture_run_test(context
, tcase
, test
, false, NULL
);
536 bool torture_run_test_restricted(struct torture_context
*context
,
537 struct torture_tcase
*tcase
,
538 struct torture_test
*test
,
539 const char **restricted
)
541 return internal_torture_run_test(context
, tcase
, test
, false, restricted
);
544 int torture_setting_int(struct torture_context
*test
, const char *name
,
547 return lpcfg_parm_int(test
->lp_ctx
, NULL
, "torture", name
, default_value
);
550 unsigned long torture_setting_ulong(struct torture_context
*test
,
552 unsigned long default_value
)
554 return lpcfg_parm_ulong(test
->lp_ctx
, NULL
, "torture", name
,
558 double torture_setting_double(struct torture_context
*test
, const char *name
,
559 double default_value
)
561 return lpcfg_parm_double(test
->lp_ctx
, NULL
, "torture", name
, default_value
);
564 bool torture_setting_bool(struct torture_context
*test
, const char *name
,
567 return lpcfg_parm_bool(test
->lp_ctx
, NULL
, "torture", name
, default_value
);
570 const char *torture_setting_string(struct torture_context
*test
,
572 const char *default_value
)
576 SMB_ASSERT(test
!= NULL
);
577 SMB_ASSERT(test
->lp_ctx
!= NULL
);
579 ret
= lpcfg_parm_string(test
->lp_ctx
, NULL
, "torture", name
);
582 return default_value
;
587 static bool wrap_test_with_simple_tcase_const (
588 struct torture_context
*torture_ctx
,
589 struct torture_tcase
*tcase
,
590 struct torture_test
*test
)
592 bool (*fn
) (struct torture_context
*, const void *tcase_data
);
596 return fn(torture_ctx
, test
->data
);
599 struct torture_tcase
*torture_suite_add_simple_tcase_const(
600 struct torture_suite
*suite
, const char *name
,
601 bool (*run
) (struct torture_context
*test
, const void *),
604 struct torture_tcase
*tcase
;
605 struct torture_test
*test
;
607 tcase
= torture_suite_add_tcase(suite
, name
);
609 test
= talloc(tcase
, struct torture_test
);
611 test
->name
= talloc_strdup(test
, name
);
612 test
->description
= NULL
;
613 test
->run
= wrap_test_with_simple_tcase_const
;
616 test
->dangerous
= false;
618 DLIST_ADD_END(tcase
->tests
, test
, struct torture_test
*);
623 static bool wrap_simple_test(struct torture_context
*torture_ctx
,
624 struct torture_tcase
*tcase
,
625 struct torture_test
*test
)
627 bool (*fn
) (struct torture_context
*);
631 return fn(torture_ctx
);
634 struct torture_tcase
*torture_suite_add_simple_test(
635 struct torture_suite
*suite
,
637 bool (*run
) (struct torture_context
*test
))
639 struct torture_test
*test
;
640 struct torture_tcase
*tcase
;
642 tcase
= torture_suite_add_tcase(suite
, name
);
644 test
= talloc(tcase
, struct torture_test
);
646 test
->name
= talloc_strdup(test
, name
);
647 test
->description
= NULL
;
648 test
->run
= wrap_simple_test
;
650 test
->dangerous
= false;
652 DLIST_ADD_END(tcase
->tests
, test
, struct torture_test
*);
658 * Add a child testsuite to a testsuite.
660 bool torture_suite_add_suite(struct torture_suite
*suite
,
661 struct torture_suite
*child
)
666 DLIST_ADD_END(suite
->children
, child
, struct torture_suite
*);
668 /* FIXME: Check for duplicates and return false if the
669 * added suite already exists as a child */
675 * Find the child testsuite with the specified name.
677 struct torture_suite
*torture_find_suite(struct torture_suite
*parent
,
680 struct torture_suite
*child
;
682 for (child
= parent
->children
; child
; child
= child
->next
)
683 if (!strcmp(child
->name
, name
))
689 static bool wrap_test_with_simple_test_const(struct torture_context
*torture_ctx
,
690 struct torture_tcase
*tcase
,
691 struct torture_test
*test
)
693 bool (*fn
) (struct torture_context
*, const void *tcase_data
);
697 return fn(torture_ctx
, tcase
->data
);
700 struct torture_test
*torture_tcase_add_simple_test_const(
701 struct torture_tcase
*tcase
,
703 bool (*run
) (struct torture_context
*test
,
704 const void *tcase_data
))
706 struct torture_test
*test
;
708 test
= talloc(tcase
, struct torture_test
);
710 test
->name
= talloc_strdup(test
, name
);
711 test
->description
= NULL
;
712 test
->run
= wrap_test_with_simple_test_const
;
715 test
->dangerous
= false;
717 DLIST_ADD_END(tcase
->tests
, test
, struct torture_test
*);
722 static bool wrap_test_with_simple_test(struct torture_context
*torture_ctx
,
723 struct torture_tcase
*tcase
,
724 struct torture_test
*test
)
726 bool (*fn
) (struct torture_context
*, void *tcase_data
);
730 return fn(torture_ctx
, tcase
->data
);
733 struct torture_test
*torture_tcase_add_simple_test(struct torture_tcase
*tcase
,
735 bool (*run
) (struct torture_context
*test
, void *tcase_data
))
737 struct torture_test
*test
;
739 test
= talloc(tcase
, struct torture_test
);
741 test
->name
= talloc_strdup(test
, name
);
742 test
->description
= NULL
;
743 test
->run
= wrap_test_with_simple_test
;
746 test
->dangerous
= false;
748 DLIST_ADD_END(tcase
->tests
, test
, struct torture_test
*);
753 void torture_ui_report_time(struct torture_context
*context
)
755 if (context
->results
->ui_ops
->report_time
)
756 context
->results
->ui_ops
->report_time(context
);