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(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
);
144 _PUBLIC_ NTSTATUS
torture_deltree_outputdir(struct torture_context
*tctx
)
146 SMB_ASSERT(tctx
->outputdir
!= NULL
);
147 if ((strcmp(tctx
->outputdir
, "/") == 0)
148 || (strcmp(tctx
->outputdir
, "") == 0)) {
149 return NT_STATUS_INVALID_PARAMETER
;
152 if (local_deltree(tctx
->outputdir
) == -1) {
154 return map_nt_error_from_unix(errno
);
156 return NT_STATUS_UNSUCCESSFUL
;
162 * Comment on the status/progress of a test
164 void torture_comment(struct torture_context
*context
, const char *comment
, ...)
169 if (!context
->results
->ui_ops
->comment
)
172 va_start(ap
, comment
);
173 tmp
= talloc_vasprintf(context
, comment
, ap
);
176 context
->results
->ui_ops
->comment(context
, tmp
);
182 * Print a warning about the current test
184 void torture_warning(struct torture_context
*context
, const char *comment
, ...)
189 if (!context
->results
->ui_ops
->warning
)
192 va_start(ap
, comment
);
193 tmp
= talloc_vasprintf(context
, comment
, ap
);
196 context
->results
->ui_ops
->warning(context
, tmp
);
202 * Store the result of a torture test.
204 void torture_result(struct torture_context
*context
,
205 enum torture_result result
, const char *fmt
, ...)
211 if (context
->last_reason
) {
212 torture_warning(context
, "%s", context
->last_reason
);
213 talloc_free(context
->last_reason
);
216 context
->last_result
= result
;
217 context
->last_reason
= talloc_vasprintf(context
, fmt
, ap
);
222 * Create a new torture suite
224 struct torture_suite
*torture_suite_create(TALLOC_CTX
*ctx
, const char *name
)
226 struct torture_suite
*suite
= talloc_zero(ctx
, struct torture_suite
);
228 suite
->name
= talloc_strdup(suite
, name
);
229 suite
->testcases
= NULL
;
230 suite
->children
= NULL
;
236 * Set the setup() and teardown() functions for a testcase.
238 void torture_tcase_set_fixture(struct torture_tcase
*tcase
,
239 bool (*setup
) (struct torture_context
*, void **),
240 bool (*teardown
) (struct torture_context
*, void *))
242 tcase
->setup
= setup
;
243 tcase
->teardown
= teardown
;
246 static bool wrap_test_with_testcase_const(struct torture_context
*torture_ctx
,
247 struct torture_tcase
*tcase
,
248 struct torture_test
*test
)
250 bool (*fn
) (struct torture_context
*,
251 const void *tcase_data
,
252 const void *test_data
);
256 return fn(torture_ctx
, tcase
->data
, test
->data
);
260 * Add a test that uses const data to a testcase
262 struct torture_test
*torture_tcase_add_test_const(struct torture_tcase
*tcase
,
264 bool (*run
) (struct torture_context
*, const void *tcase_data
,
265 const void *test_data
),
268 struct torture_test
*test
= talloc(tcase
, struct torture_test
);
270 test
->name
= talloc_strdup(test
, name
);
271 test
->description
= NULL
;
272 test
->run
= wrap_test_with_testcase_const
;
274 test
->dangerous
= false;
277 DLIST_ADD_END(tcase
->tests
, test
, struct torture_test
*);
285 bool torture_suite_init_tcase(struct torture_suite
*suite
,
286 struct torture_tcase
*tcase
,
289 tcase
->name
= talloc_strdup(tcase
, name
);
290 tcase
->description
= NULL
;
292 tcase
->teardown
= NULL
;
293 tcase
->fixture_persistent
= true;
296 DLIST_ADD_END(suite
->testcases
, tcase
, struct torture_tcase
*);
302 struct torture_tcase
*torture_suite_add_tcase(struct torture_suite
*suite
,
305 struct torture_tcase
*tcase
= talloc(suite
, struct torture_tcase
);
307 if (!torture_suite_init_tcase(suite
, tcase
, name
))
313 int torture_suite_children_count(const struct torture_suite
*suite
)
316 struct torture_tcase
*tcase
;
317 struct torture_test
*test
;
318 struct torture_suite
*tsuite
;
319 for (tcase
= suite
->testcases
; tcase
; tcase
= tcase
->next
) {
320 for (test
= tcase
->tests
; test
; test
= test
->next
) {
324 for (tsuite
= suite
->children
; tsuite
; tsuite
= tsuite
->next
) {
331 * Run a torture test suite.
333 bool torture_run_suite(struct torture_context
*context
,
334 struct torture_suite
*suite
)
336 return torture_run_suite_restricted(context
, suite
, NULL
);
339 bool torture_run_suite_restricted(struct torture_context
*context
,
340 struct torture_suite
*suite
, const char **restricted
)
343 struct torture_tcase
*tcase
;
344 struct torture_suite
*tsuite
;
346 if (context
->results
->ui_ops
->suite_start
)
347 context
->results
->ui_ops
->suite_start(context
, suite
);
349 /* FIXME: Adjust torture_suite_children_count if restricted != NULL */
350 context
->results
->ui_ops
->progress(context
,
351 torture_suite_children_count(suite
), TORTURE_PROGRESS_SET
);
353 for (tcase
= suite
->testcases
; tcase
; tcase
= tcase
->next
) {
354 ret
&= torture_run_tcase_restricted(context
, tcase
, restricted
);
357 for (tsuite
= suite
->children
; tsuite
; tsuite
= tsuite
->next
) {
358 context
->results
->ui_ops
->progress(context
, 0, TORTURE_PROGRESS_PUSH
);
359 ret
&= torture_run_suite_restricted(context
, tsuite
, restricted
);
360 context
->results
->ui_ops
->progress(context
, 0, TORTURE_PROGRESS_POP
);
363 if (context
->results
->ui_ops
->suite_finish
)
364 context
->results
->ui_ops
->suite_finish(context
, suite
);
369 void torture_ui_test_start(struct torture_context
*context
,
370 struct torture_tcase
*tcase
,
371 struct torture_test
*test
)
373 if (context
->results
->ui_ops
->test_start
)
374 context
->results
->ui_ops
->test_start(context
, tcase
, test
);
377 void torture_ui_test_result(struct torture_context
*context
,
378 enum torture_result result
,
381 if (context
->results
->ui_ops
->test_result
)
382 context
->results
->ui_ops
->test_result(context
, result
, comment
);
384 if (result
== TORTURE_ERROR
|| result
== TORTURE_FAIL
)
385 context
->results
->returncode
= false;
388 static bool test_needs_running(const char *name
, const char **restricted
)
391 if (restricted
== NULL
)
393 for (i
= 0; restricted
[i
]; i
++) {
394 if (!strcmp(name
, restricted
[i
]))
400 static bool internal_torture_run_test(struct torture_context
*context
,
401 struct torture_tcase
*tcase
,
402 struct torture_test
*test
,
404 const char **restricted
)
407 char *subunit_testname
= NULL
;
409 if (tcase
== NULL
|| strcmp(test
->name
, tcase
->name
) != 0) {
410 subunit_testname
= talloc_asprintf(context
, "%s.%s", tcase
->name
, test
->name
);
412 subunit_testname
= talloc_strdup(context
, test
->name
);
415 if (!test_needs_running(subunit_testname
, restricted
))
418 context
->active_tcase
= tcase
;
419 context
->active_test
= test
;
421 torture_ui_test_start(context
, tcase
, test
);
423 context
->last_reason
= NULL
;
424 context
->last_result
= TORTURE_OK
;
426 if (!already_setup
&& tcase
->setup
&&
427 !tcase
->setup(context
, &(tcase
->data
))) {
428 if (context
->last_reason
== NULL
)
429 context
->last_reason
= talloc_strdup(context
, "Setup failure");
430 context
->last_result
= TORTURE_ERROR
;
432 } else if (test
->dangerous
&&
433 !torture_setting_bool(context
, "dangerous", false)) {
434 context
->last_result
= TORTURE_SKIP
;
435 context
->last_reason
= talloc_asprintf(context
,
436 "disabled %s - enable dangerous tests to use", test
->name
);
439 success
= test
->run(context
, tcase
, test
);
441 if (!success
&& context
->last_result
== TORTURE_OK
) {
442 if (context
->last_reason
== NULL
)
443 context
->last_reason
= talloc_strdup(context
, "Unknown error/failure");
444 context
->last_result
= TORTURE_ERROR
;
448 if (!already_setup
&& tcase
->teardown
&& !tcase
->teardown(context
, tcase
->data
)) {
449 if (context
->last_reason
== NULL
)
450 context
->last_reason
= talloc_strdup(context
, "Setup failure");
451 context
->last_result
= TORTURE_ERROR
;
455 torture_ui_test_result(context
, context
->last_result
,
456 context
->last_reason
);
458 talloc_free(context
->last_reason
);
460 context
->active_test
= NULL
;
461 context
->active_tcase
= NULL
;
466 bool torture_run_tcase(struct torture_context
*context
,
467 struct torture_tcase
*tcase
)
469 return torture_run_tcase_restricted(context
, tcase
, NULL
);
472 bool torture_run_tcase_restricted(struct torture_context
*context
,
473 struct torture_tcase
*tcase
, const char **restricted
)
476 struct torture_test
*test
;
477 bool setup_succeeded
= true;
478 const char * setup_reason
= "Setup failed";
480 context
->active_tcase
= tcase
;
481 if (context
->results
->ui_ops
->tcase_start
)
482 context
->results
->ui_ops
->tcase_start(context
, tcase
);
484 if (tcase
->fixture_persistent
&& tcase
->setup
) {
485 setup_succeeded
= tcase
->setup(context
, &tcase
->data
);
488 if (!setup_succeeded
) {
489 /* Uh-oh. The setup failed, so we can't run any of the tests
490 * in this testcase. The subunit format doesn't specify what
491 * to do here, so we keep the failure reason, and manually
492 * use it to fail every test.
494 if (context
->last_reason
!= NULL
) {
495 setup_reason
= talloc_asprintf(context
,
496 "Setup failed: %s", context
->last_reason
);
500 for (test
= tcase
->tests
; test
; test
= test
->next
) {
501 if (setup_succeeded
) {
502 ret
&= internal_torture_run_test(context
, tcase
, test
,
503 tcase
->fixture_persistent
, restricted
);
505 context
->active_tcase
= tcase
;
506 context
->active_test
= test
;
507 torture_ui_test_start(context
, tcase
, test
);
508 torture_ui_test_result(context
, TORTURE_FAIL
, setup_reason
);
512 if (setup_succeeded
&& tcase
->fixture_persistent
&& tcase
->teardown
&&
513 !tcase
->teardown(context
, tcase
->data
)) {
517 context
->active_tcase
= NULL
;
518 context
->active_test
= NULL
;
520 if (context
->results
->ui_ops
->tcase_finish
)
521 context
->results
->ui_ops
->tcase_finish(context
, tcase
);
523 return (!setup_succeeded
) ? false : ret
;
526 bool torture_run_test(struct torture_context
*context
,
527 struct torture_tcase
*tcase
,
528 struct torture_test
*test
)
530 return internal_torture_run_test(context
, tcase
, test
, false, NULL
);
533 bool torture_run_test_restricted(struct torture_context
*context
,
534 struct torture_tcase
*tcase
,
535 struct torture_test
*test
,
536 const char **restricted
)
538 return internal_torture_run_test(context
, tcase
, test
, false, restricted
);
541 int torture_setting_int(struct torture_context
*test
, const char *name
,
544 return lpcfg_parm_int(test
->lp_ctx
, NULL
, "torture", name
, default_value
);
547 unsigned long torture_setting_ulong(struct torture_context
*test
,
549 unsigned long default_value
)
551 return lpcfg_parm_ulong(test
->lp_ctx
, NULL
, "torture", name
,
555 double torture_setting_double(struct torture_context
*test
, const char *name
,
556 double default_value
)
558 return lpcfg_parm_double(test
->lp_ctx
, NULL
, "torture", name
, default_value
);
561 bool torture_setting_bool(struct torture_context
*test
, const char *name
,
564 return lpcfg_parm_bool(test
->lp_ctx
, NULL
, "torture", name
, default_value
);
567 const char *torture_setting_string(struct torture_context
*test
,
569 const char *default_value
)
573 SMB_ASSERT(test
!= NULL
);
574 SMB_ASSERT(test
->lp_ctx
!= NULL
);
576 ret
= lpcfg_parm_string(test
->lp_ctx
, NULL
, "torture", name
);
579 return default_value
;
584 static bool wrap_test_with_simple_tcase_const (
585 struct torture_context
*torture_ctx
,
586 struct torture_tcase
*tcase
,
587 struct torture_test
*test
)
589 bool (*fn
) (struct torture_context
*, const void *tcase_data
);
593 return fn(torture_ctx
, test
->data
);
596 struct torture_tcase
*torture_suite_add_simple_tcase_const(
597 struct torture_suite
*suite
, const char *name
,
598 bool (*run
) (struct torture_context
*test
, const void *),
601 struct torture_tcase
*tcase
;
602 struct torture_test
*test
;
604 tcase
= torture_suite_add_tcase(suite
, name
);
606 test
= talloc(tcase
, struct torture_test
);
608 test
->name
= talloc_strdup(test
, name
);
609 test
->description
= NULL
;
610 test
->run
= wrap_test_with_simple_tcase_const
;
613 test
->dangerous
= false;
615 DLIST_ADD_END(tcase
->tests
, test
, struct torture_test
*);
620 static bool wrap_simple_test(struct torture_context
*torture_ctx
,
621 struct torture_tcase
*tcase
,
622 struct torture_test
*test
)
624 bool (*fn
) (struct torture_context
*);
628 return fn(torture_ctx
);
631 struct torture_tcase
*torture_suite_add_simple_test(
632 struct torture_suite
*suite
,
634 bool (*run
) (struct torture_context
*test
))
636 struct torture_test
*test
;
637 struct torture_tcase
*tcase
;
639 tcase
= torture_suite_add_tcase(suite
, name
);
641 test
= talloc(tcase
, struct torture_test
);
643 test
->name
= talloc_strdup(test
, name
);
644 test
->description
= NULL
;
645 test
->run
= wrap_simple_test
;
647 test
->dangerous
= false;
649 DLIST_ADD_END(tcase
->tests
, test
, struct torture_test
*);
655 * Add a child testsuite to a testsuite.
657 bool torture_suite_add_suite(struct torture_suite
*suite
,
658 struct torture_suite
*child
)
663 DLIST_ADD_END(suite
->children
, child
, struct torture_suite
*);
665 /* FIXME: Check for duplicates and return false if the
666 * added suite already exists as a child */
672 * Find the child testsuite with the specified name.
674 struct torture_suite
*torture_find_suite(struct torture_suite
*parent
,
677 struct torture_suite
*child
;
679 for (child
= parent
->children
; child
; child
= child
->next
)
680 if (!strcmp(child
->name
, name
))
686 static bool wrap_test_with_simple_test_const(struct torture_context
*torture_ctx
,
687 struct torture_tcase
*tcase
,
688 struct torture_test
*test
)
690 bool (*fn
) (struct torture_context
*, const void *tcase_data
);
694 return fn(torture_ctx
, tcase
->data
);
697 struct torture_test
*torture_tcase_add_simple_test_const(
698 struct torture_tcase
*tcase
,
700 bool (*run
) (struct torture_context
*test
,
701 const void *tcase_data
))
703 struct torture_test
*test
;
705 test
= talloc(tcase
, struct torture_test
);
707 test
->name
= talloc_strdup(test
, name
);
708 test
->description
= NULL
;
709 test
->run
= wrap_test_with_simple_test_const
;
712 test
->dangerous
= false;
714 DLIST_ADD_END(tcase
->tests
, test
, struct torture_test
*);
719 static bool wrap_test_with_simple_test(struct torture_context
*torture_ctx
,
720 struct torture_tcase
*tcase
,
721 struct torture_test
*test
)
723 bool (*fn
) (struct torture_context
*, void *tcase_data
);
727 return fn(torture_ctx
, tcase
->data
);
730 struct torture_test
*torture_tcase_add_simple_test(struct torture_tcase
*tcase
,
732 bool (*run
) (struct torture_context
*test
, void *tcase_data
))
734 struct torture_test
*test
;
736 test
= talloc(tcase
, struct torture_test
);
738 test
->name
= talloc_strdup(test
, name
);
739 test
->description
= NULL
;
740 test
->run
= wrap_test_with_simple_test
;
743 test
->dangerous
= false;
745 DLIST_ADD_END(tcase
->tests
, test
, struct torture_test
*);
750 void torture_ui_report_time(struct torture_context
*context
)
752 if (context
->results
->ui_ops
->report_time
)
753 context
->results
->ui_ops
->report_time(context
);