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_msg($)
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_msg($$$)
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 my $buildfarm_msg_ops = {
241 start_msg => \&buildfarm_start_msg,
242 output_msg => \&buildfarm_output_msg,
243 end_msg => \&buildfarm_end_msg
246 sub plain_output_msg($$);
248 sub plain_start_msg($)
253 $out .= "[$state->{INDEX}/$state->{TOTAL} in " . ($state->{START} - $start) . "s";
254 $out .= ", ".($#$suitesfailed+1)." errors" if ($#$suitesfailed+1 > 0);
255 $out .= "] $state->{NAME}\n";
257 $test_output->{$state->{NAME}} = "" unless $opt_verbose;
259 plain_output_msg($state, "CMD: $state->{CMD}\n");
264 sub plain_output_msg($$)
266 my ($state, $output) = @_;
271 $test_output->{$state->{NAME}} .= $output;
275 sub plain_end_msg($$$)
277 my ($state, $expected_ret, $ret) = @_;
280 if ($ret != $expected_ret) {
281 plain_output_msg($state, "ERROR: $ret\n");
284 if ($ret != $expected_ret and ($opt_immediate or $opt_one) and not $opt_verbose) {
285 $out .= $test_output->{$state->{NAME}};
288 if (not $opt_socket_wrapper_keep_pcap and defined($state->{PCAP_FILE})) {
289 $out .= "PCAP FILE: $state->{PCAP_FILE}\n";
292 $out .= getlog_env($state->{ENVNAME});
297 my $plain_msg_ops = {
298 start_msg => \&plain_start_msg,
299 output_msg => \&plain_output_msg,
300 end_msg => \&plain_end_msg
307 return unless ($opt_socket_wrapper_pcap);
308 return unless defined($ENV{SOCKET_WRAPPER_PCAP_DIR});
310 my $fname = sprintf("t%03u_%s", $state->{INDEX}, $state->{NAME});
311 $fname =~ s%[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\-]%_%g;
313 $state->{PCAP_FILE} = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/$fname.pcap";
315 SocketWrapper::setup_pcap($state->{PCAP_FILE});
318 sub cleanup_pcap($$$)
320 my ($state, $expected_ret, $ret) = @_;
322 return unless ($opt_socket_wrapper_pcap);
323 return if ($opt_socket_wrapper_keep_pcap);
324 return unless ($expected_ret == $ret);
325 return unless defined($state->{PCAP_FILE});
327 unlink($state->{PCAP_FILE});
328 $state->{PCAP_FILE} = undef;
333 my ($envname, $name, $cmd, $i, $totalsuites, $msg_ops) = @_;
334 my $expected_ret = 1;
341 TOTAL => $totalsuites,
345 setup_pcap($msg_state);
347 $msg_ops->{start_msg}->($msg_state);
349 open(RESULT, "$cmd 2>&1|");
351 $msg_ops->{output_msg}->($msg_state, $_);
352 if (/^test: (.+)\n/) {
353 $open_tests->{$1} = 1;
354 } elsif (/^(success|failure|skip|error): (.*?)( \[)?\n/) {
356 if ($1 eq "success") {
357 delete $open_tests->{$2};
358 if (expecting_failure("$name/$2")) {
359 $statistics->{TESTS_UNEXPECTED_OK}++;
361 $statistics->{TESTS_EXPECTED_OK}++;
363 } elsif ($1 eq "failure") {
364 delete $open_tests->{$2};
365 if (expecting_failure("$name/$2")) {
366 $statistics->{TESTS_EXPECTED_FAIL}++;
369 print "n:$name/$2\n";
370 $statistics->{TESTS_UNEXPECTED_FAIL}++;
372 } elsif ($1 eq "skip") {
373 delete $open_tests->{$2};
374 } elsif ($1 eq "error") {
375 $statistics->{TESTS_ERROR}++;
376 delete $open_tests->{$2};
380 foreach (keys %$open_tests) {
381 $msg_ops->{output_msg}->($msg_state, "$_ was started but never finished!\n");
382 $statistics->{TESTS_ERROR}++;
384 my $ret = close(RESULT);
386 cleanup_pcap($msg_state, $expected_ret, $ret);
388 $msg_ops->{end_msg}->($msg_state, $expected_ret, $ret);
390 if ($ret != $expected_ret) {
391 push(@$suitesfailed, $name);
392 $statistics->{SUITES_FAIL}++;
393 exit(1) if ($opt_one);
395 $statistics->{SUITES_OK}++;
398 return ($ret == $expected_ret);
403 print "Samba test runner
404 Copyright (C) Jelmer Vernooij <jelmer\@samba.org>
406 Usage: $Script [OPTIONS] PREFIX
409 --help this help page
410 --target=samba4|samba3|win Samba version to target
413 --prefix=DIR prefix to run tests in [st]
414 --srcdir=DIR source directory [.]
415 --builddir=DIR output directory [.]
418 --socket-wrapper-pcap=DIR save traffic to pcap directories
419 --socket-wrapper-keep-pcap keep all pcap files, not just those for tests that
421 --socket-wrapper enable socket wrapper
422 --expected-failures=FILE specify list of tests that is guaranteed to fail
425 --ldap=openldap|fedora-ds back smbd onto specified ldap server
428 --bindir=PATH path to binaries
431 --quick run quick overall test
432 --one abort when the first test fails
433 --immediate print test output for failed tests during run
435 --analyse-cmd CMD command to run after each test
440 my $result = GetOptions (
441 'help|h|?' => \$opt_help,
442 'target=s' => \$opt_target,
443 'prefix=s' => \$prefix,
444 'socket-wrapper' => \$opt_socket_wrapper,
445 'socket-wrapper-pcap' => \$opt_socket_wrapper_pcap,
446 'socket-wrapper-keep-pcap' => \$opt_socket_wrapper_keep_pcap,
447 'quick' => \$opt_quick,
449 'immediate' => \$opt_immediate,
450 'expected-failures=s' => \$opt_expected_failures,
451 'skip=s' => \$opt_skip,
452 'srcdir=s' => \$srcdir,
453 'builddir=s' => \$builddir,
454 'verbose' => \$opt_verbose,
455 'testenv' => \$opt_testenv,
457 'analyse-cmd=s' => \$opt_analyse_cmd,
458 'no-lazy-setup' => \$opt_no_lazy_setup,
459 'resetup-environment' => \$opt_resetup_env,
460 'bindir:s' => \$opt_bindir,
463 exit(1) if (not $result);
465 ShowHelp() if ($opt_help);
469 # quick hack to disable rpc validation when using valgrind - its way too slow
470 unless (defined($ENV{VALGRIND})) {
471 $ENV{VALIDATE} = "validate";
472 $ENV{MALLOC_CHECK_} = 2;
475 my $old_pwd = "$RealBin/..";
477 # Backwards compatibility:
478 if (defined($ENV{TEST_LDAP}) and $ENV{TEST_LDAP} eq "yes") {
479 if (defined($ENV{FEDORA_DS_PREFIX})) {
486 my $torture_maxtime = ($ENV{TORTURE_MAXTIME} or 1200);
489 $torture_maxtime *= 2;
496 die("using an empty prefix isn't allowed") unless $prefix ne "";
498 #Ensure we have the test prefix around
499 mkdir($prefix, 0777) unless -d $prefix;
501 my $prefix_abs = abs_path($prefix);
502 my $srcdir_abs = abs_path($srcdir);
504 die("using an empty absolute prefix isn't allowed") unless $prefix_abs ne "";
505 die("using '/' as absolute prefix isn't allowed") unless $prefix_abs ne "/";
507 $ENV{PREFIX} = $prefix;
508 $ENV{PREFIX_ABS} = $prefix_abs;
509 $ENV{SRCDIR} = $srcdir;
510 $ENV{SRCDIR_ABS} = $srcdir_abs;
512 my $tls_enabled = not $opt_quick;
513 my $from_build_farm = (defined($ENV{RUN_FROM_BUILD_FARM}) and
514 ($ENV{RUN_FROM_BUILD_FARM} eq "yes"));
516 $ENV{TLS_ENABLED} = ($tls_enabled?"yes":"no");
517 $ENV{LD_LDB_MODULE_PATH} = "$old_pwd/bin/modules/ldb";
518 $ENV{LD_SAMBA_MODULE_PATH} = "$old_pwd/bin/modules";
519 if (defined($ENV{LD_LIBRARY_PATH})) {
520 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared:$ENV{LD_LIBRARY_PATH}";
522 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared";
524 if (defined($ENV{PKG_CONFIG_PATH})) {
525 $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig:$ENV{PKG_CONFIG_PATH}";
527 $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig";
529 $ENV{PATH} = "$old_pwd/bin:$ENV{PATH}";
532 if ($opt_socket_wrapper_pcap) {
533 # Socket wrapper pcap implies socket wrapper
534 $opt_socket_wrapper = 1;
537 my $socket_wrapper_dir;
538 if ($opt_socket_wrapper) {
539 $socket_wrapper_dir = SocketWrapper::setup_dir("$prefix/w", $opt_socket_wrapper_pcap);
540 print "SOCKET_WRAPPER_DIR=$socket_wrapper_dir\n";
542 warn("Not using socket wrapper, but also not running as root. Will not be able to listen on proper ports") unless $< == 0;
547 if ($opt_target eq "samba4") {
548 $target = new Samba4("$srcdir/bin", $ldap, "$srcdir/setup");
549 } elsif ($opt_target eq "samba3") {
550 if ($opt_socket_wrapper and `smbd -b | grep SOCKET_WRAPPER` eq "") {
551 die("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'. Exiting....");
554 $target = new Samba3($opt_bindir);
555 } elsif ($opt_target eq "win") {
556 die("Windows tests will not run with socket wrapper enabled.")
557 if ($opt_socket_wrapper);
558 $target = new Windows();
561 if (defined($opt_expected_failures)) {
562 open(KNOWN, "<$opt_expected_failures") or die("unable to read known failures file: $!");
566 push (@expected_failures, $_); }
570 if (defined($opt_skip)) {
571 open(SKIP, "<$opt_skip") or die("unable to read skip file: $!");
579 my $interfaces = join(',', ("127.0.0.6/8",
586 my $conffile = "$prefix_abs/client/client.conf";
588 sub write_clientconf($$)
590 my ($conffile, $vars) = @_;
592 mkdir("$prefix/client", 0777) unless -d "$prefix/client";
594 if ( -d "$prefix/client/private" ) {
595 unlink <$prefix/client/private/*>;
597 mkdir("$prefix/client/private", 0777);
600 open(CF, ">$conffile");
601 print CF "[global]\n";
602 if (defined($ENV{VALGRIND})) {
603 print CF "\ticonv:native = true\n";
605 print CF "\ticonv:native = false\n";
607 print CF "\tnetbios name = client\n";
608 if (defined($vars->{DOMAIN})) {
609 print CF "\tworkgroup = $vars->{DOMAIN}\n";
611 if (defined($vars->{REALM})) {
612 print CF "\trealm = $vars->{REALM}\n";
614 if (defined($vars->{NCALRPCDIR})) {
615 print CF "\tncalrpc dir = $vars->{NCALRPCDIR}\n";
617 if (defined($vars->{PIDDIR})) {
618 print CF "\tpid directory = $vars->{PIDDIR}\n";
620 if (defined($vars->{WINBINDD_SOCKET_DIR})) {
621 print CF "\twinbindd socket directory = $vars->{WINBINDD_SOCKET_DIR}\n";
624 private dir = $prefix_abs/client/private
625 js include = $srcdir_abs/scripting/libjs
626 name resolve order = bcast
627 interfaces = $interfaces
628 panic action = $srcdir_abs/script/gdb_backtrace \%PID\% \%PROG\%
630 notify:inotify = false
632 system:anonymous = true
633 torture:basedir = $prefix_abs/client
634 #We don't want to pass our self-tests if the PAC code is wrong
635 gensec:require_pac = true
641 my @torture_options = ();
642 push (@torture_options, "--configfile=$conffile");
643 # ensure any one smbtorture call doesn't run too long
644 push (@torture_options, "--maximum-runtime=$torture_maxtime");
645 push (@torture_options, "--target=$opt_target");
646 push (@torture_options, "--option=torture:progress=no") if ($from_build_farm);
647 push (@torture_options, "--format=subunit");
648 push (@torture_options, "--option=torture:quick=yes") if ($opt_quick);
650 $ENV{TORTURE_OPTIONS} = join(' ', @torture_options);
651 print "OPTIONS $ENV{TORTURE_OPTIONS}\n";
655 my $testsdir = "$srcdir/selftest";
656 $ENV{SMB_CONF_PATH} = "$conffile";
657 $ENV{CONFIGURATION} = "--configfile=$conffile";
659 my %required_envs = ();
662 open(IN, "$testsdir/tests_quick.sh|");
664 open(IN, "$testsdir/tests_all.sh|");
667 if ($_ eq "-- TEST --\n") {
674 if (not defined($tests) or $name =~ /$tests/) {
675 $required_envs{$env} = 1;
676 push (@todo, [$name, $env, $cmdline]);
682 close(IN) or die("Error creating recipe");
684 my $suitestotal = $#todo + 1;
688 my %running_envs = ();
690 my @exported_envvars = (
695 # domain controller stuff
715 "WINBINDD_SOCKET_DIR"
723 if ($envname eq "none") {
725 } elsif (defined($running_envs{$envname})) {
726 $testenv_vars = $running_envs{$envname};
727 if (not $target->check_env($testenv_vars)) {
728 $testenv_vars = undef;
731 $testenv_vars = $target->setup_env($envname, $prefix);
734 return undef unless defined($testenv_vars);
736 SocketWrapper::set_default_iface(6);
737 write_clientconf($conffile, $testenv_vars);
739 foreach (@exported_envvars) {
740 if (defined($testenv_vars->{$_})) {
741 $ENV{$_} = $testenv_vars->{$_};
747 $running_envs{$envname} = $testenv_vars;
748 return $testenv_vars;
751 sub exported_envvars_str($)
753 my ($testenv_vars) = @_;
756 foreach (@exported_envvars) {
757 next unless defined($testenv_vars->{$_});
758 $out .= $_."=".$testenv_vars->{$_}."\n";
767 return "" if ($envname eq "none");
768 return $target->getlog_env($running_envs{$envname});
774 return 1 if ($envname eq "none");
775 return $target->check_env($running_envs{$envname});
781 return if ($envname eq "none");
782 $target->teardown_env($running_envs{$envname});
783 delete $running_envs{$envname};
787 if ($from_build_farm) {
788 $msg_ops = $buildfarm_msg_ops;
790 $msg_ops = $plain_msg_ops;
793 if ($opt_no_lazy_setup) {
794 setup_env($_) foreach (keys %required_envs);
798 my $testenv_name = $ENV{SELFTEST_TESTENV};
799 $testenv_name = "dc" unless defined($testenv_name);
801 my $testenv_vars = setup_env($testenv_name);
803 $ENV{PIDDIR} = $testenv_vars->{PIDDIR};
805 my $envvarstr = exported_envvars_str($testenv_vars);
807 my $term = ($ENV{TERM} or "xterm");
808 system("$term -e 'echo -e \"
809 Welcome to the Samba4 Test environment '$testenv_name'
811 This matches the client environment used in make test
812 smbd is pid `cat \$PIDDIR/smbd.pid`
814 Some useful environment variables:
815 TORTURE_OPTIONS=\$TORTURE_OPTIONS
816 CONFIGURATION=\$CONFIGURATION
820 teardown_env($testenv_name);
825 $cmd =~ s/([\(\)])/\\$1/g;
827 my $envname = $$_[1];
830 print "SKIPPED: $name\n";
831 $statistics->{SUITES_SKIPPED}++;
835 my $envvars = setup_env($envname);
836 if (not defined($envvars)) {
837 push(@$suitesfailed, $name);
838 $statistics->{SUITES_FAIL}++;
839 $statistics->{TESTS_ERROR}++;
840 print "FAIL: $name (ENV[$envname] not available!)\n";
844 run_test($envname, $name, $cmd, $i, $suitestotal, $msg_ops);
846 if (defined($opt_analyse_cmd)) {
847 system("$opt_analyse_cmd \"$name\"");
850 teardown_env($envname) if ($opt_resetup_env);
856 teardown_env($_) foreach (keys %running_envs);
861 my $duration = ($end-$start);
862 my $numfailed = $#$suitesfailed+1;
863 if ($numfailed == 0) {
864 my $ok = $statistics->{TESTS_EXPECTED_OK} +
865 $statistics->{TESTS_EXPECTED_FAIL};
866 print "ALL OK ($ok tests in $statistics->{SUITES_OK} testsuites)\n";
868 unless ($from_build_farm) {
869 if (not $opt_immediate and not $opt_verbose) {
870 foreach (@$suitesfailed) {
871 print "===============================================================================\n";
873 print $test_output->{$_};
878 print "FAILED ($statistics->{TESTS_UNEXPECTED_FAIL} failures and $statistics->{TESTS_ERROR} errors in $statistics->{SUITES_FAIL} testsuites)\n";
881 print "DURATION: $duration seconds\n";
885 # if there were any valgrind failures, show them
886 foreach (<$prefix/valgrind.log*>) {
888 system("grep DWARF2.CFI.reader $_ > /dev/null");
890 print "VALGRIND FAILURE\n";
896 if ($from_build_farm) {
897 print "TEST STATUS: $numfailed\n";