libcli/cldap: fix a crash bug in cldap_socket_recv_dgram() (bug #8593)
[gd/samba-autobuild/.git] / script / land.py
index 55bc65b72bba00db9c5015bf47c21409f3b80e54..72bdd4b8408cdd6093673c088485ec1e03180d88 100755 (executable)
@@ -16,6 +16,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../lib/subunit/pytho
 import subunit
 import testtools
 import subunithelper
+import tempfile
 from email.mime.application import MIMEApplication
 from email.mime.text import MIMEText
 from email.mime.multipart import MIMEMultipart
@@ -25,7 +26,7 @@ samba_master_ssh = os.getenv('SAMBA_MASTER_SSH', 'git+ssh://git.samba.org/data/g
 
 cleanup_list = []
 
-os.putenv('CC', "ccache gcc")
+os.environ['CC'] = "ccache gcc"
 
 tasks = {
     "source3" : [ ("autogen", "./autogen.sh", "text/plain"),
@@ -40,7 +41,7 @@ tasks = {
                   ("install", "make install", "text/plain"),
                   ("test", "TDB_NO_FSYNC=1 make subunit-test", "text/x-subunit") ],
 
-    "source4/lib/ldb" : [ ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
+    "lib/ldb" : [ ("configure", "./configure --enable-developer -C ${PREFIX}", "text/plain"),
                           ("make", "make -j", "text/plain"),
                           ("install", "make install", "text/plain"),
                           ("test", "make test", "text/plain") ],
@@ -124,15 +125,15 @@ class TreeStageBuilder(object):
         self.name = name
         self.command = command
         self.fail_quickly = fail_quickly
-        self.status = None
+        self.exitcode = None
         self.stdin = open(os.devnull, 'r')
 
     def start(self):
         raise NotImplementedError(self.start)
 
     def poll(self):
-        self.status = self.proc.poll()
-        return self.status
+        self.exitcode = self.proc.poll()
+        return self.exitcode
 
     def kill(self):
         if self.proc is not None:
@@ -147,11 +148,11 @@ class TreeStageBuilder(object):
 
     @property
     def failure_reason(self):
-        return "failed '%s' with status %d" % (self.cmd, self.status)
+        raise NotImplementedError(self.failure_reason)
 
     @property
     def failed(self):
-        return (os.WIFSIGNALED(self.status) or os.WEXITSTATUS(self.status) != 0)
+        return (self.exitcode != 0)
 
 
 class PlainTreeStageBuilder(TreeStageBuilder):
@@ -162,6 +163,10 @@ class PlainTreeStageBuilder(TreeStageBuilder):
                           stdout=self.tree.stdout, stderr=self.tree.stderr,
                           stdin=self.stdin)
 
+    @property
+    def failure_reason(self):
+        return "failed '%s' with exit code %d" % (self.command, self.exitcode)
+
 
 class AbortingTestResult(subunithelper.TestsuiteEnabledTestResult):
 
@@ -176,12 +181,27 @@ class AbortingTestResult(subunithelper.TestsuiteEnabledTestResult):
         self.stage.proc.terminate()
 
 
+class FailureTrackingTestResult(subunithelper.TestsuiteEnabledTestResult):
+
+    def __init__(self, stage):
+        super(FailureTrackingTestResult, self).__init__()
+        self.stage = stage
+
+    def addError(self, test, details=None):
+        if self.stage.failed_test is None:
+            self.stage.failed_test = ("error", test)
+
+    def addFailure(self, test, details=None):
+        if self.stage.failed_test is None:
+            self.stage.failed_test = ("failure", test)
+
+
 class SubunitTreeStageBuilder(TreeStageBuilder):
 
     def __init__(self, tree, name, command, fail_quickly=False):
         super(SubunitTreeStageBuilder, self).__init__(tree, name, command,
                 fail_quickly)
-        self.failed_tests = []
+        self.failed_test = None
         self.subunit_path = os.path.join(gitroot,
             "%s.%s.subunit" % (self.tree.tag, self.name))
         self.tree.logfiles.append(
@@ -190,7 +210,8 @@ class SubunitTreeStageBuilder(TreeStageBuilder):
         self.subunit = open(self.subunit_path, 'w')
 
         formatter = subunithelper.PlainFormatter(False, True, {})
-        clients = [formatter, subunit.TestProtocolClient(self.subunit)]
+        clients = [formatter, subunit.TestProtocolClient(self.subunit),
+                   FailureTrackingTestResult(self)]
         if fail_quickly:
             clients.append(AbortingTestResult(self))
         self.subunit_server = subunit.TestProtocolServer(
@@ -220,10 +241,17 @@ class SubunitTreeStageBuilder(TreeStageBuilder):
                 else:
                     buffered += l
             self.buffered = buffered
-            self.status = self.proc.poll()
-            if self.status is not None:
+            self.exitcode = self.proc.poll()
+            if self.exitcode is not None:
                 self.subunit.close()
-            return self.status
+            return self.exitcode
+
+    @property
+    def failure_reason(self):
+        if self.failed_test:
+            return "failed '%s' with %s in test %s" (self.command, self.failed_test[0], self.failed_test[1])
+        else:
+            return "failed '%s' with exit code %d in unknown test" % (self.command, self.exitcode)
 
 
 class TreeBuilder(object):
@@ -236,6 +264,7 @@ class TreeBuilder(object):
         self.tag = self.name.replace('/', '_')
         self.sequence = sequence
         self.next = 0
+        self.stages = []
         self.stdout_path = os.path.join(gitroot, "%s.stdout" % (self.tag, ))
         self.stderr_path = os.path.join(gitroot, "%s.stderr" % (self.tag, ))
         self.logfiles = [
@@ -264,6 +293,7 @@ class TreeBuilder(object):
         run_cmd(["rm",  "-rf", self.sdir])
         clone_gitroot(self.sdir, revision)
         self.start_next()
+        self.exitcode = None
 
     def start_next(self):
         if self.next == len(self.sequence):
@@ -275,42 +305,47 @@ class TreeBuilder(object):
         (stage_name, cmd, output_mime_type) = self.sequence[self.next]
         cmd = cmd.replace("${PREFIX}", "--prefix=%s" % self.prefix)
         if output_mime_type == "text/plain":
-            self.stage = PlainTreeStageBuilder(self, stage_name, cmd,
+            self.current_stage = PlainTreeStageBuilder(self, stage_name, cmd,
                 self.fail_quickly)
         elif output_mime_type == "text/x-subunit":
-            self.stage = SubunitTreeStageBuilder(self, stage_name, cmd,
+            self.current_stage = SubunitTreeStageBuilder(self, stage_name, cmd,
                 self.fail_quickly)
         else:
             raise Exception("Unknown output mime type %s" % output_mime_type)
-        self.stage.start()
+        self.stages.append(self.current_stage)
+        self.current_stage.start()
         self.next += 1
 
     def remove_logs(self):
         for path, name, mime_type in self.logfiles:
             os.unlink(path)
 
-    @property
-    def status(self):
-        return self.stage.status
-
     def poll(self):
-        return self.stage.poll()
+        self.exitcode = self.current_stage.poll()
+        if self.exitcode is not None:
+            self.current_stage = None
+        return self.exitcode
 
     def kill(self):
-        if self.stage is not None:
-            self.stage.kill()
-            self.stage = None
+        if self.current_stage is not None:
+            self.current_stage.kill()
+            self.current_stage = None
 
     @property
     def failed(self):
-        if self.stage is None:
-            return False
-        return self.stage.failed
+        return any([s.failed for s in self.stages])
+
+    @property
+    def failed_stage(self):
+        for s in self.stages:
+            if s.failed:
+                return s
+        return s
 
     @property
     def failure_reason(self):
-        return "%s: [%s] %s" % (self.name, self.stage.name,
-            self.stage.failure_reason)
+        return "%s: [%s] %s" % (self.name, self.failed_stage.name,
+            self.failed_stage.failure_reason)
 
 
 class BuildList(object):
@@ -348,12 +383,11 @@ class BuildList(object):
         while True:
             none_running = True
             for b in self.tlist:
-                if b.stage is None:
+                if b.current_stage is None:
                     continue
                 none_running = False
                 if b.poll() is None:
                     continue
-                b.stage = None
                 return b
             if options.retry:
                 ret = self.retry.poll()
@@ -376,7 +410,7 @@ class BuildList(object):
                 break
             if b.failed:
                 self.kill_kids()
-                return (b.status, b.name, b.stage, b.tag, b.failure_reason)
+                return (b.exitcode, b.name, b.failed_stage, b.tag, b.failure_reason)
             b.start_next()
         self.kill_kids()
         return (0, None, None, None, "All OK")
@@ -478,7 +512,12 @@ def push_to(url):
     run_cmd(["git", "push", "pushto", "+HEAD:master"], show=True,
         dir=test_master)
 
-def_testbase = os.getenv("AUTOBUILD_TESTBASE", "/memdisk/%s" % os.getenv('USER'))
+def_testbase = os.getenv("AUTOBUILD_TESTBASE")
+if def_testbase is None:
+    if os.path.exists("/memdisk"):
+        def_testbase = "/memdisk/%s" % os.getenv('USER')
+    else:
+        def_testbase = os.path.join(tempfile.gettempdir(), "autobuild-%s" % os.getenv("USER"))
 
 parser = OptionParser()
 parser.add_option("--repository", help="repository to run tests for", default=None, type=str)
@@ -515,7 +554,7 @@ parser.add_option("--fail-slowly", help="continue running tests even after one h
                   action="store_true")
 
 
-def email_failure(blist, status, failed_task, failed_stage, failed_tag, errstr):
+def email_failure(blist, exitcode, failed_task, failed_stage, failed_tag, errstr):
     '''send an email to options.email about the failure'''
     user = os.getenv("USER")
     text = '''
@@ -548,7 +587,7 @@ The top commit for the tree that was built was:
 
     msg = MIMEMultipart()
     msg['Subject'] = 'autobuild failure for task %s during %s' % (
-        failed_task, failed_stage)
+        failed_task, failed_stage.name)
     msg['From'] = 'autobuild@samba.org'
     msg['To'] = options.email
 
@@ -658,8 +697,8 @@ while True:
         blist = BuildList(tasks, args)
         if options.tail:
             blist.start_tail()
-        (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
-        if status != 0 or errstr != "retry":
+        (exitcode, failed_task, failed_stage, failed_tag, errstr) = blist.run()
+        if exitcode != 0 or errstr != "retry":
             break
         cleanup()
     except:
@@ -671,7 +710,7 @@ if options.tail:
     print("waiting for tail to flush")
     time.sleep(1)
 
-if status == 0:
+if exitcode == 0:
     print errstr
     if options.passcmd is not None:
         print("Running passcmd: %s" % options.passcmd)
@@ -693,10 +732,10 @@ else:
     blist.tarlogs("logs.tar.gz")
 
     if options.email is not None:
-        email_failure(blist, status, failed_task, failed_stage, failed_tag,
+        email_failure(blist, exitcode, failed_task, failed_stage, failed_tag,
             errstr)
 
     cleanup()
     print(errstr)
     print("Logs in logs.tar.gz")
-sys.exit(status)
+sys.exit(exitcode)