samba-tool domain join subdomain: Set "reveal_internals:0" control so we can see...
[nivanova/samba-autobuild/.git] / selftest / selftest.py
index ec2d1efde532967bc1f8b4789abc670fa10f2f4f..2da1ef8ff630ebf4be4c6140869b88361bae23c2 100755 (executable)
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import atexit
 from cStringIO import StringIO
 import os
 import sys
 import signal
-import shutil
 import subprocess
-import tempfile
-import time
+import subunit
+import traceback
 import warnings
 
 import optparse
@@ -35,7 +35,19 @@ from selftest import (
     subunithelper,
     testlist,
     )
-from selftest.target import EnvironmentManager
+from selftest.client import write_clientconf
+from selftest.run import (
+    expand_command_list,
+    expand_command_run,
+    exported_envvars_str,
+    now,
+    run_testsuite_command,
+    )
+from selftest.target import (
+    EnvironmentManager,
+    NoneTarget,
+    UnsupportedEnvironment,
+    )
 
 includes = ()
 excludes = ()
@@ -47,18 +59,17 @@ def read_includes(fn):
     includes.extend(testlist.read_test_regexes(fn))
 
 parser = optparse.OptionParser("TEST-REGEXES")
-parser.add_option("--target", type="choice", choices=["samba", "samba3"], default="samba", help="Samba version to target")
-parser.add_option("--quick", help="run quick overall test")
-parser.add_option("--verbose", help="be verbose")
-parser.add_option("--list", help="list available tests")
-parser.add_option("--socket-wrapper", help="enable socket wrapper")
+parser.add_option("--target", type="choice", choices=["samba", "samba3", "none"], default="samba", help="Samba version to target")
+parser.add_option("--quick", help="run quick overall test", action="store_true", default=False)
+parser.add_option("--list", help="list available tests", action="store_true", default=False)
+parser.add_option("--socket-wrapper", help="enable socket wrapper", action="store_true", default=False)
 parser.add_option("--socket-wrapper-pcap", help="save traffic to pcap directories", type="str")
-parser.add_option("--socket-wrapper-keep-pcap", help="keep all pcap files, not just those for tests that failed")
-parser.add_option("--one", help="abort when the first test fails")
+parser.add_option("--socket-wrapper-keep-pcap", help="keep all pcap files, not just those for tests that failed", action="store_true", default=False)
+parser.add_option("--one", help="abort when the first test fails", action="store_true", default=False)
 parser.add_option("--exclude", action="callback", help="Add file to exclude files", callback=read_excludes)
 parser.add_option("--include", action="callback", help="Add file to include files", callback=read_includes)
-parser.add_option("--testenv", help="run a shell in the requested test environment")
-parser.add_option("--resetup-environment", help="Re-setup environment")
+parser.add_option("--testenv", help="run a shell in the requested test environment", action="store_true", default=False)
+parser.add_option("--resetup-environment", help="Re-setup environment", action="store_true", default=False)
 parser.add_option("--binary-mapping", help="Map binaries to use", type=str)
 parser.add_option("--load-list", help="Load list of tests to load from a file", type=str)
 parser.add_option("--prefix", help="prefix to run tests in", type=str, default="./st")
@@ -71,11 +82,12 @@ opts, args = parser.parse_args()
 
 subunit_ops = subunithelper.SubunitOps(sys.stdout)
 
-def pipe_handler(sig):
-    sys.stderr.write("Exiting early because of SIGPIPE.\n")
+def handle_signal(sig, frame):
+    sys.stderr.write("Exiting early because of signal %s.\n" % sig)
     sys.exit(1)
 
-signal.signal(signal.SIGPIPE, pipe_handler)
+for sig in (signal.SIGINT, signal.SIGQUIT, signal.SIGTERm, signal.SIGPIPE):
+    signal.signal(sig, handle_signal)
 
 def skip(name):
     return testlist.find_in_list(excludes, name)
@@ -107,45 +119,20 @@ def cleanup_pcap(pcap_file, exit_code):
     os.unlink(pcap_file)
 
 
-# expand strings from %ENV
-def expand_environment_strings(s):
-    # we use a reverse sort so we do the longer ones first
-    for k in sorted(os.environ.keys(), reverse=True):
-        v = os.environ[k]
-        s = s.replace("$%s" % k, v)
-    return s
-
+def run_testsuite(name, cmd, subunit_ops, env=None):
+    """Run a single testsuite.
 
-def run_testsuite(envname, name, cmd, i, totalsuites):
+    :param env: Environment to run in
+    :param name: Name of the testsuite
+    :param cmd: Name of the (fully expanded) command to run
+    :return: exitcode of the command
+    """
     pcap_file = setup_pcap(name)
 
-    subunit_ops.start_testsuite(name)
-    subunit_ops.progress_push()
-    subunit_ops.report_time(time.time())
-    try:
-        exitcode = subprocess.call(cmd)
-    except Exception, e:
-        subunit_ops.report_time(time.time())
-        subunit_ops.progress_pop()
-        subunit_ops.progress_pop()
-        subunit_ops.end_testsuite(name, "error", "Unable to run %s: %s" % (cmd, e))
+    exitcode = run_testsuite_command(name, cmd, subunit_ops, env)
+    if exitcode is None:
         sys.exit(1)
 
-    subunit_ops.report_time(time.time())
-    subunit_ops.progress_pop()
-
-    envlog = env_manager.getlog_env(envname)
-    if envlog != "":
-        sys.stdout.write("envlog: %s\n" % envlog)
-
-    sys.stdout.write("command: %s\n" % cmd)
-    sys.stdout.write("expanded command: %s\n" % expand_environment_strings(cmd))
-
-    if exitcode == 0:
-        subunit_ops.end_testsuite(name, "success")
-    else:
-        subunit_ops.end_testsuite(name, "failure", "Exit code was %d" % exitcode)
-
     cleanup_pcap(pcap_file, exitcode)
 
     if not opts.socket_wrapper_keep_pcap and pcap_file is not None:
@@ -156,13 +143,14 @@ def run_testsuite(envname, name, cmd, i, totalsuites):
 
     return exitcode
 
+
 if opts.list and opts.testenv:
     sys.stderr.write("--list and --testenv are mutually exclusive\n")
     sys.exit(1)
 
 tests = args
 
-# quick hack to disable rpc validation when using valgrind - its way too slow
+# quick hack to disable rpc validation when using valgrind - it is way too slow
 if not os.environ.get("VALGRIND"):
     os.environ["VALIDATE"] = "validate"
     os.environ["MALLOC_CHECK_"] = "2"
@@ -186,14 +174,10 @@ if opts.ldap:
 
 prefix = os.path.normpath(opts.prefix)
 
-if prefix == "":
-    raise Exception("using an empty prefix isn't allowed")
-
 # Ensure we have the test prefix around.
 #
-# We need restrictive
-# permissions on this as some subdirectories in this tree will have
-# wider permissions (ie 0777) and this would allow other users on the
+# We need restrictive permissions on this as some subdirectories in this tree
+# will have wider permissions (ie 0777) and this would allow other users on the
 # host to subvert the test process.
 if not os.path.isdir(prefix):
     os.mkdir(prefix, 0700)
@@ -201,16 +185,14 @@ else:
     os.chmod(prefix, 0700)
 
 prefix_abs = os.path.abspath(prefix)
-tmpdir_abs = os.path.abspath(os.path.join(prefix, "tmp"))
+tmpdir_abs = os.path.abspath(os.path.join(prefix_abs, "tmp"))
 if not os.path.isdir(tmpdir_abs):
     os.mkdir(tmpdir_abs, 0777)
 
 srcdir_abs = os.path.abspath(opts.srcdir)
 
-if prefix_abs == "":
-    raise Exception("using an empty absolute prefix isn't allowed")
 if prefix_abs == "/":
-    raise Exception("using '/' as absolute prefix isn't allowed")
+    raise Exception("using '/' as absolute prefix is a bad idea")
 
 os.environ["PREFIX"] = prefix
 os.environ["KRB5CCNAME"] = os.path.join(prefix, "krb5ticket")
@@ -261,12 +243,15 @@ else:
 # must terminate in this time, and testenv will only stay alive this
 # long
 
-server_maxtime = 7500
 if os.environ.get("SMBD_MAXTIME", ""):
     server_maxtime = int(os.environ["SMBD_MAXTIME"])
+else:
+    server_maxtime = 7500
 
 
 def has_socket_wrapper(bindir):
+    """Check if Samba has been built with socket wrapper support.
+    """
     f = StringIO()
     subprocess.check_call([os.path.join(bindir, "smbd"), "-b"], stdout=f)
     for l in f.readlines():
@@ -290,8 +275,12 @@ if not opts.list:
         testenv_default = "member"
         from selftest.target.samba3 import Samba3
         target = Samba3(opts.bindir, binary_mapping, srcdir_abs, server_maxtime)
+    elif opts.target == "none":
+        testenv_default = "none"
+        target = NoneTarget()
 
     env_manager = EnvironmentManager(target)
+    atexit.register(env_manager.teardown_all)
 
 interfaces = ",".join([
     "127.0.0.11/8",
@@ -306,66 +295,9 @@ clientdir = os.path.join(prefix_abs, "client")
 conffile = os.path.join(clientdir, "client.conf")
 os.environ["SMB_CONF_PATH"] = conffile
 
-def write_clientconf(conffile, clientdir, vars):
-    if not os.path.isdir(clientdir):
-        os.mkdir(clientdir, 0777)
-
-    for n in ["private", "lockdir", "statedir", "cachedir"]:
-        p = os.path.join(clientdir, n)
-        shutil.rmtree(p)
-        os.mkdir(p, 0777)
-
-    # this is ugly, but the ncalrpcdir needs exactly 0755
-    # otherwise tests fail.
-    mask = os.umask(0022)
-
-    for n in ["ncalrpcdir", "ncalrpcdir/np"]:
-        p = os.path.join(clientdir, n)
-        shutil.rmtree(p)
-        os.mkdir(p, 0777)
-    os.umask(mask)
-
-    settings = {
-        "netbios name": "client",
-        "private dir": os.path.join(clientdir, "private"),
-        "lock dir": os.path.join(clientdir, "lockdir"),
-        "state directory": os.path.join(clientdir, "statedir"),
-        "cache directory": os.path.join(clientdir, "cachedir"),
-        "ncalrpc dir": os.path.join(clientdir, "ncalrpcdir"),
-        "name resolve order": "file bcast",
-        "panic action": os.path.join(os.path.dirname(__file__), "gdb_backtrace \%d"),
-        "max xmit": "32K",
-        "notify:inotify": "false",
-        "ldb:nosync": "true",
-        "system:anonymous": "true",
-        "client lanman auth": "Yes",
-        "log level": "1",
-        "torture:basedir": clientdir,
-    #We don't want to pass our self-tests if the PAC code is wrong
-        "gensec:require_pac": "true",
-        "resolv:host file": os.path.join(prefix_abs, "dns_host_file"),
-    #We don't want to run 'speed' tests for very long
-        "torture:timelimit": "1",
-        }
-
-    if "DOMAIN" in vars:
-        settings["workgroup"] = vars["DOMAIN"]
-    if "REALM" in vars:
-        settings["realm"] = vars["REALM"]
-    if opts.socket_wrapper:
-        settings["interfaces"] = interfaces
-
-    f = open(conffile, 'w')
-    try:
-        f.write("[global]\n")
-        for item in settings.iteritems():
-            f.write("\t%s = %s\n" % item)
-    finally:
-        f.close()
-
 todo = []
 
-if testlists == []:
+if not opts.testlist:
     sys.stderr.write("No testlists specified\n")
     sys.exit(1)
 
@@ -376,23 +308,21 @@ if opts.socket_wrapper:
     os.environ["SELFTEST_INTERFACES"] = interfaces
 else:
     os.environ["SELFTEST_INTERFACES"] = ""
-if opts.verbose:
-    os.environ["SELFTEST_VERBOSE"] = "1"
-else:
-    os.environ["SELFTEST_VERBOSE"] = ""
 if opts.quick:
     os.environ["SELFTEST_QUICK"] = "1"
 else:
     os.environ["SELFTEST_QUICK"] = ""
 os.environ["SELFTEST_MAXTIME"] = str(torture_maxtime)
 
+
 available = []
-for fn in testlists:
-    for testsuite in testlist.read_testlist(fn):
-        if not should_run_test(tests, testsuite):
+for fn in opts.testlist:
+    for testsuite in testlist.read_testlist_file(fn):
+        if not testlist.should_run_test(tests, testsuite):
             continue
         name = testsuite[0]
-        if includes is not None and testlist.find_in_list(includes, name) is not None:
+        if (includes is not None and
+            testlist.find_in_list(includes, name) is not None):
             continue
         available.append(testsuite)
 
@@ -401,7 +331,6 @@ if opts.load_list:
 else:
     restricted_mgr = None
 
-
 for testsuite in available:
     name = testsuite[0]
     skipreason = skip(name)
@@ -427,10 +356,8 @@ if todo == []:
 suitestotal = len(todo)
 
 if not opts.list:
-    subunit_ops.progress(suitestotal)
-    subunit_ops.report_time(time.time())
-
-i = 0
+    subunit_ops.progress(suitestotal, subunit.PROGRESS_SET)
+    subunit_ops.time(now())
 
 exported_envvars = [
     # domain stuff
@@ -461,6 +388,12 @@ exported_envvars = [
     "VAMPIRE_DC_NETBIOSNAME",
     "VAMPIRE_DC_NETBIOSALIAS",
 
+    # domain controller stuff for Vampired DC
+    "PROMOTED_DC_SERVER",
+    "PROMOTED_DC_SERVER_IP",
+    "PROMOTED_DC_NETBIOSNAME",
+    "PROMOTED_DC_NETBIOSALIAS",
+
     # server stuff
     "SERVER",
     "SERVER_IP",
@@ -482,26 +415,6 @@ exported_envvars = [
     "LOCAL_PATH"
 ]
 
-def handle_sigdie(signame):
-    env_manager.teardown_all()
-    sys.stderr.write("Received signal %s" % signame)
-    sys.exit(1)
-
-signal.signal(signal.SIGINT, handle_sigdie)
-signal.signal(signal.SIGQUIT, handle_sigdie)
-signal.signal(signal.SIGTERM, handle_sigdie)
-
-def exported_envvars_str(testenv_vars):
-    out = ""
-
-    for n in exported_envvars:
-        if not n in testenv_vars:
-            continue
-        out += "%s=%s\n" % (n, testenv_vars[n])
-
-    return out
-
-
 def switch_env(name, prefix):
     if ":" in name:
         (envname, option) = name.split(":", 1)
@@ -527,23 +440,26 @@ def switch_env(name, prefix):
     for name in exported_envvars:
         if name in testenv_vars:
             os.environ[name] = testenv_vars[name]
-        else:
+        elif name in os.environ:
             del os.environ[name]
 
-    return testenv_vars
+    return env
 
 # This 'global' file needs to be empty when we start
-os.unlink(os.path.join(prefix_abs, "dns_host_file"))
+dns_host_file_path = os.path.join(prefix_abs, "dns_host_file")
+if os.path.exists(dns_host_file_path):
+    os.unlink(dns_host_file_path)
 
 if opts.testenv:
     testenv_name = os.environ.get("SELFTEST_TESTENV", testenv_default)
 
-    testenv_vars = switch_env(testenv_name, prefix)
+    env = switch_env(testenv_name, prefix)
+    testenv_vars = env.get_vars()
 
     os.environ["PIDDIR"] = testenv_vars["PIDDIR"]
     os.environ["ENVNAME"] = testenv_name
 
-    envvarstr = exported_envvars_str(testenv_vars)
+    envvarstr = exported_envvars_str(testenv_vars, exported_envvars)
 
     term = os.environ.get("TERMINAL", "xterm -e")
     cmd = """'echo -e "
@@ -560,17 +476,16 @@ $envvarstr
 \" && LD_LIBRARY_PATH=%(LD_LIBRARY_PATH)s $(SHELL)'""" % {
         "testenv_name": testenv_name,
         "LD_LIBRARY_PATH": os.environ["LD_LIBRARY_PATH"]}
-    subprocess.call(term + ' ' + cmd)
+    subprocess.call(term + ' ' + cmd, shell=True)
     env_manager.teardown_env(testenv_name)
 elif opts.list:
     for (name, envname, cmd, supports_loadfile, supports_idlist, subtests) in todo:
-        if not "$LISTOPT" in cmd:
+        cmd = expand_command_list(cmd)
+        if cmd is None:
             warnings.warn("Unable to list tests in %s" % name)
             continue
 
-        cmd = cmd.replace("$LISTOPT", "--list")
-
-        exitcode = subprocess.call(cmd)
+        exitcode = subprocess.call(cmd, shell=True)
 
         if exitcode != 0:
             sys.stderr.write("%s exited with exit code %s\n" % (cmd, exitcode))
@@ -578,43 +493,33 @@ elif opts.list:
 else:
     for (name, envname, cmd, supports_loadfile, supports_idlist, subtests) in todo:
         try:
-            envvars = switch_env(envname, prefix)
-        except Exception:
-            subunit_ops.start_testsuite(name);
-            subunit_ops.end_testsuite(name, "error",
-                "unable to set up environment %s" % envname);
-            continue
-        if envvars is None:
-            subunit_ops.start_testsuite(name);
+            env = switch_env(envname, prefix)
+        except UnsupportedEnvironment:
+            subunit_ops.start_testsuite(name)
             subunit_ops.end_testsuite(name, "skip",
-                "environment is unknown in this test backend - skipping" % envname)
+                "environment %s is unknown in this test backend - skipping" % envname)
             continue
+        except Exception, e:
+            subunit_ops.start_testsuite(name)
+            traceback.print_exc()
+            subunit_ops.end_testsuite(name, "error",
+                "unable to set up environment %s: %s" % (envname, e))
+            continue
+
+        cmd, tmpf = expand_command_run(cmd, supports_loadfile, supports_idlist,
+            subtests)
 
-        # Generate a file with the individual tests to run, if the
-        # test runner for this test suite supports it.
-        if subtests is not None:
-            if supports_loadfile:
-                (fd, listid_file) = tempfile.mkstemp()
-                # FIXME: Remove tempfile afterwards
-                f = os.fdopen(fd)
-                try:
-                    for test in subtests:
-                        f.write(test+"\n")
-                finally:
-                    f.close()
-                cmd = cmd.replace("$LOADLIST", "--load-list=%s" % listid_file)
-            elif supports_idlist:
-                cmd += " ".join(subtests)
-
-        run_testsuite(envname, name, cmd, i, suitestotal)
-
-        if opts.resetup_env:
+        run_testsuite(name, cmd, subunit_ops, env=env)
+
+        if tmpf is not None:
+            os.remove(tmpf)
+
+        if opts.resetup_environment:
             env_manager.teardown_env(envname)
+    env_manager.teardown_all()
 
 sys.stdout.write("\n")
 
-env_manager.teardown_all()
-
 # if there were any valgrind failures, show them
 for fn in os.listdir(prefix):
     if fn.startswith("valgrind.log"):