Fix and test python scripts and kerberos
[samba.git] / source4 / selftest / selftest.pl
index 7089cfa022e8147287d9111a4712bc7600994627..39a1b5a45089be203d4da12aa4dd4730f3a0b6b6 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 # Bootstrap Samba and run a number of tests against it.
-# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
+# Copyright (C) 2005-2008 Jelmer Vernooij <jelmer@samba.org>
 # Published under the GNU GPL, v3 or later.
 
 =pod
@@ -13,7 +13,7 @@ selftest - Samba test runner
 
 selftest --help
 
-selftest [--srcdir=DIR] [--builddir=DIR] [--target=samba4|samba3|win] [--socket-wrapper] [--quick] [--one] [--prefix=prefix] [--immediate] [--testlist=FILE] [TESTS]
+selftest [--srcdir=DIR] [--builddir=DIR] [--target=samba4|samba3|win|kvm] [--socket-wrapper] [--quick] [--exclude=FILE] [--include=FILE] [--one] [--prefix=prefix] [--immediate] [--testlist=FILE] [TESTS]
 
 =head1 DESCRIPTION
 
@@ -43,7 +43,7 @@ Change directory to run tests in. Default is 'st'.
 
 Show errors as soon as they happen rather than at the end of the test run.
                
-=item I<--target samba4|samba3|win>
+=item I<--target samba4|samba3|win|kvm>
 
 Specify test target against which to run. Default is 'samba4'.
 
@@ -67,17 +67,24 @@ these tests will be counted as successes, successes will be counted as failures.
 
 The format for the file is, one entry per line:
 
-TESTSUITE-NAME/TEST-NAME
+TESTSUITE-NAME.TEST-NAME
 
 The reason for a test can also be specified, by adding a hash sign (#) and the reason 
 after the test name.
 
-=item I<--skip>
+=item I<--exclude>
 
-Specify a file containing a list of tests that should be skipped. Possible candidates are
-tests that segfault the server, flip or don't end. The format of this file is the same as 
+Specify a file containing a list of tests that should be skipped. Possible 
+candidates are tests that segfault the server, flip or don't end. The format of this file is the same as 
 for the --expected-failures flag.
 
+=item I<--include>
+
+Specify a file containing a list of tests that should be run. Same format 
+as the --exclude flag.
+
+Not includes specified means all tests will be run.
+
 =item I<--one>
 
 Abort as soon as one test fails.
@@ -123,9 +130,6 @@ use POSIX;
 use Cwd qw(abs_path);
 use lib "$RealBin";
 use Subunit qw(parse_results);
-use env::Samba3;
-use env::Samba4;
-use env::Windows;
 use SocketWrapper;
 
 my $opt_help = 0;
@@ -137,8 +141,10 @@ my $opt_socket_wrapper_keep_pcap = undef;
 my $opt_one = 0;
 my $opt_immediate = 0;
 my $opt_expected_failures = undef;
-my $opt_skip = undef;
+my @opt_exclude = ();
+my @opt_include = ();
 my $opt_verbose = 0;
+my $opt_image = undef;
 my $opt_testenv = 0;
 my $ldap = undef;
 my $opt_analyse_cmd = undef;
@@ -146,21 +152,18 @@ my $opt_resetup_env = undef;
 my $opt_bindir = undef;
 my $opt_no_lazy_setup = undef;
 my $opt_format = "plain";
-my @opt_testlists = ();
+my @testlists = ();
 
 my $srcdir = ".";
 my $builddir = ".";
 my $prefix = "./st";
 
 my @expected_failures = ();
-my @skips = ();
+my @includes = ();
+my @excludes = ();
 
 my $statistics = {
-       START_TIME => time(),
-
        SUITES_FAIL => 0,
-       SUITES_OK => 0,
-       SUITES_SKIPPED => 0,
 
        TESTS_UNEXPECTED_OK => 0,
        TESTS_EXPECTED_OK => 0,
@@ -193,78 +196,77 @@ sub expecting_failure($)
 sub skip($)
 {
        my ($name) = @_;
-       return find_in_list(\@skips, $name);
+
+       return find_in_list(\@excludes, $name);
 }
 
 sub getlog_env($);
 
 sub setup_pcap($)
 {
-       my ($state) = @_;
+       my ($name) = @_;
 
        return unless ($opt_socket_wrapper_pcap);
        return unless defined($ENV{SOCKET_WRAPPER_PCAP_DIR});
 
-       my $fname = sprintf("t%03u_%s", $state->{INDEX}, $state->{NAME});
+       my $fname = $name;
        $fname =~ s%[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\-]%_%g;
 
-       $state->{PCAP_FILE} = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/$fname.pcap";
+       my $pcap_file = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/$fname.pcap";
+
+       SocketWrapper::setup_pcap($pcap_file);
 
-       SocketWrapper::setup_pcap($state->{PCAP_FILE});
+       return $pcap_file;
 }
 
 sub cleanup_pcap($$$)
 {
-       my ($state, $expected_ret, $ret) = @_;
+       my ($pcap_file, $expected_ret, $ret) = @_;
 
        return unless ($opt_socket_wrapper_pcap);
        return if ($opt_socket_wrapper_keep_pcap);
        return unless ($expected_ret == $ret);
-       return unless defined($state->{PCAP_FILE});
+       return unless defined($pcap_file);
 
-       unlink($state->{PCAP_FILE});
-       $state->{PCAP_FILE} = undef;
+       unlink($pcap_file);
 }
 
-sub run_testsuite($$$$$$$)
+sub run_testsuite($$$$$$)
 {
-       my ($envname, $envvars, $name, $cmd, $i, $totalsuites, $msg_ops) = @_;
-       my $msg_state = {
-               ENVNAME => $envname,
-               ENVVARS => $envvars,
-               NAME    => $name,
-               CMD     => $cmd,
-               INDEX   => $i,
-               TOTAL   => $totalsuites,
-               START_TIME      => time()
-       };
-
-       setup_pcap($msg_state);
+       my ($envname, $name, $cmd, $i, $totalsuites, $msg_ops) = @_;
+       my $pcap_file = setup_pcap($name);
 
-       open(RESULT, "$cmd 2>&1|");
-       $msg_ops->start_testsuite($msg_state);
+       $msg_ops->start_test([], $name);
 
+       open(RESULT, "$cmd 2>&1|");
        my $expected_ret = parse_results(
-               $msg_ops, $msg_state, $statistics, *RESULT, \&expecting_failure);
+               $msg_ops, $statistics, *RESULT, \&expecting_failure, [$name]);
+
+       my $envlog = getlog_env($envname);
+       $msg_ops->output_msg("ENVLOG: $envlog\n") if ($envlog ne "");
+
+       $msg_ops->output_msg("CMD: $cmd\n");
 
        my $ret = close(RESULT);
+       $ret = 0 unless $ret == 1;
 
-       cleanup_pcap($msg_state, $expected_ret, $ret);
+       if ($ret == 1) {
+               $msg_ops->end_test([], $name, "success", $expected_ret != $ret, undef);
+       } else {
+               $msg_ops->end_test([], $name, "failure", $expected_ret != $ret, 
+                                              "Returned $ret");
+       }
 
-       $msg_ops->end_testsuite($msg_state, $expected_ret, $ret,
-                                                       getlog_env($msg_state->{ENVNAME}));
+       cleanup_pcap($pcap_file, $expected_ret, $ret);
 
        if (not $opt_socket_wrapper_keep_pcap and 
-               defined($msg_state->{PCAP_FILE})) {
-               $msg_ops->output_msg($msg_state, 
-                       "PCAP FILE: $msg_state->{PCAP_FILE}\n");
+               defined($pcap_file)) {
+               $msg_ops->output_msg("PCAP FILE: $pcap_file\n");
        }
 
        if ($ret != $expected_ret) {
                $statistics->{SUITES_FAIL}++;
                exit(1) if ($opt_one);
-       } else {
-               $statistics->{SUITES_OK}++;
        }
 
        return ($ret == $expected_ret);
@@ -279,7 +281,7 @@ Usage: $Script [OPTIONS] PREFIX
 
 Generic options:
  --help                     this help page
- --target=samba4|samba3|win Samba version to target
+ --target=samba[34]|win|kvm Samba version to target
  --testlist=FILE                       file to read available tests from
 
 Paths:
@@ -288,7 +290,7 @@ Paths:
  --builddir=DIR             output directory [.]
 
 Target Specific:
- --socket-wrapper-pcap=DIR     save traffic to pcap directories
+ --socket-wrapper-pcap         save traffic to pcap directories
  --socket-wrapper-keep-pcap keep all pcap files, not just those for tests that 
                             failed
  --socket-wrapper           enable socket wrapper
@@ -300,6 +302,9 @@ Samba4 Specific:
 Samba3 Specific:
  --bindir=PATH              path to binaries
 
+Kvm Specific:
+ --image=PATH               path to KVM image
+
 Behaviour:
  --quick                    run quick overall test
  --one                      abort when the first test fails
@@ -321,7 +326,8 @@ my $result = GetOptions (
                'one' => \$opt_one,
                'immediate' => \$opt_immediate,
                'expected-failures=s' => \$opt_expected_failures,
-               'skip=s' => \$opt_skip,
+               'exclude=s' => \@opt_exclude,
+               'include=s' => \@opt_include,
                'srcdir=s' => \$srcdir,
                'builddir=s' => \$builddir,
                'verbose' => \$opt_verbose,
@@ -332,7 +338,8 @@ my $result = GetOptions (
                'resetup-environment' => \$opt_resetup_env,
                'bindir:s' => \$opt_bindir,
                'format=s' => \$opt_format,
-               'testlist=s' => \@opt_testlists
+               'image=s' => \$opt_image,
+               'testlist=s' => \@testlists
            );
 
 exit(1) if (not $result);
@@ -351,7 +358,7 @@ my $old_pwd = "$RealBin/..";
 
 # Backwards compatibility:
 if (defined($ENV{TEST_LDAP}) and $ENV{TEST_LDAP} eq "yes") {
-       if (defined($ENV{FEDORA_DS_PREFIX})) {
+       if (defined($ENV{FEDORA_DS_ROOT})) {
                $ldap = "fedora-ds";
        } else {
                $ldap = "openldap";
@@ -380,24 +387,20 @@ die("using an empty absolute prefix isn't allowed") unless $prefix_abs ne "";
 die("using '/' as absolute prefix isn't allowed") unless $prefix_abs ne "/";
 
 $ENV{PREFIX} = $prefix;
+$ENV{KRB5CCNAME} = "$prefix/krb5ticket";
 $ENV{PREFIX_ABS} = $prefix_abs;
 $ENV{SRCDIR} = $srcdir;
 $ENV{SRCDIR_ABS} = $srcdir_abs;
 
-my $tls_enabled = not $opt_quick;
 if (defined($ENV{RUN_FROM_BUILD_FARM}) and 
        ($ENV{RUN_FROM_BUILD_FARM} eq "yes")) {
        $opt_format = "buildfarm";
 }
 
+my $tls_enabled = not $opt_quick;
 $ENV{TLS_ENABLED} = ($tls_enabled?"yes":"no");
-$ENV{LD_LDB_MODULE_PATH} = "$old_pwd/bin/modules/ldb";
+$ENV{LDB_MODULES_PATH} = "$old_pwd/bin/modules/ldb";
 $ENV{LD_SAMBA_MODULE_PATH} = "$old_pwd/bin/modules";
-if (defined($ENV{LD_LIBRARY_PATH})) {
-       $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared:$ENV{LD_LIBRARY_PATH}";
-} else {
-       $ENV{LD_LIBRARY_PATH} = "$old_pwd/bin/shared";
-}
 if (defined($ENV{PKG_CONFIG_PATH})) {
        $ENV{PKG_CONFIG_PATH} = "$old_pwd/bin/pkgconfig:$ENV{PKG_CONFIG_PATH}";
 } else { 
@@ -406,6 +409,10 @@ if (defined($ENV{PKG_CONFIG_PATH})) {
 # Required for smbscript:
 $ENV{PATH} = "$old_pwd/bin:$old_pwd:$ENV{PATH}";
 
+if ($opt_socket_wrapper_keep_pcap) {
+       # Socket wrapper keep pcap implies socket wrapper pcap
+       $opt_socket_wrapper_pcap = 1;
+}
 
 if ($opt_socket_wrapper_pcap) {
        # Socket wrapper pcap implies socket wrapper
@@ -421,19 +428,56 @@ if ($opt_socket_wrapper) {
 }
 
 my $target;
+my $testenv_default = "none";
 
 if ($opt_target eq "samba4") {
+       $testenv_default = "member";
+       require target::Samba4;
        $target = new Samba4($opt_bindir or "$srcdir/bin", $ldap, "$srcdir/setup");
 } elsif ($opt_target eq "samba3") {
        if ($opt_socket_wrapper and `$opt_bindir/smbd -b | grep SOCKET_WRAPPER` eq "") {
                die("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'.  Exiting....");
        }
-
+       $testenv_default = "dc";
+       require target::Samba3;
        $target = new Samba3($opt_bindir);
 } elsif ($opt_target eq "win") {
        die("Windows tests will not run with socket wrapper enabled.") 
                if ($opt_socket_wrapper);
+       $testenv_default = "dc";
+       require target::Windows;
        $target = new Windows();
+} elsif ($opt_target eq "kvm") {
+       die("Kvm tests will not run with socket wrapper enabled.") 
+               if ($opt_socket_wrapper);
+       require target::Kvm;
+       die("No image specified") unless ($opt_image);
+       $target = new Kvm($opt_image, undef);
+}
+
+#
+# Start a Virtual Distributed Ethernet Switch
+# Returns the pid of the switch.
+#
+sub start_vde_switch($)
+{
+       my ($path) = @_;
+
+       system("vde_switch --pidfile $path/vde.pid --sock $path/vde.sock --daemon");
+
+       open(PID, "$path/vde.pid");
+       <PID> =~ /([0-9]+)/;
+       my $pid = $1;
+       close(PID);
+
+       return $pid;
+}
+
+# Stop a Virtual Distributed Ethernet Switch
+sub stop_vde_switch($)
+{
+       my ($pid) = @_;
+       kill 9, $pid;
 }
 
 sub read_test_regexes($)
@@ -443,10 +487,10 @@ sub read_test_regexes($)
        open(LF, "<$name") or die("unable to read $name: $!");
        while (<LF>) { 
                chomp; 
-               if (/^(.*?)([ \t]+)\#(.*)$/) {
-                       push (@ret, [$1, $3]);
+               if (/^(.*?)([ \t]+)\#([\t ]*)(.*?)$/) {
+                       push (@ret, [$1, $4]);
                } else {
-                       s/^(.*?)([ \t]+)\#(.*)$//;
+                       s/^(.*?)([ \t]+)\#([\t ]*)(.*?)$//;
                        push (@ret, [$_, undef]); 
                }
        }
@@ -458,8 +502,16 @@ if (defined($opt_expected_failures)) {
        @expected_failures = read_test_regexes($opt_expected_failures);
 }
 
-if (defined($opt_skip)) {
-       @skips = read_test_regexes($opt_skip);
+foreach (@opt_exclude) {
+       push (@excludes, read_test_regexes($_));
+}
+
+if ($opt_quick) {
+       push (@includes, read_test_regexes("samba4-quick"));
+}
+
+foreach (@opt_include) {
+       push (@includes, read_test_regexes($_));
 }
 
 my $interfaces = join(',', ("127.0.0.6/8", 
@@ -506,11 +558,13 @@ sub write_clientconf($$)
        if (defined($vars->{WINBINDD_SOCKET_DIR})) {
                print CF "\twinbindd socket directory = $vars->{WINBINDD_SOCKET_DIR}\n";
        }
+       if ($opt_socket_wrapper) {
+               print CF "\tinterfaces = $interfaces\n";
+       }
        print CF "
        private dir = $prefix_abs/client/private
        js include = $srcdir_abs/scripting/libjs
        name resolve order = bcast
-       interfaces = $interfaces
        panic action = $srcdir_abs/script/gdb_backtrace \%PID\% \%PROG\%
        max xmit = 32K
        notify:inotify = false
@@ -519,18 +573,18 @@ sub write_clientconf($$)
        torture:basedir = $prefix_abs/client
 #We don't want to pass our self-tests if the PAC code is wrong
        gensec:require_pac = true
+       modules dir = $ENV{LD_SAMBA_MODULE_PATH}
 ";
        close(CF);
 }
 
-
 my @torture_options = ();
 push (@torture_options, "--configfile=$conffile");
 # ensure any one smbtorture call doesn't run too long
 push (@torture_options, "--maximum-runtime=$torture_maxtime");
 push (@torture_options, "--target=$opt_target");
 push (@torture_options, "--basedir=$prefix_abs");
-push (@torture_options, "--option=torture:progress=no") if ($opt_format eq "buildfarm");
+push (@torture_options, "--option=torture:progress=no") unless ($opt_verbose);
 push (@torture_options, "--format=subunit");
 push (@torture_options, "--option=torture:quick=yes") if ($opt_quick);
 
@@ -572,16 +626,50 @@ sub read_testlist($)
        return @ret;
 }
 
-if ($opt_quick) {
-       @todo = read_testlist("$testsdir/tests_quick.sh|");
+if ($#testlists == -1) {
+       die("No testlists specified");
+}
+
+my @available = ();
+foreach my $fn (@testlists) {
+       foreach (read_testlist($fn)) {
+               my $name = $$_[0];
+               next if (@includes and not find_in_list(\@includes, $name));
+               push (@available, $_);
+       }
+}
+
+my $msg_ops;
+if ($opt_format eq "buildfarm") {
+       require output::buildfarm;
+       $msg_ops = new output::buildfarm($statistics);
+} elsif ($opt_format eq "plain") {
+       require output::plain;
+       $msg_ops = new output::plain("$prefix/summary", $opt_verbose, $opt_immediate, $statistics, $#available+1);
+} elsif ($opt_format eq "html") {
+       require output::html;
+       mkdir("test-results", 0777);
+       $msg_ops = new output::html("test-results", $statistics);
 } else {
-       @todo = read_testlist("$testsdir/tests_all.sh|");
+       die("Invalid output format '$opt_format'");
 }
 
-foreach (@opt_testlists) {
-       push(@todo, read_testlist($_));
+
+foreach (@available) {
+       my $name = $$_[0];
+       my $skipreason = skip($name);
+       if ($skipreason) {
+               $msg_ops->skip_testsuite($name, $skipreason);
+       } else {
+               push(@todo, $_); 
+       }
 }
 
+if ($#todo == -1) {
+       print STDERR "No tests to run\n";
+       exit(1);
+       }
+
 my $suitestotal = $#todo + 1;
 my $i = 0;
 $| = 1;
@@ -613,7 +701,8 @@ my @exported_envvars = (
 
        # misc stuff
        "KRB5_CONFIG",
-       "WINBINDD_SOCKET_DIR"
+       "WINBINDD_SOCKET_DIR",
+       "WINBINDD_PRIV_PIPE_DIR"
 );
 
 $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub { 
@@ -691,28 +780,13 @@ sub teardown_env($)
        delete $running_envs{$envname};
 }
 
-my $msg_ops;
-if ($opt_format eq "buildfarm") {
-       require output::buildfarm;
-       $msg_ops = new output::buildfarm();
-} elsif ($opt_format eq "plain") {
-       require output::plain;
-       $msg_ops = new output::plain($opt_verbose, $opt_immediate, $statistics);
-} elsif ($opt_format eq "html") {
-       require output::html;
-       mkdir("test-results", 0777);
-       $msg_ops = new output::html("test-results", $statistics);
-} else {
-       die("Invalid output format '$opt_format'");
-}
-
 if ($opt_no_lazy_setup) {
        setup_env($_) foreach (keys %required_envs);
 }
 
 if ($opt_testenv) {
        my $testenv_name = $ENV{SELFTEST_TESTENV};
-       $testenv_name = "member" unless defined($testenv_name);
+       $testenv_name = $testenv_default unless defined($testenv_name);
 
        my $testenv_vars = setup_env($testenv_name);
 
@@ -732,7 +806,7 @@ TORTURE_OPTIONS=\$TORTURE_OPTIONS
 CONFIGURATION=\$CONFIGURATION
 
 $envvarstr
-\" && bash'");
+\" && LD_LIBRARY_PATH=$ENV{LD_LIBRARY_PATH} bash'");
        teardown_env($testenv_name);
 } else {
        foreach (@todo) {
@@ -742,21 +816,13 @@ $envvarstr
                my $name = $$_[0];
                my $envname = $$_[1];
                
-               my $skipreason = skip($name);
-               if ($skipreason) {
-                       $msg_ops->skip_testsuite($envname, $name, $skipreason);
-                       $statistics->{SUITES_SKIPPED}++;
-                       next;
-               }
-
                my $envvars = setup_env($envname);
                if (not defined($envvars)) {
-                       $statistics->{SUITES_SKIPPED}++;
-                       $msg_ops->missing_env($name, $envname);
+                       $msg_ops->skip_testsuite($name, "unable to set up environment $envname");
                        next;
                }
 
-               run_testsuite($envname, $envvars, $name, $cmd, $i, $suitestotal, 
+               run_testsuite($envname, $name, $cmd, $i, $suitestotal, 
                              $msg_ops);
 
                if (defined($opt_analyse_cmd)) {
@@ -773,10 +839,7 @@ teardown_env($_) foreach (keys %running_envs);
 
 $target->stop();
 
-$statistics->{END_TIME} = time();
-my $duration = ($statistics->{END_TIME}-$statistics->{START_TIME});
 $msg_ops->summary();
-print "DURATION: $duration seconds\n";
 
 my $failed = 0;