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 @expected_failures = ();
147 START_TIME => time(),
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 =~ /$_/);
188 return unless ($opt_socket_wrapper_pcap);
189 return unless defined($ENV{SOCKET_WRAPPER_PCAP_DIR});
191 my $fname = sprintf("t%03u_%s", $state->{INDEX}, $state->{NAME});
192 $fname =~ s%[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\-]%_%g;
194 $state->{PCAP_FILE} = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/$fname.pcap";
196 SocketWrapper::setup_pcap($state->{PCAP_FILE});
199 sub cleanup_pcap($$$)
201 my ($state, $expected_ret, $ret) = @_;
203 return unless ($opt_socket_wrapper_pcap);
204 return if ($opt_socket_wrapper_keep_pcap);
205 return unless ($expected_ret == $ret);
206 return unless defined($state->{PCAP_FILE});
208 unlink($state->{PCAP_FILE});
209 $state->{PCAP_FILE} = undef;
214 my ($envname, $name, $cmd, $i, $totalsuites, $msg_ops) = @_;
215 my $expected_ret = 1;
222 TOTAL => $totalsuites,
226 setup_pcap($msg_state);
228 $msg_ops->start_testsuite($msg_state);
230 open(RESULT, "$cmd 2>&1|");
232 $msg_ops->output_msg($msg_state, $_);
233 if (/^test: (.+)\n/) {
234 $open_tests->{$1} = 1;
235 $msg_ops->start_test($msg_state, $1);
236 } elsif (/^(success|failure|skip|error): (.*?)( \[)?\n/) {
238 if ($1 eq "success") {
239 delete $open_tests->{$2};
240 if (expecting_failure("$name/$2")) {
241 $statistics->{TESTS_UNEXPECTED_OK}++;
242 $msg_ops->end_test($msg_state, $2, $1, 1);
244 $statistics->{TESTS_EXPECTED_OK}++;
245 $msg_ops->end_test($msg_state, $2, $1, 0);
247 } elsif ($1 eq "failure") {
248 delete $open_tests->{$2};
249 if (expecting_failure("$name/$2")) {
250 $statistics->{TESTS_EXPECTED_FAIL}++;
251 $msg_ops->end_test($msg_state, $2, $1, 0);
254 print "n:$name/$2\n";
255 $statistics->{TESTS_UNEXPECTED_FAIL}++;
256 $msg_ops->end_test($msg_state, $2, $1, 1);
258 } elsif ($1 eq "skip") {
259 delete $open_tests->{$2};
260 $msg_ops->end_test($msg_state, $2, $1, 0);
261 } elsif ($1 eq "error") {
262 $statistics->{TESTS_ERROR}++;
263 delete $open_tests->{$2};
264 $msg_ops->end_test($msg_state, $2, $1, 1);
269 foreach (keys %$open_tests) {
270 $msg_ops->end_test($msg_state, $_, "error", 1);
271 $msg_ops->output_msg($msg_state, "$_ was started but never finished!");
272 $statistics->{TESTS_ERROR}++;
274 my $ret = close(RESULT);
276 cleanup_pcap($msg_state, $expected_ret, $ret);
278 $msg_ops->end_testsuite($msg_state, $expected_ret, $ret,
279 getlog_env($msg_state->{ENVNAME}));
281 if (not $opt_socket_wrapper_keep_pcap and
282 defined($msg_state->{PCAP_FILE})) {
283 $msg_ops->output_msg($msg_state,
284 "PCAP FILE: $msg_state->{PCAP_FILE}\n");
287 if ($ret != $expected_ret) {
288 $statistics->{SUITES_FAIL}++;
289 exit(1) if ($opt_one);
291 $statistics->{SUITES_OK}++;
294 return ($ret == $expected_ret);
299 print "Samba test runner
300 Copyright (C) Jelmer Vernooij <jelmer\@samba.org>
302 Usage: $Script [OPTIONS] PREFIX
305 --help this help page
306 --target=samba4|samba3|win Samba version to target
309 --prefix=DIR prefix to run tests in [st]
310 --srcdir=DIR source directory [.]
311 --builddir=DIR output directory [.]
314 --socket-wrapper-pcap=DIR save traffic to pcap directories
315 --socket-wrapper-keep-pcap keep all pcap files, not just those for tests that
317 --socket-wrapper enable socket wrapper
318 --expected-failures=FILE specify list of tests that is guaranteed to fail
321 --ldap=openldap|fedora-ds back smbd onto specified ldap server
324 --bindir=PATH path to binaries
327 --quick run quick overall test
328 --one abort when the first test fails
329 --immediate print test output for failed tests during run
331 --analyse-cmd CMD command to run after each test
336 my $result = GetOptions (
337 'help|h|?' => \$opt_help,
338 'target=s' => \$opt_target,
339 'prefix=s' => \$prefix,
340 'socket-wrapper' => \$opt_socket_wrapper,
341 'socket-wrapper-pcap' => \$opt_socket_wrapper_pcap,
342 'socket-wrapper-keep-pcap' => \$opt_socket_wrapper_keep_pcap,
343 'quick' => \$opt_quick,
345 'immediate' => \$opt_immediate,
346 'expected-failures=s' => \$opt_expected_failures,
347 'skip=s' => \$opt_skip,
348 'srcdir=s' => \$srcdir,
349 'builddir=s' => \$builddir,
350 'verbose' => \$opt_verbose,
351 'testenv' => \$opt_testenv,
353 'analyse-cmd=s' => \$opt_analyse_cmd,
354 'no-lazy-setup' => \$opt_no_lazy_setup,
355 'resetup-environment' => \$opt_resetup_env,
356 'bindir:s' => \$opt_bindir,
359 exit(1) if (not $result);
361 ShowHelp() if ($opt_help);
365 # quick hack to disable rpc validation when using valgrind - its way too slow
366 unless (defined($ENV{VALGRIND})) {
367 $ENV{VALIDATE} = "validate";
368 $ENV{MALLOC_CHECK_} = 2;
371 my $old_pwd = "$RealBin/..";
373 # Backwards compatibility:
374 if (defined($ENV{TEST_LDAP}) and $ENV{TEST_LDAP} eq "yes") {
375 if (defined($ENV{FEDORA_DS_PREFIX})) {
382 my $torture_maxtime = ($ENV{TORTURE_MAXTIME} or 1200);
385 $torture_maxtime *= 2;
392 die("using an empty prefix isn't allowed") unless $prefix ne "";
394 #Ensure we have the test prefix around
395 mkdir($prefix, 0777) unless -d $prefix;
397 my $prefix_abs = abs_path($prefix);
398 my $srcdir_abs = abs_path($srcdir);
400 die("using an empty absolute prefix isn't allowed") unless $prefix_abs ne "";
401 die("using '/' as absolute prefix isn't allowed") unless $prefix_abs ne "/";
403 $ENV{PREFIX} = $prefix;
404 $ENV{PREFIX_ABS} = $prefix_abs;
405 $ENV{SRCDIR} = $srcdir;
406 $ENV{SRCDIR_ABS} = $srcdir_abs;
408 my $tls_enabled = not $opt_quick;
409 my $from_build_farm = (defined($ENV{RUN_FROM_BUILD_FARM}) and
410 ($ENV{RUN_FROM_BUILD_FARM} eq "yes"));
412 $ENV{TLS_ENABLED} = ($tls_enabled?"yes":"no");
413 $ENV{LD_LDB_MODULE_PATH} = "$old_pwd/bin/modules/ldb";
414 $ENV{LD_SAMBA_MODULE_PATH} = "$old_pwd/bin/modules";
415 if (defined($ENV{LD_LIBRARY_PATH})) {
416 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared:$ENV{LD_LIBRARY_PATH}";
418 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared";
420 if (defined($ENV{PKG_CONFIG_PATH})) {
421 $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig:$ENV{PKG_CONFIG_PATH}";
423 $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig";
425 $ENV{PATH} = "$old_pwd/bin:$ENV{PATH}";
428 if ($opt_socket_wrapper_pcap) {
429 # Socket wrapper pcap implies socket wrapper
430 $opt_socket_wrapper = 1;
433 my $socket_wrapper_dir;
434 if ($opt_socket_wrapper) {
435 $socket_wrapper_dir = SocketWrapper::setup_dir("$prefix/w", $opt_socket_wrapper_pcap);
436 print "SOCKET_WRAPPER_DIR=$socket_wrapper_dir\n";
438 warn("Not using socket wrapper, but also not running as root. Will not be able to listen on proper ports") unless $< == 0;
443 if ($opt_target eq "samba4") {
444 $target = new Samba4("$srcdir/bin", $ldap, "$srcdir/setup");
445 } elsif ($opt_target eq "samba3") {
446 if ($opt_socket_wrapper and `smbd -b | grep SOCKET_WRAPPER` eq "") {
447 die("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'. Exiting....");
450 $target = new Samba3($opt_bindir);
451 } elsif ($opt_target eq "win") {
452 die("Windows tests will not run with socket wrapper enabled.")
453 if ($opt_socket_wrapper);
454 $target = new Windows();
457 if (defined($opt_expected_failures)) {
458 open(KNOWN, "<$opt_expected_failures") or die("unable to read known failures file: $!");
462 push (@expected_failures, $_); }
466 if (defined($opt_skip)) {
467 open(SKIP, "<$opt_skip") or die("unable to read skip file: $!");
475 my $interfaces = join(',', ("127.0.0.6/8",
482 my $conffile = "$prefix_abs/client/client.conf";
484 sub write_clientconf($$)
486 my ($conffile, $vars) = @_;
488 mkdir("$prefix/client", 0777) unless -d "$prefix/client";
490 if ( -d "$prefix/client/private" ) {
491 unlink <$prefix/client/private/*>;
493 mkdir("$prefix/client/private", 0777);
496 open(CF, ">$conffile");
497 print CF "[global]\n";
498 if (defined($ENV{VALGRIND})) {
499 print CF "\ticonv:native = true\n";
501 print CF "\ticonv:native = false\n";
503 print CF "\tnetbios name = client\n";
504 if (defined($vars->{DOMAIN})) {
505 print CF "\tworkgroup = $vars->{DOMAIN}\n";
507 if (defined($vars->{REALM})) {
508 print CF "\trealm = $vars->{REALM}\n";
510 if (defined($vars->{NCALRPCDIR})) {
511 print CF "\tncalrpc dir = $vars->{NCALRPCDIR}\n";
513 if (defined($vars->{PIDDIR})) {
514 print CF "\tpid directory = $vars->{PIDDIR}\n";
516 if (defined($vars->{WINBINDD_SOCKET_DIR})) {
517 print CF "\twinbindd socket directory = $vars->{WINBINDD_SOCKET_DIR}\n";
520 private dir = $prefix_abs/client/private
521 js include = $srcdir_abs/scripting/libjs
522 name resolve order = bcast
523 interfaces = $interfaces
524 panic action = $srcdir_abs/script/gdb_backtrace \%PID\% \%PROG\%
526 notify:inotify = false
528 system:anonymous = true
529 torture:basedir = $prefix_abs/client
530 #We don't want to pass our self-tests if the PAC code is wrong
531 gensec:require_pac = true
537 my @torture_options = ();
538 push (@torture_options, "--configfile=$conffile");
539 # ensure any one smbtorture call doesn't run too long
540 push (@torture_options, "--maximum-runtime=$torture_maxtime");
541 push (@torture_options, "--target=$opt_target");
542 push (@torture_options, "--option=torture:progress=no") if ($from_build_farm);
543 push (@torture_options, "--format=subunit");
544 push (@torture_options, "--option=torture:quick=yes") if ($opt_quick);
546 $ENV{TORTURE_OPTIONS} = join(' ', @torture_options);
547 print "OPTIONS $ENV{TORTURE_OPTIONS}\n";
551 my $testsdir = "$srcdir/selftest";
552 $ENV{SMB_CONF_PATH} = "$conffile";
553 $ENV{CONFIGURATION} = "--configfile=$conffile";
555 my %required_envs = ();
558 open(IN, "$testsdir/tests_quick.sh|");
560 open(IN, "$testsdir/tests_all.sh|");
563 if ($_ eq "-- TEST --\n") {
570 if (not defined($tests) or $name =~ /$tests/) {
571 $required_envs{$env} = 1;
572 push (@todo, [$name, $env, $cmdline]);
578 close(IN) or die("Error creating recipe");
580 my $suitestotal = $#todo + 1;
584 my %running_envs = ();
586 my @exported_envvars = (
591 # domain controller stuff
611 "WINBINDD_SOCKET_DIR"
619 if ($envname eq "none") {
621 } elsif (defined($running_envs{$envname})) {
622 $testenv_vars = $running_envs{$envname};
623 if (not $target->check_env($testenv_vars)) {
624 $testenv_vars = undef;
627 $testenv_vars = $target->setup_env($envname, $prefix);
630 return undef unless defined($testenv_vars);
632 SocketWrapper::set_default_iface(6);
633 write_clientconf($conffile, $testenv_vars);
635 foreach (@exported_envvars) {
636 if (defined($testenv_vars->{$_})) {
637 $ENV{$_} = $testenv_vars->{$_};
643 $running_envs{$envname} = $testenv_vars;
644 return $testenv_vars;
647 sub exported_envvars_str($)
649 my ($testenv_vars) = @_;
652 foreach (@exported_envvars) {
653 next unless defined($testenv_vars->{$_});
654 $out .= $_."=".$testenv_vars->{$_}."\n";
663 return "" if ($envname eq "none");
664 return $target->getlog_env($running_envs{$envname});
670 return 1 if ($envname eq "none");
671 return $target->check_env($running_envs{$envname});
677 return if ($envname eq "none");
678 $target->teardown_env($running_envs{$envname});
679 delete $running_envs{$envname};
683 if ($from_build_farm) {
684 require output::buildfarm;
685 $msg_ops = new output::buildfarm();
687 require output::plain;
688 $msg_ops = new output::plain($opt_verbose, $opt_immediate, $statistics);
691 if ($opt_no_lazy_setup) {
692 setup_env($_) foreach (keys %required_envs);
696 my $testenv_name = $ENV{SELFTEST_TESTENV};
697 $testenv_name = "dc" unless defined($testenv_name);
699 my $testenv_vars = setup_env($testenv_name);
701 $ENV{PIDDIR} = $testenv_vars->{PIDDIR};
703 my $envvarstr = exported_envvars_str($testenv_vars);
705 my $term = ($ENV{TERM} or "xterm");
706 system("$term -e 'echo -e \"
707 Welcome to the Samba4 Test environment '$testenv_name'
709 This matches the client environment used in make test
710 smbd is pid `cat \$PIDDIR/smbd.pid`
712 Some useful environment variables:
713 TORTURE_OPTIONS=\$TORTURE_OPTIONS
714 CONFIGURATION=\$CONFIGURATION
718 teardown_env($testenv_name);
723 $cmd =~ s/([\(\)])/\\$1/g;
725 my $envname = $$_[1];
728 print "SKIPPED: $name\n";
729 $statistics->{SUITES_SKIPPED}++;
733 my $envvars = setup_env($envname);
734 if (not defined($envvars)) {
735 $statistics->{SUITES_FAIL}++;
736 $statistics->{TESTS_ERROR}++;
737 $msg_ops->missing_env($name, $envname);
741 run_test($envname, $name, $cmd, $i, $suitestotal, $msg_ops);
743 if (defined($opt_analyse_cmd)) {
744 system("$opt_analyse_cmd \"$name\"");
747 teardown_env($envname) if ($opt_resetup_env);
753 teardown_env($_) foreach (keys %running_envs);
757 $statistics->{END_TIME} = time();
758 my $duration = ($statistics->{END_TIME}-$statistics->{START_TIME});
759 my $numfailed = $statistics->{SUITES_FAIL};
760 if ($numfailed == 0) {
761 my $ok = $statistics->{TESTS_EXPECTED_OK} +
762 $statistics->{TESTS_EXPECTED_FAIL};
763 print "ALL OK ($ok tests in $statistics->{SUITES_OK} testsuites)\n";
767 print "DURATION: $duration seconds\n";
771 # if there were any valgrind failures, show them
772 foreach (<$prefix/valgrind.log*>) {
774 system("grep DWARF2.CFI.reader $_ > /dev/null");
776 print "VALGRIND FAILURE\n";
782 if ($from_build_farm) {
783 print "TEST STATUS: $numfailed\n";