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;
142 my $suitesfailed = [];
144 my @expected_failures = ();
152 TESTS_UNEXPECTED_OK => 0,
153 TESTS_EXPECTED_OK => 0,
154 TESTS_UNEXPECTED_FAIL => 0,
155 TESTS_EXPECTED_FAIL => 0,
159 sub expecting_failure($)
161 my $fullname = shift;
163 foreach (@expected_failures) {
164 return 1 if ($fullname =~ /$_/);
172 my $fullname = shift;
175 return 1 if ($fullname =~ /$_/);
183 my $test_output = {};
185 sub buildfarm_start_msg($)
190 $out .= "--==--==--==--==--==--==--==--==--==--==--\n";
191 $out .= "Running test $state->{NAME} (level 0 stdout)\n";
192 $out .= "--==--==--==--==--==--==--==--==--==--==--\n";
193 $out .= scalar(localtime())."\n";
194 $out .= "SELFTEST RUNTIME: " . ($state->{START} - $start) . "s\n";
195 $out .= "NAME: $state->{NAME}\n";
196 $out .= "CMD: $state->{CMD}\n";
198 $test_output->{$state->{NAME}} = "";
203 sub buildfarm_output_msg($$)
205 my ($state, $output) = @_;
207 $test_output->{$state->{NAME}} .= $output;
210 sub buildfarm_end_msg($$$)
212 my ($state, $expected_ret, $ret) = @_;
215 $out .= "TEST RUNTIME: " . (time() - $state->{START}) . "s\n";
217 if ($ret == $expected_ret) {
220 $out .= "ERROR: $ret";
221 $out .= $test_output->{$state->{NAME}};
224 $out .= "PCAP FILE: $state->{PCAP_FILE}\n" if defined($state->{PCAP_FILE});
226 $out .= getlog_env($state->{ENVNAME});
228 $out .= "==========================================\n";
229 if ($ret == $expected_ret) {
230 $out .= "TEST PASSED: $state->{NAME}\n";
232 $out .= "TEST FAILED: $state->{NAME} (status $ret)\n";
234 $out .= "==========================================\n";
239 my $buildfarm_msg_ops = {
240 start_msg => \&buildfarm_start_msg,
241 output_msg => \&buildfarm_output_msg,
242 end_msg => \&buildfarm_end_msg
245 sub plain_output_msg($$);
247 sub plain_start_msg($)
252 $out .= "[$state->{INDEX}/$state->{TOTAL} in " . ($state->{START} - $start) . "s";
253 $out .= ", ".($#$suitesfailed+1)." errors" if ($#$suitesfailed+1 > 0);
254 $out .= "] $state->{NAME}\n";
256 $test_output->{$state->{NAME}} = "" unless $opt_verbose;
258 plain_output_msg($state, "CMD: $state->{CMD}\n");
263 sub plain_output_msg($$)
265 my ($state, $output) = @_;
270 $test_output->{$state->{NAME}} .= $output;
274 sub plain_end_msg($$$)
276 my ($state, $expected_ret, $ret) = @_;
279 if ($ret != $expected_ret) {
280 plain_output_msg($state, "ERROR: $ret\n");
283 if ($ret != $expected_ret and ($opt_immediate or $opt_one) and not $opt_verbose) {
284 $out .= $test_output->{$state->{NAME}};
287 if (not $opt_socket_wrapper_keep_pcap and defined($state->{PCAP_FILE})) {
288 $out .= "PCAP FILE: $state->{PCAP_FILE}\n";
291 $out .= getlog_env($state->{ENVNAME});
296 my $plain_msg_ops = {
297 start_msg => \&plain_start_msg,
298 output_msg => \&plain_output_msg,
299 end_msg => \&plain_end_msg
306 return unless ($opt_socket_wrapper_pcap);
307 return unless defined($ENV{SOCKET_WRAPPER_PCAP_DIR});
309 my $fname = sprintf("t%03u_%s", $state->{INDEX}, $state->{NAME});
310 $fname =~ s%[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\-]%_%g;
312 $state->{PCAP_FILE} = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/$fname.pcap";
314 SocketWrapper::setup_pcap($state->{PCAP_FILE});
317 sub cleanup_pcap($$$)
319 my ($state, $expected_ret, $ret) = @_;
321 return unless ($opt_socket_wrapper_pcap);
322 return if ($opt_socket_wrapper_keep_pcap);
323 return unless ($expected_ret == $ret);
324 return unless defined($state->{PCAP_FILE});
326 unlink($state->{PCAP_FILE});
327 $state->{PCAP_FILE} = undef;
332 my ($envname, $name, $cmd, $i, $totalsuites, $msg_ops) = @_;
333 my $expected_ret = 1;
340 TOTAL => $totalsuites,
344 setup_pcap($msg_state);
346 $msg_ops->{start_msg}->($msg_state);
348 open(RESULT, "$cmd 2>&1|");
350 $msg_ops->{output_msg}->($msg_state, $_);
351 if (/^test: (.+)\n/) {
352 $open_tests->{$1} = 1;
353 } elsif (/^(success|failure|skip|error): (.*?)( \[)?\n/) {
355 if ($1 eq "success") {
356 delete $open_tests->{$2};
357 if (expecting_failure("$name/$2")) {
358 $statistics->{TESTS_UNEXPECTED_OK}++;
360 $statistics->{TESTS_EXPECTED_OK}++;
362 } elsif ($1 eq "failure") {
363 delete $open_tests->{$2};
364 if (expecting_failure("$name/$2")) {
365 $statistics->{TESTS_EXPECTED_FAIL}++;
368 print "n:$name/$2l\n";
369 $statistics->{TESTS_UNEXPECTED_FAIL}++;
371 } elsif ($1 eq "skip") {
372 delete $open_tests->{$2};
373 } elsif ($1 eq "error") {
374 $statistics->{TESTS_ERROR}++;
375 delete $open_tests->{$2};
379 foreach (keys %$open_tests) {
380 $msg_ops->{output_msg}->($msg_state, "$_ was started but never finished!\n");
381 $statistics->{TESTS_ERROR}++;
383 my $ret = close(RESULT);
385 cleanup_pcap($msg_state, $expected_ret, $ret);
387 $msg_ops->{end_msg}->($msg_state, $expected_ret, $ret);
389 if ($ret != $expected_ret) {
390 push(@$suitesfailed, $name);
391 $statistics->{SUITES_FAIL}++;
392 exit(1) if ($opt_one);
394 $statistics->{SUITES_OK}++;
397 return ($ret == $expected_ret);
402 print "Samba test runner
403 Copyright (C) Jelmer Vernooij <jelmer\@samba.org>
405 Usage: $Script [OPTIONS] PREFIX
408 --help this help page
409 --target=samba4|samba3|win Samba version to target
412 --prefix=DIR prefix to run tests in [st]
413 --srcdir=DIR source directory [.]
414 --builddir=DIR output directory [.]
417 --socket-wrapper-pcap=DIR save traffic to pcap directories
418 --socket-wrapper-keep-pcap keep all pcap files, not just those for tests that
420 --socket-wrapper enable socket wrapper
421 --expected-failures=FILE specify list of tests that is guaranteed to fail
424 --ldap=openldap|fedora back smbd onto specified ldap server
427 --bindir=PATH path to binaries
430 --quick run quick overall test
431 --one abort when the first test fails
432 --immediate print test output for failed tests during run
434 --analyse-cmd CMD command to run after each test
439 my $result = GetOptions (
440 'help|h|?' => \$opt_help,
441 'target=s' => \$opt_target,
442 'prefix=s' => \$prefix,
443 'socket-wrapper' => \$opt_socket_wrapper,
444 'socket-wrapper-pcap' => \$opt_socket_wrapper_pcap,
445 'socket-wrapper-keep-pcap' => \$opt_socket_wrapper_keep_pcap,
446 'quick' => \$opt_quick,
448 'immediate' => \$opt_immediate,
449 'expected-failures=s' => \$opt_expected_failures,
450 'skip=s' => \$opt_skip,
451 'srcdir=s' => \$srcdir,
452 'builddir=s' => \$builddir,
453 'verbose' => \$opt_verbose,
454 'testenv' => \$opt_testenv,
456 'analyse-cmd=s' => \$opt_analyse_cmd,
457 'resetup-environment' => \$opt_resetup_env,
458 'bindir:s' => \$opt_bindir,
461 exit(1) if (not $result);
463 ShowHelp() if ($opt_help);
467 # quick hack to disable rpc validation when using valgrind - its way too slow
468 unless (defined($ENV{VALGRIND})) {
469 $ENV{VALIDATE} = "validate";
470 $ENV{MALLOC_CHECK_} = 2;
473 my $old_pwd = "$RealBin/..";
475 # Backwards compatibility:
476 if (defined($ENV{TEST_LDAP}) and $ENV{TEST_LDAP} eq "yes") {
477 if (defined($ENV{FEDORA_DS_PREFIX})) {
484 my $torture_maxtime = ($ENV{TORTURE_MAXTIME} or 1200);
487 $torture_maxtime *= 2;
494 die("using an empty prefix isn't allowed") unless $prefix ne "";
496 #Ensure we have the test prefix around
497 mkdir($prefix, 0777) unless -d $prefix;
499 my $prefix_abs = abs_path($prefix);
500 my $srcdir_abs = abs_path($srcdir);
502 die("using an empty absolute prefix isn't allowed") unless $prefix_abs ne "";
503 die("using '/' as absolute prefix isn't allowed") unless $prefix_abs ne "/";
505 $ENV{PREFIX} = $prefix;
506 $ENV{SRCDIR} = $srcdir;
508 my $tls_enabled = not $opt_quick;
509 my $from_build_farm = (defined($ENV{RUN_FROM_BUILD_FARM}) and
510 ($ENV{RUN_FROM_BUILD_FARM} eq "yes"));
512 $ENV{TLS_ENABLED} = ($tls_enabled?"yes":"no");
513 $ENV{LD_LDB_MODULE_PATH} = "$old_pwd/bin/modules/ldb";
514 $ENV{LD_SAMBA_MODULE_PATH} = "$old_pwd/bin/modules";
515 if (defined($ENV{LD_LIBRARY_PATH})) {
516 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared:$ENV{LD_LIBRARY_PATH}";
518 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared";
520 $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig:$ENV{PKG_CONFIG_PATH}";
521 $ENV{PATH} = "$old_pwd/bin:$ENV{PATH}";
524 if ($opt_socket_wrapper_pcap) {
525 # Socket wrapper pcap implies socket wrapper
526 $opt_socket_wrapper = 1;
529 my $socket_wrapper_dir;
530 if ($opt_socket_wrapper) {
531 $socket_wrapper_dir = SocketWrapper::setup_dir("$prefix/w", $opt_socket_wrapper_pcap);
532 print "SOCKET_WRAPPER_DIR=$socket_wrapper_dir\n";
534 warn("Not using socket wrapper, but also not running as root. Will not be able to listen on proper ports") unless $< == 0;
539 if ($opt_target eq "samba4") {
540 $target = new Samba4("$srcdir/bin", $ldap, "$srcdir/setup");
541 } elsif ($opt_target eq "samba3") {
542 if ($opt_socket_wrapper and `smbd -b | grep SOCKET_WRAPPER` eq "") {
543 die("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'. Exiting....");
546 $target = new Samba3($opt_bindir);
547 } elsif ($opt_target eq "win") {
548 die("Windows tests will not run with socket wrapper enabled.")
549 if ($opt_socket_wrapper);
550 $target = new Windows();
553 if (defined($opt_expected_failures)) {
554 open(KNOWN, "<$opt_expected_failures") or die("unable to read known failures file: $!");
558 push (@expected_failures, $_); }
562 if (defined($opt_skip)) {
563 open(SKIP, "<$opt_skip") or die("unable to read skip file: $!");
571 my $interfaces = join(',', ("127.0.0.6/8",
578 my $conffile = "$prefix_abs/client/client.conf";
580 sub write_clientconf($$)
582 my ($conffile, $vars) = @_;
584 mkdir("$prefix/client", 0777) unless -d "$prefix/client";
586 if ( -d "$prefix/client/private" ) {
587 unlink <$prefix/client/private/*>;
589 mkdir("$prefix/client/private", 0777);
592 open(CF, ">$conffile");
593 print CF "[global]\n";
594 if (defined($ENV{VALGRIND})) {
595 print CF "\ticonv:native = true\n";
597 print CF "\ticonv:native = false\n";
599 print CF "\tnetbios name = client\n";
600 if (defined($vars->{DOMAIN})) {
601 print CF "\tworkgroup = $vars->{DOMAIN}\n";
603 if (defined($vars->{REALM})) {
604 print CF "\trealm = $vars->{REALM}\n";
606 if (defined($vars->{NCALRPCDIR})) {
607 print CF "\tncalrpc dir = $vars->{NCALRPCDIR}\n";
609 if (defined($vars->{PIDDIR})) {
610 print CF "\tpid directory = $vars->{PIDDIR}\n";
612 if (defined($vars->{WINBINDD_SOCKET_DIR})) {
613 print CF "\twinbindd socket directory = $vars->{WINBINDD_SOCKET_DIR}\n";
616 private dir = $prefix_abs/client/private
617 js include = $srcdir_abs/scripting/libjs
618 name resolve order = bcast
619 interfaces = $interfaces
620 panic action = $srcdir_abs/script/gdb_backtrace \%PID\% \%PROG\%
622 notify:inotify = false
624 system:anonymous = true
625 torture:basedir = $prefix_abs/client
626 #We don't want to pass our self-tests if the PAC code is wrong
627 gensec:require_pac = true
633 my @torture_options = ();
634 push (@torture_options, "--configfile=$conffile");
635 # ensure any one smbtorture call doesn't run too long
636 push (@torture_options, "--maximum-runtime=$torture_maxtime");
637 push (@torture_options, "--target=$opt_target");
638 push (@torture_options, "--option=torture:progress=no") if ($from_build_farm);
639 push (@torture_options, "--format=subunit");
640 push (@torture_options, "--option=torture:quick=yes") if ($opt_quick);
642 $ENV{TORTURE_OPTIONS} = join(' ', @torture_options);
643 print "OPTIONS $ENV{TORTURE_OPTIONS}\n";
647 my $testsdir = "$srcdir/selftest";
648 $ENV{CONFIGURATION} = "--configfile=$conffile";
652 open(IN, "$testsdir/tests_quick.sh|");
654 open(IN, "$testsdir/tests_all.sh|");
657 if ($_ eq "-- TEST --\n") {
664 push (@todo, [$name, $env, $cmdline])
665 if (not defined($tests) or $name =~ /$tests/);
670 close(IN) or die("Error creating recipe");
672 my $suitestotal = $#todo + 1;
676 my %running_envs = ();
678 my @exported_envvars = (
683 # domain controller stuff
710 if ($envname eq "none") {
712 } elsif (defined($running_envs{$envname})) {
713 $testenv_vars = $running_envs{$envname};
714 if (not $target->check_env($testenv_vars)) {
715 $testenv_vars = undef;
718 $testenv_vars = $target->setup_env($envname, $prefix);
721 return undef unless defined($testenv_vars);
723 SocketWrapper::set_default_iface(6);
724 write_clientconf($conffile, $testenv_vars);
726 foreach (@exported_envvars) {
727 if (defined($testenv_vars->{$_})) {
728 $ENV{$_} = $testenv_vars->{$_};
734 $running_envs{$envname} = $testenv_vars;
735 return $testenv_vars;
738 sub exported_envvars_str($)
740 my ($testenv_vars) = @_;
743 foreach (@exported_envvars) {
744 next unless defined($testenv_vars->{$_});
745 $out .= $_."=".$testenv_vars->{$_}."\n";
754 return "" if ($envname eq "none");
755 return $target->getlog_env($running_envs{$envname});
761 return 1 if ($envname eq "none");
762 return $target->check_env($running_envs{$envname});
768 return if ($envname eq "none");
769 $target->teardown_env($running_envs{$envname});
770 delete $running_envs{$envname};
774 if ($from_build_farm) {
775 $msg_ops = $buildfarm_msg_ops;
777 $msg_ops = $plain_msg_ops;
781 my $testenv_name = $ENV{SELFTEST_TESTENV};
782 $testenv_name = "dc" unless defined($testenv_name);
784 my $testenv_vars = setup_env($testenv_name);
786 $ENV{PIDDIR} = $testenv_vars->{PIDDIR};
788 my $envvarstr = exported_envvars_str($testenv_vars);
790 my $term = ($ENV{TERM} or "xterm");
791 system("$term -e 'echo -e \"
792 Welcome to the Samba4 Test environment '$testenv_name'
794 This matches the client environment used in make test
795 smbd is pid `cat \$PIDDIR/smbd.pid`
797 Some useful environment variables:
798 TORTURE_OPTIONS=\$TORTURE_OPTIONS
799 CONFIGURATION=\$CONFIGURATION
803 teardown_env($testenv_name);
808 $cmd =~ s/([\(\)])/\\$1/g;
810 my $envname = $$_[1];
813 print "SKIPPED: $name\n";
814 $statistics->{SUITES_SKIPPED}++;
818 my $envvars = setup_env($envname);
819 if (not defined($envvars)) {
820 push(@$suitesfailed, $name);
821 $statistics->{SUITES_FAIL}++;
822 $statistics->{TESTS_ERROR}++;
823 print "FAIL: $name (ENV[$envname] not available!)\n";
827 run_test($envname, $name, $cmd, $i, $suitestotal, $msg_ops);
829 if (defined($opt_analyse_cmd)) {
830 system("$opt_analyse_cmd \"$name\"");
833 teardown_env($envname) if ($opt_resetup_env);
839 teardown_env($_) foreach (keys %running_envs);
844 my $duration = ($end-$start);
845 my $numfailed = $#$suitesfailed+1;
846 if ($numfailed == 0) {
847 my $ok = $statistics->{TESTS_EXPECTED_OK} +
848 $statistics->{TESTS_EXPECTED_FAIL};
849 print "ALL OK ($ok tests in $statistics->{SUITES_OK} testsuites)\n";
851 unless ($from_build_farm) {
852 if (not $opt_immediate and not $opt_verbose) {
853 foreach (@$suitesfailed) {
854 print "===============================================================================\n";
856 print $test_output->{$_};
861 print "FAILED ($statistics->{TESTS_UNEXPECTED_FAIL} failures and $statistics->{TESTS_ERROR} errors in $statistics->{SUITES_FAIL} testsuites)\n";
864 print "DURATION: $duration seconds\n";
868 # if there were any valgrind failures, show them
869 foreach (<$prefix/valgrind.log*>) {
871 system("grep DWARF2.CFI.reader $_ > /dev/null");
873 print "VALGRIND FAILURE\n";
879 if ($from_build_farm) {
880 print "TEST STATUS: $numfailed\n";