remove .fns file for removed host SerNet-aix
[build-farm.git] / buildfarm / build.py
index 4108ac0acb268d292f357084c3020ba7527fc2a1..fae37a179709b038109c2118833b7169f8569fdf 100644 (file)
@@ -125,15 +125,26 @@ class BuildStatus(object):
             return False
         if ("panic" in self.other_failures and
             not "panic" in older.other_failures):
+            # If this build introduced panics, then that's always worse.
             return True
         if len(self.stages) < len(older.stages):
             # Less stages completed
             return True
-        for ((old_name, old_result), (new_name, new_result)) in zip(
-            older.stages, self.stages):
-            assert old_name == new_name
-            if new_result > old_result:
+        old_stages = dict(older.stages)
+        new_stages = dict(self.stages)
+        for name, new_result in new_stages.iteritems():
+            try:
+                old_result = old_stages[name]
+            except KeyError:
+                continue
+            if new_result == old_result:
+                continue
+            if new_result < 0 and old_result >= 0:
                 return True
+            elif new_result >= 0 and old_result < 0:
+                return False
+            else:
+                return (abs(new_result) > abs(old_result))
         return False
 
     def __cmp__(self, other):
@@ -329,10 +340,7 @@ class Build(object):
 
     def read_subunit(self):
         """read the test output as subunit"""
-        try:
-            return open_opt_compressed_file(self.basename+".subunit")
-        except IOError:
-            raise NoTestOutput()
+        return StringIO("".join(extract_test_output(self.read_log())))
 
     def read_log(self):
         """read full log file"""
@@ -341,6 +349,15 @@ class Build(object):
         except IOError:
             raise LogFileMissing()
 
+    def has_log(self):
+        try:
+            f = self.read_log()
+        except LogFileMissing:
+            return False
+        else:
+            f.close()
+            return True
+
     def read_err(self):
         """read full err file"""
         try:
@@ -549,16 +566,6 @@ class BuildResultStore(object):
         os.link(build.basename+".log", new_basename+".log")
         if os.path.exists(build.basename+".err"):
             os.link(build.basename+".err", new_basename+".err")
-        try:
-            subunit_output = extract_test_output(build.read_log())
-        except NoTestOutput:
-            pass
-        else:
-            f = bz2.BZ2File(new_basename+".subunit.bz2", 'w')
-            try:
-                f.writelines(subunit_output)
-            finally:
-                f.close()
         new_build = StormBuild(new_basename, build.tree, build.host,
             build.compiler, rev)
         new_build.checksum = build.log_checksum()
@@ -575,8 +582,8 @@ class BuildResultStore(object):
     def get_by_checksum(self, checksum):
         from buildfarm.sqldb import Cast
         result = self.store.find(StormBuild,
-            Cast(StormBuild.checksum, "TEXT") == Cast(checksum, "TEXT"))
-        ret = result.one()
+            Cast(StormBuild.checksum, "TEXT") == Cast(checksum, "TEXT")).order_by(Desc(StormBuild.upload_time))
+        ret = result.first()
         if ret is None:
             raise NoSuchBuildError(None, None, None, None)
         return ret
@@ -607,3 +614,26 @@ class BuildResultStore(object):
         if build is None:
             raise NoSuchBuildError(tree, host, compiler)
         return build
+
+
+class BuildDiff(object):
+    """Represents the difference between two builds."""
+
+    def __init__(self, tree, old, new):
+        self.tree = tree
+        self.old = old
+        self.new = new
+        self.new_rev = new.revision_details()
+        self.new_status = new.status()
+
+        self.old_rev = old.revision_details()
+        self.old_status = old.status()
+
+    def is_regression(self):
+        """Is there a regression in new build since old build?"""
+        return self.new_status.regressed_since(self.old_status)
+
+    def revisions(self):
+        """Returns the revisions introduced since old in new."""
+        branch = self.tree.get_branch()
+        return branch.log(from_rev=self.new.revision, exclude_revs=set([self.old.revision]))