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);
123 my $opt_target = "samba4";
125 my $opt_socket_wrapper = 0;
126 my $opt_socket_wrapper_pcap = undef;
127 my $opt_socket_wrapper_keep_pcap = undef;
129 my $opt_immediate = 0;
130 my $opt_expected_failures = undef;
131 my $opt_skip = undef;
135 my $opt_analyse_cmd = undef;
136 my $opt_resetup_env = undef;
137 my $opt_bindir = undef;
138 my $opt_no_lazy_setup = undef;
144 my @expected_failures = ();
148 START_TIME => time(),
154 TESTS_UNEXPECTED_OK => 0,
155 TESTS_EXPECTED_OK => 0,
156 TESTS_UNEXPECTED_FAIL => 0,
157 TESTS_EXPECTED_FAIL => 0,
161 sub expecting_failure($)
163 my $fullname = shift;
165 foreach (@expected_failures) {
166 return 1 if ($fullname =~ /$_/);
174 my $fullname = shift;
177 return 1 if ($fullname =~ /$_/);
189 return unless ($opt_socket_wrapper_pcap);
190 return unless defined($ENV{SOCKET_WRAPPER_PCAP_DIR});
192 my $fname = sprintf("t%03u_%s", $state->{INDEX}, $state->{NAME});
193 $fname =~ s%[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\-]%_%g;
195 $state->{PCAP_FILE} = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/$fname.pcap";
197 SocketWrapper::setup_pcap($state->{PCAP_FILE});
200 sub cleanup_pcap($$$)
202 my ($state, $expected_ret, $ret) = @_;
204 return unless ($opt_socket_wrapper_pcap);
205 return if ($opt_socket_wrapper_keep_pcap);
206 return unless ($expected_ret == $ret);
207 return unless defined($state->{PCAP_FILE});
209 unlink($state->{PCAP_FILE});
210 $state->{PCAP_FILE} = undef;
213 sub parse_subunit_results($$$$)
215 my ($msg_ops, $msg_state, $statistics, $fh) = @_;
216 my $expected_ret = 1;
220 $msg_ops->output_msg($msg_state, $_);
221 if (/^test: (.+)\n/) {
222 $open_tests->{$1} = 1;
223 $msg_ops->start_test($msg_state, $1);
224 } elsif (/^(success|failure|skip|error): (.*?)( \[)?\n/) {
226 if ($1 eq "success") {
227 delete $open_tests->{$2};
228 if (expecting_failure("$msg_state->{NAME}/$2")) {
229 $statistics->{TESTS_UNEXPECTED_OK}++;
230 $msg_ops->end_test($msg_state, $2, $1, 1);
232 $statistics->{TESTS_EXPECTED_OK}++;
233 $msg_ops->end_test($msg_state, $2, $1, 0);
235 } elsif ($1 eq "failure") {
236 delete $open_tests->{$2};
237 if (expecting_failure("$msg_state->{NAME}/$2")) {
238 $statistics->{TESTS_EXPECTED_FAIL}++;
239 $msg_ops->end_test($msg_state, $2, $1, 0);
242 print "n:$msg_state->{NAME}/$2\n";
243 $statistics->{TESTS_UNEXPECTED_FAIL}++;
244 $msg_ops->end_test($msg_state, $2, $1, 1);
246 } elsif ($1 eq "skip") {
247 delete $open_tests->{$2};
248 $msg_ops->end_test($msg_state, $2, $1, 0);
249 } elsif ($1 eq "error") {
250 $statistics->{TESTS_ERROR}++;
251 delete $open_tests->{$2};
252 $msg_ops->end_test($msg_state, $2, $1, 1);
257 foreach (keys %$open_tests) {
258 $msg_ops->end_test($msg_state, $_, "error", 1);
259 $msg_ops->output_msg($msg_state, "$_ was started but never finished!");
260 $statistics->{TESTS_ERROR}++;
263 return $expected_ret;
268 my ($envname, $name, $cmd, $i, $totalsuites, $msg_ops) = @_;
274 TOTAL => $totalsuites,
278 setup_pcap($msg_state);
280 open(RESULT, "$cmd 2>&1|");
281 $msg_ops->start_testsuite($msg_state);
283 my $expected_ret = parse_subunit_results(
284 $msg_ops, $msg_state, $statistics, *RESULT);
286 my $ret = close(RESULT);
288 cleanup_pcap($msg_state, $expected_ret, $ret);
290 $msg_ops->end_testsuite($msg_state, $expected_ret, $ret,
291 getlog_env($msg_state->{ENVNAME}));
293 if (not $opt_socket_wrapper_keep_pcap and
294 defined($msg_state->{PCAP_FILE})) {
295 $msg_ops->output_msg($msg_state,
296 "PCAP FILE: $msg_state->{PCAP_FILE}\n");
299 if ($ret != $expected_ret) {
300 $statistics->{SUITES_FAIL}++;
301 exit(1) if ($opt_one);
303 $statistics->{SUITES_OK}++;
306 return ($ret == $expected_ret);
311 print "Samba test runner
312 Copyright (C) Jelmer Vernooij <jelmer\@samba.org>
314 Usage: $Script [OPTIONS] PREFIX
317 --help this help page
318 --target=samba4|samba3|win Samba version to target
321 --prefix=DIR prefix to run tests in [st]
322 --srcdir=DIR source directory [.]
323 --builddir=DIR output directory [.]
326 --socket-wrapper-pcap=DIR save traffic to pcap directories
327 --socket-wrapper-keep-pcap keep all pcap files, not just those for tests that
329 --socket-wrapper enable socket wrapper
330 --expected-failures=FILE specify list of tests that is guaranteed to fail
333 --ldap=openldap|fedora-ds back smbd onto specified ldap server
336 --bindir=PATH path to binaries
339 --quick run quick overall test
340 --one abort when the first test fails
341 --immediate print test output for failed tests during run
343 --analyse-cmd CMD command to run after each test
348 my $result = GetOptions (
349 'help|h|?' => \$opt_help,
350 'target=s' => \$opt_target,
351 'prefix=s' => \$prefix,
352 'socket-wrapper' => \$opt_socket_wrapper,
353 'socket-wrapper-pcap' => \$opt_socket_wrapper_pcap,
354 'socket-wrapper-keep-pcap' => \$opt_socket_wrapper_keep_pcap,
355 'quick' => \$opt_quick,
357 'immediate' => \$opt_immediate,
358 'expected-failures=s' => \$opt_expected_failures,
359 'skip=s' => \$opt_skip,
360 'srcdir=s' => \$srcdir,
361 'builddir=s' => \$builddir,
362 'verbose' => \$opt_verbose,
363 'testenv' => \$opt_testenv,
365 'analyse-cmd=s' => \$opt_analyse_cmd,
366 'no-lazy-setup' => \$opt_no_lazy_setup,
367 'resetup-environment' => \$opt_resetup_env,
368 'bindir:s' => \$opt_bindir,
371 exit(1) if (not $result);
373 ShowHelp() if ($opt_help);
377 # quick hack to disable rpc validation when using valgrind - its way too slow
378 unless (defined($ENV{VALGRIND})) {
379 $ENV{VALIDATE} = "validate";
380 $ENV{MALLOC_CHECK_} = 2;
383 my $old_pwd = "$RealBin/..";
385 # Backwards compatibility:
386 if (defined($ENV{TEST_LDAP}) and $ENV{TEST_LDAP} eq "yes") {
387 if (defined($ENV{FEDORA_DS_PREFIX})) {
394 my $torture_maxtime = ($ENV{TORTURE_MAXTIME} or 1200);
397 $torture_maxtime *= 2;
404 die("using an empty prefix isn't allowed") unless $prefix ne "";
406 #Ensure we have the test prefix around
407 mkdir($prefix, 0777) unless -d $prefix;
409 my $prefix_abs = abs_path($prefix);
410 my $srcdir_abs = abs_path($srcdir);
412 die("using an empty absolute prefix isn't allowed") unless $prefix_abs ne "";
413 die("using '/' as absolute prefix isn't allowed") unless $prefix_abs ne "/";
415 $ENV{PREFIX} = $prefix;
416 $ENV{PREFIX_ABS} = $prefix_abs;
417 $ENV{SRCDIR} = $srcdir;
418 $ENV{SRCDIR_ABS} = $srcdir_abs;
420 my $tls_enabled = not $opt_quick;
421 my $from_build_farm = (defined($ENV{RUN_FROM_BUILD_FARM}) and
422 ($ENV{RUN_FROM_BUILD_FARM} eq "yes"));
424 $ENV{TLS_ENABLED} = ($tls_enabled?"yes":"no");
425 $ENV{LD_LDB_MODULE_PATH} = "$old_pwd/bin/modules/ldb";
426 $ENV{LD_SAMBA_MODULE_PATH} = "$old_pwd/bin/modules";
427 if (defined($ENV{LD_LIBRARY_PATH})) {
428 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared:$ENV{LD_LIBRARY_PATH}";
430 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared";
432 if (defined($ENV{PKG_CONFIG_PATH})) {
433 $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig:$ENV{PKG_CONFIG_PATH}";
435 $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig";
437 $ENV{PATH} = "$old_pwd/bin:$ENV{PATH}";
440 if ($opt_socket_wrapper_pcap) {
441 # Socket wrapper pcap implies socket wrapper
442 $opt_socket_wrapper = 1;
445 my $socket_wrapper_dir;
446 if ($opt_socket_wrapper) {
447 $socket_wrapper_dir = SocketWrapper::setup_dir("$prefix/w", $opt_socket_wrapper_pcap);
448 print "SOCKET_WRAPPER_DIR=$socket_wrapper_dir\n";
450 warn("Not using socket wrapper, but also not running as root. Will not be able to listen on proper ports") unless $< == 0;
455 if ($opt_target eq "samba4") {
456 $target = new Samba4("$srcdir/bin", $ldap, "$srcdir/setup");
457 } elsif ($opt_target eq "samba3") {
458 if ($opt_socket_wrapper and `smbd -b | grep SOCKET_WRAPPER` eq "") {
459 die("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'. Exiting....");
462 $target = new Samba3($opt_bindir);
463 } elsif ($opt_target eq "win") {
464 die("Windows tests will not run with socket wrapper enabled.")
465 if ($opt_socket_wrapper);
466 $target = new Windows();
469 if (defined($opt_expected_failures)) {
470 open(KNOWN, "<$opt_expected_failures") or die("unable to read known failures file: $!");
474 push (@expected_failures, $_); }
478 if (defined($opt_skip)) {
479 open(SKIP, "<$opt_skip") or die("unable to read skip file: $!");
487 my $interfaces = join(',', ("127.0.0.6/8",
494 my $conffile = "$prefix_abs/client/client.conf";
496 sub write_clientconf($$)
498 my ($conffile, $vars) = @_;
500 mkdir("$prefix/client", 0777) unless -d "$prefix/client";
502 if ( -d "$prefix/client/private" ) {
503 unlink <$prefix/client/private/*>;
505 mkdir("$prefix/client/private", 0777);
508 open(CF, ">$conffile");
509 print CF "[global]\n";
510 if (defined($ENV{VALGRIND})) {
511 print CF "\ticonv:native = true\n";
513 print CF "\ticonv:native = false\n";
515 print CF "\tnetbios name = client\n";
516 if (defined($vars->{DOMAIN})) {
517 print CF "\tworkgroup = $vars->{DOMAIN}\n";
519 if (defined($vars->{REALM})) {
520 print CF "\trealm = $vars->{REALM}\n";
522 if (defined($vars->{NCALRPCDIR})) {
523 print CF "\tncalrpc dir = $vars->{NCALRPCDIR}\n";
525 if (defined($vars->{PIDDIR})) {
526 print CF "\tpid directory = $vars->{PIDDIR}\n";
528 if (defined($vars->{WINBINDD_SOCKET_DIR})) {
529 print CF "\twinbindd socket directory = $vars->{WINBINDD_SOCKET_DIR}\n";
532 private dir = $prefix_abs/client/private
533 js include = $srcdir_abs/scripting/libjs
534 name resolve order = bcast
535 interfaces = $interfaces
536 panic action = $srcdir_abs/script/gdb_backtrace \%PID\% \%PROG\%
538 notify:inotify = false
540 system:anonymous = true
541 torture:basedir = $prefix_abs/client
542 #We don't want to pass our self-tests if the PAC code is wrong
543 gensec:require_pac = true
549 my @torture_options = ();
550 push (@torture_options, "--configfile=$conffile");
551 # ensure any one smbtorture call doesn't run too long
552 push (@torture_options, "--maximum-runtime=$torture_maxtime");
553 push (@torture_options, "--target=$opt_target");
554 push (@torture_options, "--option=torture:progress=no") if ($from_build_farm);
555 push (@torture_options, "--format=subunit");
556 push (@torture_options, "--option=torture:quick=yes") if ($opt_quick);
558 $ENV{TORTURE_OPTIONS} = join(' ', @torture_options);
559 print "OPTIONS $ENV{TORTURE_OPTIONS}\n";
563 my $testsdir = "$srcdir/selftest";
564 $ENV{SMB_CONF_PATH} = "$conffile";
565 $ENV{CONFIGURATION} = "--configfile=$conffile";
567 my %required_envs = ();
570 open(IN, "$testsdir/tests_quick.sh|");
572 open(IN, "$testsdir/tests_all.sh|");
575 if ($_ eq "-- TEST --\n") {
582 if (not defined($tests) or $name =~ /$tests/) {
583 $required_envs{$env} = 1;
584 push (@todo, [$name, $env, $cmdline]);
590 close(IN) or die("Error creating recipe");
592 my $suitestotal = $#todo + 1;
596 my %running_envs = ();
598 my @exported_envvars = (
603 # domain controller stuff
623 "WINBINDD_SOCKET_DIR"
631 if ($envname eq "none") {
633 } elsif (defined($running_envs{$envname})) {
634 $testenv_vars = $running_envs{$envname};
635 if (not $target->check_env($testenv_vars)) {
636 $testenv_vars = undef;
639 $testenv_vars = $target->setup_env($envname, $prefix);
642 return undef unless defined($testenv_vars);
644 SocketWrapper::set_default_iface(6);
645 write_clientconf($conffile, $testenv_vars);
647 foreach (@exported_envvars) {
648 if (defined($testenv_vars->{$_})) {
649 $ENV{$_} = $testenv_vars->{$_};
655 $running_envs{$envname} = $testenv_vars;
656 return $testenv_vars;
659 sub exported_envvars_str($)
661 my ($testenv_vars) = @_;
664 foreach (@exported_envvars) {
665 next unless defined($testenv_vars->{$_});
666 $out .= $_."=".$testenv_vars->{$_}."\n";
675 return "" if ($envname eq "none");
676 return $target->getlog_env($running_envs{$envname});
682 return 1 if ($envname eq "none");
683 return $target->check_env($running_envs{$envname});
689 return if ($envname eq "none");
690 $target->teardown_env($running_envs{$envname});
691 delete $running_envs{$envname};
695 if ($from_build_farm) {
696 require output::buildfarm;
697 $msg_ops = new output::buildfarm();
699 require output::plain;
700 $msg_ops = new output::plain($opt_verbose, $opt_immediate, $statistics);
703 if ($opt_no_lazy_setup) {
704 setup_env($_) foreach (keys %required_envs);
708 my $testenv_name = $ENV{SELFTEST_TESTENV};
709 $testenv_name = "dc" unless defined($testenv_name);
711 my $testenv_vars = setup_env($testenv_name);
713 $ENV{PIDDIR} = $testenv_vars->{PIDDIR};
715 my $envvarstr = exported_envvars_str($testenv_vars);
717 my $term = ($ENV{TERM} or "xterm");
718 system("$term -e 'echo -e \"
719 Welcome to the Samba4 Test environment '$testenv_name'
721 This matches the client environment used in make test
722 smbd is pid `cat \$PIDDIR/smbd.pid`
724 Some useful environment variables:
725 TORTURE_OPTIONS=\$TORTURE_OPTIONS
726 CONFIGURATION=\$CONFIGURATION
730 teardown_env($testenv_name);
735 $cmd =~ s/([\(\)])/\\$1/g;
737 my $envname = $$_[1];
740 print "SKIPPED: $name\n";
741 $statistics->{SUITES_SKIPPED}++;
745 my $envvars = setup_env($envname);
746 if (not defined($envvars)) {
747 $statistics->{SUITES_FAIL}++;
748 $statistics->{TESTS_ERROR}++;
749 $msg_ops->missing_env($name, $envname);
753 run_test($envname, $name, $cmd, $i, $suitestotal, $msg_ops);
755 if (defined($opt_analyse_cmd)) {
756 system("$opt_analyse_cmd \"$name\"");
759 teardown_env($envname) if ($opt_resetup_env);
765 teardown_env($_) foreach (keys %running_envs);
769 $statistics->{END_TIME} = time();
770 my $duration = ($statistics->{END_TIME}-$statistics->{START_TIME});
771 my $numfailed = $statistics->{SUITES_FAIL};
772 if ($numfailed == 0) {
773 my $ok = $statistics->{TESTS_EXPECTED_OK} +
774 $statistics->{TESTS_EXPECTED_FAIL};
775 print "ALL OK ($ok tests in $statistics->{SUITES_OK} testsuites)\n";
779 print "DURATION: $duration seconds\n";
783 # if there were any valgrind failures, show them
784 foreach (<$prefix/valgrind.log*>) {
786 system("grep DWARF2.CFI.reader $_ > /dev/null");
788 print "VALGRIND FAILURE\n";
794 if ($from_build_farm) {
795 print "TEST STATUS: $numfailed\n";