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);
117 use Subunit qw(parse_results);
124 my $opt_target = "samba4";
126 my $opt_socket_wrapper = 0;
127 my $opt_socket_wrapper_pcap = undef;
128 my $opt_socket_wrapper_keep_pcap = undef;
130 my $opt_immediate = 0;
131 my $opt_expected_failures = undef;
132 my $opt_skip = undef;
136 my $opt_analyse_cmd = undef;
137 my $opt_resetup_env = undef;
138 my $opt_bindir = undef;
139 my $opt_no_lazy_setup = undef;
140 my $opt_format = "plain";
146 my @expected_failures = ();
150 START_TIME => time(),
156 TESTS_UNEXPECTED_OK => 0,
157 TESTS_EXPECTED_OK => 0,
158 TESTS_UNEXPECTED_FAIL => 0,
159 TESTS_EXPECTED_FAIL => 0,
164 sub expecting_failure($)
166 my $fullname = shift;
168 foreach (@expected_failures) {
169 return 1 if ($fullname =~ /$_/);
177 my $fullname = shift;
180 return 1 if ($fullname =~ /$_/);
192 return unless ($opt_socket_wrapper_pcap);
193 return unless defined($ENV{SOCKET_WRAPPER_PCAP_DIR});
195 my $fname = sprintf("t%03u_%s", $state->{INDEX}, $state->{NAME});
196 $fname =~ s%[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\-]%_%g;
198 $state->{PCAP_FILE} = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/$fname.pcap";
200 SocketWrapper::setup_pcap($state->{PCAP_FILE});
203 sub cleanup_pcap($$$)
205 my ($state, $expected_ret, $ret) = @_;
207 return unless ($opt_socket_wrapper_pcap);
208 return if ($opt_socket_wrapper_keep_pcap);
209 return unless ($expected_ret == $ret);
210 return unless defined($state->{PCAP_FILE});
212 unlink($state->{PCAP_FILE});
213 $state->{PCAP_FILE} = undef;
216 sub run_testsuite($$$$$$)
218 my ($envname, $name, $cmd, $i, $totalsuites, $msg_ops) = @_;
224 TOTAL => $totalsuites,
228 setup_pcap($msg_state);
230 open(RESULT, "$cmd 2>&1|");
231 $msg_ops->start_testsuite($msg_state);
233 my $expected_ret = parse_results(
234 $msg_ops, $msg_state, $statistics, *RESULT, \&expecting_failure);
236 my $ret = close(RESULT);
238 cleanup_pcap($msg_state, $expected_ret, $ret);
240 $msg_ops->end_testsuite($msg_state, $expected_ret, $ret,
241 getlog_env($msg_state->{ENVNAME}));
243 if (not $opt_socket_wrapper_keep_pcap and
244 defined($msg_state->{PCAP_FILE})) {
245 $msg_ops->output_msg($msg_state,
246 "PCAP FILE: $msg_state->{PCAP_FILE}\n");
249 if ($ret != $expected_ret) {
250 $statistics->{SUITES_FAIL}++;
251 exit(1) if ($opt_one);
253 $statistics->{SUITES_OK}++;
256 return ($ret == $expected_ret);
261 print "Samba test runner
262 Copyright (C) Jelmer Vernooij <jelmer\@samba.org>
264 Usage: $Script [OPTIONS] PREFIX
267 --help this help page
268 --target=samba4|samba3|win Samba version to target
271 --prefix=DIR prefix to run tests in [st]
272 --srcdir=DIR source directory [.]
273 --builddir=DIR output directory [.]
276 --socket-wrapper-pcap=DIR save traffic to pcap directories
277 --socket-wrapper-keep-pcap keep all pcap files, not just those for tests that
279 --socket-wrapper enable socket wrapper
280 --expected-failures=FILE specify list of tests that is guaranteed to fail
283 --ldap=openldap|fedora-ds back smbd onto specified ldap server
286 --bindir=PATH path to binaries
289 --quick run quick overall test
290 --one abort when the first test fails
291 --immediate print test output for failed tests during run
293 --analyse-cmd CMD command to run after each test
298 my $result = GetOptions (
299 'help|h|?' => \$opt_help,
300 'target=s' => \$opt_target,
301 'prefix=s' => \$prefix,
302 'socket-wrapper' => \$opt_socket_wrapper,
303 'socket-wrapper-pcap' => \$opt_socket_wrapper_pcap,
304 'socket-wrapper-keep-pcap' => \$opt_socket_wrapper_keep_pcap,
305 'quick' => \$opt_quick,
307 'immediate' => \$opt_immediate,
308 'expected-failures=s' => \$opt_expected_failures,
309 'skip=s' => \$opt_skip,
310 'srcdir=s' => \$srcdir,
311 'builddir=s' => \$builddir,
312 'verbose' => \$opt_verbose,
313 'testenv' => \$opt_testenv,
315 'analyse-cmd=s' => \$opt_analyse_cmd,
316 'no-lazy-setup' => \$opt_no_lazy_setup,
317 'resetup-environment' => \$opt_resetup_env,
318 'bindir:s' => \$opt_bindir,
319 'format=s' => \$opt_format,
322 exit(1) if (not $result);
324 ShowHelp() if ($opt_help);
328 # quick hack to disable rpc validation when using valgrind - its way too slow
329 unless (defined($ENV{VALGRIND})) {
330 $ENV{VALIDATE} = "validate";
331 $ENV{MALLOC_CHECK_} = 2;
334 my $old_pwd = "$RealBin/..";
336 # Backwards compatibility:
337 if (defined($ENV{TEST_LDAP}) and $ENV{TEST_LDAP} eq "yes") {
338 if (defined($ENV{FEDORA_DS_PREFIX})) {
345 my $torture_maxtime = ($ENV{TORTURE_MAXTIME} or 1200);
348 $torture_maxtime *= 2;
355 die("using an empty prefix isn't allowed") unless $prefix ne "";
357 #Ensure we have the test prefix around
358 mkdir($prefix, 0777) unless -d $prefix;
360 my $prefix_abs = abs_path($prefix);
361 my $srcdir_abs = abs_path($srcdir);
363 die("using an empty absolute prefix isn't allowed") unless $prefix_abs ne "";
364 die("using '/' as absolute prefix isn't allowed") unless $prefix_abs ne "/";
366 $ENV{PREFIX} = $prefix;
367 $ENV{PREFIX_ABS} = $prefix_abs;
368 $ENV{SRCDIR} = $srcdir;
369 $ENV{SRCDIR_ABS} = $srcdir_abs;
371 my $tls_enabled = not $opt_quick;
372 if (defined($ENV{RUN_FROM_BUILD_FARM}) and
373 ($ENV{RUN_FROM_BUILD_FARM} eq "yes")) {
374 $opt_format = "buildfarm";
377 $ENV{TLS_ENABLED} = ($tls_enabled?"yes":"no");
378 $ENV{LD_LDB_MODULE_PATH} = "$old_pwd/bin/modules/ldb";
379 $ENV{LD_SAMBA_MODULE_PATH} = "$old_pwd/bin/modules";
380 if (defined($ENV{LD_LIBRARY_PATH})) {
381 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared:$ENV{LD_LIBRARY_PATH}";
383 $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared";
385 if (defined($ENV{PKG_CONFIG_PATH})) {
386 $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig:$ENV{PKG_CONFIG_PATH}";
388 $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig";
390 # Required for smbscript:
391 $ENV{PATH} = "$old_pwd/bin:$old_pwd:$ENV{PATH}";
394 if ($opt_socket_wrapper_pcap) {
395 # Socket wrapper pcap implies socket wrapper
396 $opt_socket_wrapper = 1;
399 my $socket_wrapper_dir;
400 if ($opt_socket_wrapper) {
401 $socket_wrapper_dir = SocketWrapper::setup_dir("$prefix/w", $opt_socket_wrapper_pcap);
402 print "SOCKET_WRAPPER_DIR=$socket_wrapper_dir\n";
404 warn("Not using socket wrapper, but also not running as root. Will not be able to listen on proper ports") unless $< == 0;
409 if ($opt_target eq "samba4") {
410 $target = new Samba4($opt_bindir or "$srcdir/bin", $ldap, "$srcdir/setup");
411 } elsif ($opt_target eq "samba3") {
412 if ($opt_socket_wrapper and `$opt_bindir/smbd -b | grep SOCKET_WRAPPER` eq "") {
413 die("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'. Exiting....");
416 $target = new Samba3($opt_bindir);
417 } elsif ($opt_target eq "win") {
418 die("Windows tests will not run with socket wrapper enabled.")
419 if ($opt_socket_wrapper);
420 $target = new Windows();
423 if (defined($opt_expected_failures)) {
424 open(KNOWN, "<$opt_expected_failures") or die("unable to read known failures file: $!");
428 push (@expected_failures, $_); }
432 if (defined($opt_skip)) {
433 open(SKIP, "<$opt_skip") or die("unable to read skip file: $!");
441 my $interfaces = join(',', ("127.0.0.6/8",
448 my $conffile = "$prefix_abs/client/client.conf";
450 sub write_clientconf($$)
452 my ($conffile, $vars) = @_;
454 mkdir("$prefix/client", 0777) unless -d "$prefix/client";
456 if ( -d "$prefix/client/private" ) {
457 unlink <$prefix/client/private/*>;
459 mkdir("$prefix/client/private", 0777);
462 open(CF, ">$conffile");
463 print CF "[global]\n";
464 if (defined($ENV{VALGRIND})) {
465 print CF "\ticonv:native = true\n";
467 print CF "\ticonv:native = false\n";
469 print CF "\tnetbios name = client\n";
470 if (defined($vars->{DOMAIN})) {
471 print CF "\tworkgroup = $vars->{DOMAIN}\n";
473 if (defined($vars->{REALM})) {
474 print CF "\trealm = $vars->{REALM}\n";
476 if (defined($vars->{NCALRPCDIR})) {
477 print CF "\tncalrpc dir = $vars->{NCALRPCDIR}\n";
479 if (defined($vars->{PIDDIR})) {
480 print CF "\tpid directory = $vars->{PIDDIR}\n";
482 if (defined($vars->{WINBINDD_SOCKET_DIR})) {
483 print CF "\twinbindd socket directory = $vars->{WINBINDD_SOCKET_DIR}\n";
486 private dir = $prefix_abs/client/private
487 js include = $srcdir_abs/scripting/libjs
488 name resolve order = bcast
489 interfaces = $interfaces
490 panic action = $srcdir_abs/script/gdb_backtrace \%PID\% \%PROG\%
492 notify:inotify = false
494 system:anonymous = true
495 torture:basedir = $prefix_abs/client
496 #We don't want to pass our self-tests if the PAC code is wrong
497 gensec:require_pac = true
503 my @torture_options = ();
504 push (@torture_options, "--configfile=$conffile");
505 # ensure any one smbtorture call doesn't run too long
506 push (@torture_options, "--maximum-runtime=$torture_maxtime");
507 push (@torture_options, "--target=$opt_target");
508 push (@torture_options, "--basedir=$prefix");
509 push (@torture_options, "--option=torture:progress=no") if ($opt_format eq "buildfarm");
510 push (@torture_options, "--format=subunit");
511 push (@torture_options, "--option=torture:quick=yes") if ($opt_quick);
513 $ENV{TORTURE_OPTIONS} = join(' ', @torture_options);
514 print "OPTIONS $ENV{TORTURE_OPTIONS}\n";
518 my $testsdir = "$srcdir/selftest";
519 $ENV{SMB_CONF_PATH} = "$conffile";
520 $ENV{CONFIGURATION} = "--configfile=$conffile";
522 my %required_envs = ();
525 open(IN, "$testsdir/tests_quick.sh|");
527 open(IN, "$testsdir/tests_all.sh|");
530 if ($_ eq "-- TEST --\n") {
537 if (not defined($tests) or $name =~ /$tests/) {
538 $required_envs{$env} = 1;
539 push (@todo, [$name, $env, $cmdline]);
545 close(IN) or die("Error creating recipe");
547 my $suitestotal = $#todo + 1;
551 my %running_envs = ();
553 my @exported_envvars = (
558 # domain controller stuff
578 "WINBINDD_SOCKET_DIR"
586 if ($envname eq "none") {
588 } elsif (defined($running_envs{$envname})) {
589 $testenv_vars = $running_envs{$envname};
590 if (not $target->check_env($testenv_vars)) {
591 $testenv_vars = undef;
594 $testenv_vars = $target->setup_env($envname, $prefix);
597 return undef unless defined($testenv_vars);
599 SocketWrapper::set_default_iface(6);
600 write_clientconf($conffile, $testenv_vars);
602 foreach (@exported_envvars) {
603 if (defined($testenv_vars->{$_})) {
604 $ENV{$_} = $testenv_vars->{$_};
610 $running_envs{$envname} = $testenv_vars;
611 return $testenv_vars;
614 sub exported_envvars_str($)
616 my ($testenv_vars) = @_;
619 foreach (@exported_envvars) {
620 next unless defined($testenv_vars->{$_});
621 $out .= $_."=".$testenv_vars->{$_}."\n";
630 return "" if ($envname eq "none");
631 return $target->getlog_env($running_envs{$envname});
637 return 1 if ($envname eq "none");
638 return $target->check_env($running_envs{$envname});
644 return if ($envname eq "none");
645 $target->teardown_env($running_envs{$envname});
646 delete $running_envs{$envname};
650 if ($opt_format eq "buildfarm") {
651 require output::buildfarm;
652 $msg_ops = new output::buildfarm();
653 } elsif ($opt_format eq "plain") {
654 require output::plain;
655 $msg_ops = new output::plain($opt_verbose, $opt_immediate, $statistics);
656 } elsif ($opt_format eq "html") {
657 require output::html;
658 mkdir("test-results", 0777);
659 $msg_ops = new output::html("test-results", $statistics);
661 die("Invalid output format '$opt_format'");
664 if ($opt_no_lazy_setup) {
665 setup_env($_) foreach (keys %required_envs);
669 my $testenv_name = $ENV{SELFTEST_TESTENV};
670 $testenv_name = "member" unless defined($testenv_name);
672 my $testenv_vars = setup_env($testenv_name);
674 $ENV{PIDDIR} = $testenv_vars->{PIDDIR};
676 my $envvarstr = exported_envvars_str($testenv_vars);
678 my $term = ($ENV{TERM} or "xterm");
679 system("$term -e 'echo -e \"
680 Welcome to the Samba4 Test environment '$testenv_name'
682 This matches the client environment used in make test
683 smbd is pid `cat \$PIDDIR/smbd.pid`
685 Some useful environment variables:
686 TORTURE_OPTIONS=\$TORTURE_OPTIONS
687 CONFIGURATION=\$CONFIGURATION
691 teardown_env($testenv_name);
696 $cmd =~ s/([\(\)])/\\$1/g;
698 my $envname = $$_[1];
701 $msg_ops->skip_testsuite($envname, $name);
702 $statistics->{SUITES_SKIPPED}++;
706 my $envvars = setup_env($envname);
707 if (not defined($envvars)) {
708 $statistics->{SUITES_FAIL}++;
709 $statistics->{TESTS_ERROR}++;
710 $msg_ops->missing_env($name, $envname);
714 run_testsuite($envname, $name, $cmd, $i, $suitestotal, $msg_ops);
716 if (defined($opt_analyse_cmd)) {
717 system("$opt_analyse_cmd \"$name\"");
720 teardown_env($envname) if ($opt_resetup_env);
726 teardown_env($_) foreach (keys %running_envs);
730 $statistics->{END_TIME} = time();
731 my $duration = ($statistics->{END_TIME}-$statistics->{START_TIME});
733 print "DURATION: $duration seconds\n";
737 # if there were any valgrind failures, show them
738 foreach (<$prefix/valgrind.log*>) {
740 system("grep DWARF2.CFI.reader $_ > /dev/null");
742 print "VALGRIND FAILURE\n";
748 if ($opt_format eq "buildfarm") {
749 print "TEST STATUS: $statistics->{SUITES_FAIL}\n";
752 exit $statistics->{SUITES_FAIL};