2 # Bootstrap Samba and run a number of tests against it.
3 # Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
4 # Published under the GNU GPL, v3 or later.
10 selftest - Samba test runner
16 selftest [--srcdir=DIR] [--builddir=DIR] [--target=samba4|samba3|win] [--socket-wrapper] [--quick] [--one] [--prefix=prefix] [--immediate] [TESTS]
20 A simple test runner. TESTS is a regular expression with tests to run.
28 Show list of available options.
34 =item I<--builddir=DIR>
40 Change directory to run tests in. Default is 'st'.
44 Show errors as soon as they happen rather than at the end of the test run.
46 =item I<--target samba4|samba3|win>
48 Specify test target against which to run. Default is 'samba4'.
52 Run only a limited number of tests. Intended to run in about 30 seconds on
53 moderately recent systems.
55 =item I<--socket-wrapper>
57 Use socket wrapper library for communication with server. Only works
58 when the server is running locally.
60 Will prevent TCP and UDP ports being opened on the local host but
61 (transparently) redirects these calls to use unix domain sockets.
63 =item I<--expected-failures>
65 Specify a file containing a list of tests that are expected to fail. Failures for
66 these tests will be counted as successes, successes will be counted as failures.
68 The format for the file is, one entry per line:
70 TESTSUITE-NAME/TEST-NAME
74 Specify a file containing a list of tests that should be skipped. Possible candidates are
75 tests that segfault the server, flip or don't end.
79 Abort as soon as one test fails.
87 =item I<SMBD_VALGRIND>
89 =item I<TORTURE_MAXTIME>
101 selftest is licensed under the GNU General Public License L<http://www.gnu.org/licenses/gpl.html>.
111 use FindBin qw($RealBin $Script);
115 use Cwd qw(abs_path);
122 my $opt_target = "samba4";
124 my $opt_socket_wrapper = 0;
125 my $opt_socket_wrapper_pcap = undef;
126 my $opt_socket_wrapper_keep_pcap = undef;
128 my $opt_immediate = 0;
129 my $opt_expected_failures = undef;
130 my $opt_skip = undef;
134 my $opt_analyse_cmd = undef;
135 my $opt_resetup_env = undef;
136 my $opt_bindir = undef;
137 my $opt_no_lazy_setup = undef;
143 my $suitesfailed = [];
145 my @expected_failures = ();
153 TESTS_UNEXPECTED_OK => 0,
154 TESTS_EXPECTED_OK => 0,
155 TESTS_UNEXPECTED_FAIL => 0,
156 TESTS_EXPECTED_FAIL => 0,
160 sub expecting_failure($)
162 my $fullname = shift;
164 foreach (@expected_failures) {
165 return 1 if ($fullname =~ /$_/);
173 my $fullname = shift;
176 return 1 if ($fullname =~ /$_/);
184 my $test_output = {};
186 sub buildfarm_start_testsuite($)
191 $out .= "--==--==--==--==--==--==--==--==--==--==--\n";
192 $out .= "Running test $state->{NAME} (level 0 stdout)\n";
193 $out .= "--==--==--==--==--==--==--==--==--==--==--\n";
194 $out .= scalar(localtime())."\n";
195 $out .= "SELFTEST RUNTIME: " . ($state->{START} - $start) . "s\n";
196 $out .= "NAME: $state->{NAME}\n";
197 $out .= "CMD: $state->{CMD}\n";
199 $test_output->{$state->{NAME}} = "";
204 sub buildfarm_output_msg($$)
206 my ($state, $output) = @_;
208 $test_output->{$state->{NAME}} .= $output;
211 sub buildfarm_end_testsuite($$$)
213 my ($state, $expected_ret, $ret) = @_;
216 $out .= "TEST RUNTIME: " . (time() - $state->{START}) . "s\n";
218 if ($ret == $expected_ret) {
221 $out .= "ERROR: $ret";
222 $out .= $test_output->{$state->{NAME}};
225 $out .= "PCAP FILE: $state->{PCAP_FILE}\n" if defined($state->{PCAP_FILE});
227 $out .= getlog_env($state->{ENVNAME});
229 $out .= "==========================================\n";
230 if ($ret == $expected_ret) {
231 $out .= "TEST PASSED: $state->{NAME}\n";
233 $out .= "TEST FAILED: $state->{NAME} (status $ret)\n";
235 $out .= "==========================================\n";
240 sub buildfarm_start_test($$)
242 my ($state, $testname) = @_;
245 sub buildfarm_end_test($$$$)
247 my ($state, $testname, $result, $expected) = @_;
250 my $buildfarm_msg_ops = {
251 start_testsuite => \&buildfarm_start_testsuite,
252 output_msg => \&buildfarm_output_msg,
253 end_testsuite => \&buildfarm_end_testsuite,
254 start_test => \&buildfarm_start_test,
255 end_test => \&buildfarm_end_test,
258 sub plain_output_msg($$);
260 sub plain_start_testsuite($)
265 $out .= "[$state->{INDEX}/$state->{TOTAL} in " . ($state->{START} - $start) . "s";
266 $out .= ", ".($#$suitesfailed+1)." errors" if ($#$suitesfailed+1 > 0);
267 $out .= "] $state->{NAME}\n";
269 $test_output->{$state->{NAME}} = "" unless $opt_verbose;
271 plain_output_msg($state, "CMD: $state->{CMD}\n");
276 sub plain_output_msg($$)
278 my ($state, $output) = @_;
283 $test_output->{$state->{NAME}} .= $output;
287 sub plain_end_testsuite($$$)
289 my ($state, $expected_ret, $ret) = @_;
292 if ($ret != $expected_ret) {
293 plain_output_msg($state, "ERROR: $ret\n");
296 if ($ret != $expected_ret and ($opt_immediate or $opt_one) and not $opt_verbose) {
297 $out .= $test_output->{$state->{NAME}};
300 if (not $opt_socket_wrapper_keep_pcap and defined($state->{PCAP_FILE})) {
301 $out .= "PCAP FILE: $state->{PCAP_FILE}\n";
304 $out .= getlog_env($state->{ENVNAME});
309 sub plain_start_test($$)
311 my ($state, $testname) = @_;
314 sub plain_end_test($$$$)
316 my ($state, $testname, $result, $expected) = @_;
319 my $plain_msg_ops = {
320 start_testsuite => \&plain_start_testsuite,
321 output_msg => \&plain_output_msg,
322 end_testsuite => \&plain_end_testsuite,
323 start_test => \&plain_start_test,
324 end_test => \&plain_end_test,
331 return unless ($opt_socket_wrapper_pcap);
332 return unless defined($ENV{SOCKET_WRAPPER_PCAP_DIR});
334 my $fname = sprintf("t%03u_%s", $state->{INDEX}, $state->{NAME});
335 $fname =~ s%[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\-]%_%g;
337 $state->{PCAP_FILE} = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/$fname.pcap";
339 SocketWrapper::setup_pcap($state->{PCAP_FILE});
342 sub cleanup_pcap($$$)
344 my ($state, $expected_ret, $ret) = @_;
346 return unless ($opt_socket_wrapper_pcap);
347 return if ($opt_socket_wrapper_keep_pcap);
348 return unless ($expected_ret == $ret);
349 return unless defined($state->{PCAP_FILE});
351 unlink($state->{PCAP_FILE});
352 $state->{PCAP_FILE} = undef;
357 my ($envname, $name, $cmd, $i, $totalsuites, $msg_ops) = @_;
358 my $expected_ret = 1;
365 TOTAL => $totalsuites,
369 setup_pcap($msg_state);
371 $msg_ops->{start_testsuite}->($msg_state);
373 open(RESULT, "$cmd 2>&1|");
375 $msg_ops->{output_msg}->($msg_state, $_);
376 if (/^test: (.+)\n/) {
377 $open_tests->{$1} = 1;
378 $msg_ops->{start_test}->($msg_state, $1);
379 } elsif (/^(success|failure|skip|error): (.*?)( \[)?\n/) {
381 if ($1 eq "success") {
382 delete $open_tests->{$2};
383 if (expecting_failure("$name/$2")) {
384 $statistics->{TESTS_UNEXPECTED_OK}++;
385 $msg_ops->{end_test}->($msg_state, $2, $1, 1);
387 $statistics->{TESTS_EXPECTED_OK}++;
388 $msg_ops->{end_test}->($msg_state, $2, $1, 0);
390 } elsif ($1 eq "failure") {
391 delete $open_tests->{$2};
392 if (expecting_failure("$name/$2")) {
393 $statistics->{TESTS_EXPECTED_FAIL}++;
394 $msg_ops->{end_test}->($msg_state, $2, $1, 0);
397 print "n:$name/$2\n";
398 $statistics->{TESTS_UNEXPECTED_FAIL}++;
399 $msg_ops->{end_test}->($msg_state, $2, $1, 1);
401 } elsif ($1 eq "skip") {
402 delete $open_tests->{$2};
403 $msg_ops->{end_test}->($msg_state, $2, $1, 0);
404 } elsif ($1 eq "error") {
405 $statistics->{TESTS_ERROR}++;
406 delete $open_tests->{$2};
407 $msg_ops->{end_test}->($msg_state, $2, $1, 1);
412 foreach (keys %$open_tests) {
413 $msg_ops->{end_test}->($msg_state, $_, "error", 1);
414 $msg_ops->{output_msg}->($msg_state, "$_ was started but never finished!");
415 $statistics->{TESTS_ERROR}++;
417 my $ret = close(RESULT);
419 cleanup_pcap($msg_state, $expected_ret, $ret);
421 $msg_ops->{end_testsuite}->($msg_state, $expected_ret, $ret);
423 if ($ret != $expected_ret) {
424 push(@$suitesfailed, $name);
425 $statistics->{SUITES_FAIL}++;
426 exit(1) if ($opt_one);
428 $statistics->{SUITES_OK}++;
431 return ($ret == $expected_ret);
436 print "Samba test runner
437 Copyright (C) Jelmer Vernooij <jelmer\@samba.org>
439 Usage: $Script [OPTIONS] PREFIX
442 --help this help page
443 --target=samba4|samba3|win Samba version to target
446 --prefix=DIR prefix to run tests in [st]
447 --srcdir=DIR source directory [.]
448 --builddir=DIR output directory [.]
451 --socket-wrapper-pcap=DIR save traffic to pcap directories
452 --socket-wrapper-keep-pcap keep all pcap files, not just those for tests that
454 --socket-wrapper enable socket wrapper
455 --expected-failures=FILE specify list of tests that is guaranteed to fail
458 --ldap=openldap|fedora-ds back smbd onto specified ldap server
461 --bindir=PATH path to binaries
464 --quick run quick overall test
465 --one abort when the first test fails
466 --immediate print test output for failed tests during run
468 --analyse-cmd CMD command to run after each test
473 my $result = GetOptions (
474 'help|h|?' => \$opt_help,
475 'target=s' => \$opt_target,
476 'prefix=s' => \$prefix,
477 'socket-wrapper' => \$opt_socket_wrapper,
478 'socket-wrapper-pcap' => \$opt_socket_wrapper_pcap,
479 'socket-wrapper-keep-pcap' => \$opt_socket_wrapper_keep_pcap,
480 'quick' => \$opt_quick,
482 'immediate' => \$opt_immediate,
483 'expected-failures=s' => \$opt_expected_failures,
484 'skip=s' => \$opt_skip,
485 'srcdir=s' => \$srcdir,
486 'builddir=s' => \$builddir,
487 'verbose' => \$opt_verbose,
488 'testenv' => \$opt_testenv,
490 'analyse-cmd=s' => \$opt_analyse_cmd,
491 'no-lazy-setup' => \$opt_no_lazy_setup,
492 'resetup-environment' => \$opt_resetup_env,
493 'bindir:s' => \$opt_bindir,
496 exit(1) if (not $result);
498 ShowHelp() if ($opt_help);
502 # quick hack to disable rpc validation when using valgrind - its way too slow
503 unless (defined($ENV{VALGRIND})) {
504 $ENV{VALIDATE} = "validate";
505 $ENV{MALLOC_CHECK_} = 2;
508 my $old_pwd = "$RealBin/..";
510 # Backwards compatibility:
511 if (defined($ENV{TEST_LDAP}) and $ENV{TEST_LDAP} eq "yes") {
512 if (defined($ENV{FEDORA_DS_PREFIX})) {
519 my $torture_maxtime = ($ENV{TORTURE_MAXTIME} or 1200);
522 $torture_maxtime *= 2;
529 die("using an empty prefix isn't allowed") unless $prefix ne "";
531 #Ensure we have the test prefix around
532 mkdir($prefix, 0777) unless -d $prefix;
534 my $prefix_abs = abs_path($prefix);
535 my $srcdir_abs = abs_path($srcdir);
537 die("using an empty absolute prefix isn't allowed") unless $prefix_abs ne "";
538 die("using '/' as absolute prefix isn't allowed") unless $prefix_abs ne "/";
540 $ENV{PREFIX} = $prefix;
541 $ENV{PREFIX_ABS} = $prefix_abs;
542 $ENV{SRCDIR} = $srcdir;
543 $ENV{SRCDIR_ABS} = $srcdir_abs;
545 my $tls_enabled = not $opt_quick;
546 my $from_build_farm = (defined($ENV{RUN_FROM_BUILD_FARM}) and
547 ($ENV{RUN_FROM_BUILD_FARM} eq "yes"));
549 $ENV{TLS_ENABLED} = ($tls_enabled?"yes":"no");
550 $ENV{LD_LDB_MODULE_PATH} = "$old_pwd/bin/modules/ldb";
551 $ENV{LD_SAMBA_MODULE_PATH} = "$old_pwd/bin/modules";
552 if (defined($ENV{LD_LIBRARY_PATH})) {
553 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared:$ENV{LD_LIBRARY_PATH}";
555 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared";
557 if (defined($ENV{PKG_CONFIG_PATH})) {
558 $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig:$ENV{PKG_CONFIG_PATH}";
560 $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig";
562 $ENV{PATH} = "$old_pwd/bin:$ENV{PATH}";
565 if ($opt_socket_wrapper_pcap) {
566 # Socket wrapper pcap implies socket wrapper
567 $opt_socket_wrapper = 1;
570 my $socket_wrapper_dir;
571 if ($opt_socket_wrapper) {
572 $socket_wrapper_dir = SocketWrapper::setup_dir("$prefix/w", $opt_socket_wrapper_pcap);
573 print "SOCKET_WRAPPER_DIR=$socket_wrapper_dir\n";
575 warn("Not using socket wrapper, but also not running as root. Will not be able to listen on proper ports") unless $< == 0;
580 if ($opt_target eq "samba4") {
581 $target = new Samba4("$srcdir/bin", $ldap, "$srcdir/setup");
582 } elsif ($opt_target eq "samba3") {
583 if ($opt_socket_wrapper and `smbd -b | grep SOCKET_WRAPPER` eq "") {
584 die("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'. Exiting....");
587 $target = new Samba3($opt_bindir);
588 } elsif ($opt_target eq "win") {
589 die("Windows tests will not run with socket wrapper enabled.")
590 if ($opt_socket_wrapper);
591 $target = new Windows();
594 if (defined($opt_expected_failures)) {
595 open(KNOWN, "<$opt_expected_failures") or die("unable to read known failures file: $!");
599 push (@expected_failures, $_); }
603 if (defined($opt_skip)) {
604 open(SKIP, "<$opt_skip") or die("unable to read skip file: $!");
612 my $interfaces = join(',', ("127.0.0.6/8",
619 my $conffile = "$prefix_abs/client/client.conf";
621 sub write_clientconf($$)
623 my ($conffile, $vars) = @_;
625 mkdir("$prefix/client", 0777) unless -d "$prefix/client";
627 if ( -d "$prefix/client/private" ) {
628 unlink <$prefix/client/private/*>;
630 mkdir("$prefix/client/private", 0777);
633 open(CF, ">$conffile");
634 print CF "[global]\n";
635 if (defined($ENV{VALGRIND})) {
636 print CF "\ticonv:native = true\n";
638 print CF "\ticonv:native = false\n";
640 print CF "\tnetbios name = client\n";
641 if (defined($vars->{DOMAIN})) {
642 print CF "\tworkgroup = $vars->{DOMAIN}\n";
644 if (defined($vars->{REALM})) {
645 print CF "\trealm = $vars->{REALM}\n";
647 if (defined($vars->{NCALRPCDIR})) {
648 print CF "\tncalrpc dir = $vars->{NCALRPCDIR}\n";
650 if (defined($vars->{PIDDIR})) {
651 print CF "\tpid directory = $vars->{PIDDIR}\n";
653 if (defined($vars->{WINBINDD_SOCKET_DIR})) {
654 print CF "\twinbindd socket directory = $vars->{WINBINDD_SOCKET_DIR}\n";
657 private dir = $prefix_abs/client/private
658 js include = $srcdir_abs/scripting/libjs
659 name resolve order = bcast
660 interfaces = $interfaces
661 panic action = $srcdir_abs/script/gdb_backtrace \%PID\% \%PROG\%
663 notify:inotify = false
665 system:anonymous = true
666 torture:basedir = $prefix_abs/client
667 #We don't want to pass our self-tests if the PAC code is wrong
668 gensec:require_pac = true
674 my @torture_options = ();
675 push (@torture_options, "--configfile=$conffile");
676 # ensure any one smbtorture call doesn't run too long
677 push (@torture_options, "--maximum-runtime=$torture_maxtime");
678 push (@torture_options, "--target=$opt_target");
679 push (@torture_options, "--option=torture:progress=no") if ($from_build_farm);
680 push (@torture_options, "--format=subunit");
681 push (@torture_options, "--option=torture:quick=yes") if ($opt_quick);
683 $ENV{TORTURE_OPTIONS} = join(' ', @torture_options);
684 print "OPTIONS $ENV{TORTURE_OPTIONS}\n";
688 my $testsdir = "$srcdir/selftest";
689 $ENV{SMB_CONF_PATH} = "$conffile";
690 $ENV{CONFIGURATION} = "--configfile=$conffile";
692 my %required_envs = ();
695 open(IN, "$testsdir/tests_quick.sh|");
697 open(IN, "$testsdir/tests_all.sh|");
700 if ($_ eq "-- TEST --\n") {
707 if (not defined($tests) or $name =~ /$tests/) {
708 $required_envs{$env} = 1;
709 push (@todo, [$name, $env, $cmdline]);
715 close(IN) or die("Error creating recipe");
717 my $suitestotal = $#todo + 1;
721 my %running_envs = ();
723 my @exported_envvars = (
728 # domain controller stuff
748 "WINBINDD_SOCKET_DIR"
756 if ($envname eq "none") {
758 } elsif (defined($running_envs{$envname})) {
759 $testenv_vars = $running_envs{$envname};
760 if (not $target->check_env($testenv_vars)) {
761 $testenv_vars = undef;
764 $testenv_vars = $target->setup_env($envname, $prefix);
767 return undef unless defined($testenv_vars);
769 SocketWrapper::set_default_iface(6);
770 write_clientconf($conffile, $testenv_vars);
772 foreach (@exported_envvars) {
773 if (defined($testenv_vars->{$_})) {
774 $ENV{$_} = $testenv_vars->{$_};
780 $running_envs{$envname} = $testenv_vars;
781 return $testenv_vars;
784 sub exported_envvars_str($)
786 my ($testenv_vars) = @_;
789 foreach (@exported_envvars) {
790 next unless defined($testenv_vars->{$_});
791 $out .= $_."=".$testenv_vars->{$_}."\n";
800 return "" if ($envname eq "none");
801 return $target->getlog_env($running_envs{$envname});
807 return 1 if ($envname eq "none");
808 return $target->check_env($running_envs{$envname});
814 return if ($envname eq "none");
815 $target->teardown_env($running_envs{$envname});
816 delete $running_envs{$envname};
820 if ($from_build_farm) {
821 $msg_ops = $buildfarm_msg_ops;
823 $msg_ops = $plain_msg_ops;
826 if ($opt_no_lazy_setup) {
827 setup_env($_) foreach (keys %required_envs);
831 my $testenv_name = $ENV{SELFTEST_TESTENV};
832 $testenv_name = "dc" unless defined($testenv_name);
834 my $testenv_vars = setup_env($testenv_name);
836 $ENV{PIDDIR} = $testenv_vars->{PIDDIR};
838 my $envvarstr = exported_envvars_str($testenv_vars);
840 my $term = ($ENV{TERM} or "xterm");
841 system("$term -e 'echo -e \"
842 Welcome to the Samba4 Test environment '$testenv_name'
844 This matches the client environment used in make test
845 smbd is pid `cat \$PIDDIR/smbd.pid`
847 Some useful environment variables:
848 TORTURE_OPTIONS=\$TORTURE_OPTIONS
849 CONFIGURATION=\$CONFIGURATION
853 teardown_env($testenv_name);
858 $cmd =~ s/([\(\)])/\\$1/g;
860 my $envname = $$_[1];
863 print "SKIPPED: $name\n";
864 $statistics->{SUITES_SKIPPED}++;
868 my $envvars = setup_env($envname);
869 if (not defined($envvars)) {
870 push(@$suitesfailed, $name);
871 $statistics->{SUITES_FAIL}++;
872 $statistics->{TESTS_ERROR}++;
873 print "FAIL: $name (ENV[$envname] not available!)\n";
877 run_test($envname, $name, $cmd, $i, $suitestotal, $msg_ops);
879 if (defined($opt_analyse_cmd)) {
880 system("$opt_analyse_cmd \"$name\"");
883 teardown_env($envname) if ($opt_resetup_env);
889 teardown_env($_) foreach (keys %running_envs);
894 my $duration = ($end-$start);
895 my $numfailed = $#$suitesfailed+1;
896 if ($numfailed == 0) {
897 my $ok = $statistics->{TESTS_EXPECTED_OK} +
898 $statistics->{TESTS_EXPECTED_FAIL};
899 print "ALL OK ($ok tests in $statistics->{SUITES_OK} testsuites)\n";
901 unless ($from_build_farm) {
902 if (not $opt_immediate and not $opt_verbose) {
903 foreach (@$suitesfailed) {
904 print "===============================================================================\n";
906 print $test_output->{$_};
911 print "FAILED ($statistics->{TESTS_UNEXPECTED_FAIL} failures and $statistics->{TESTS_ERROR} errors in $statistics->{SUITES_FAIL} testsuites)\n";
914 print "DURATION: $duration seconds\n";
918 # if there were any valgrind failures, show them
919 foreach (<$prefix/valgrind.log*>) {
921 system("grep DWARF2.CFI.reader $_ > /dev/null");
923 print "VALGRIND FAILURE\n";
929 if ($from_build_farm) {
930 print "TEST STATUS: $numfailed\n";