← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:revision-status-reports-without-result into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:revision-status-reports-without-result into launchpad:master.

Commit message:
Fix display of RevisionStatusReports with result=None

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/417343

A revision status report whose build job hasn't started yet may have `result=None`.  Fix display of such reports not to crash, and improve icon selection for revision status reports more generally in the process.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:revision-status-reports-without-result into launchpad:master.
diff --git a/lib/lp/app/browser/configure.zcml b/lib/lp/app/browser/configure.zcml
index 753ce04..9419f7f 100644
--- a/lib/lp/app/browser/configure.zcml
+++ b/lib/lp/app/browser/configure.zcml
@@ -610,6 +610,13 @@
       name="image"
       />
 
+  <adapter
+      for="lp.code.interfaces.revisionstatus.IRevisionStatusReport"
+      provides="zope.traversing.interfaces.IPathAdapter"
+      factory="lp.app.browser.tales.RevisionStatusReportImageDisplayAPI"
+      name="image"
+      />
+
   <!-- TALES badges: namespace -->
 
   <adapter
diff --git a/lib/lp/app/browser/tales.py b/lib/lp/app/browser/tales.py
index 905ba0d..304fb1b 100644
--- a/lib/lp/app/browser/tales.py
+++ b/lib/lp/app/browser/tales.py
@@ -60,6 +60,7 @@ from lp.blueprints.interfaces.specification import ISpecification
 from lp.blueprints.interfaces.sprint import ISprint
 from lp.bugs.interfaces.bug import IBug
 from lp.buildmaster.enums import BuildStatus
+from lp.code.enums import RevisionStatusResult
 from lp.code.interfaces.branch import IBranch
 from lp.layers import LaunchpadLayer
 from lp.registry.interfaces.distribution import IDistribution
@@ -1207,6 +1208,45 @@ class SnapBuildRequestImageDisplayAPI(ObjectImageDisplayAPI):
             }
 
 
+class RevisionStatusReportImageDisplayAPI(ObjectImageDisplayAPI):
+    """Adapter for IRevisionStatusReport objects to an image.
+
+    Used for image:icon.
+    """
+    icon_template = (
+        '<img width="%(width)s" height="14" alt="%(alt)s" '
+        'title="%(title)s" src="%(src)s" />')
+
+    def icon(self):
+        """Return the appropriate <img> tag for the result icon."""
+        icon_map = {
+            RevisionStatusResult.WAITING: {"src": "/@@/build-needed"},
+            RevisionStatusResult.RUNNING: {"src": "/@@/processing"},
+            RevisionStatusResult.SUCCEEDED: {"src": "/@@/yes"},
+            RevisionStatusResult.FAILED: {"src": "/@@/no"},
+            RevisionStatusResult.SKIPPED: {"src": "/@@/yes-gray"},
+            RevisionStatusResult.CANCELLED: {
+                "src": "/@@/build-failed",
+                "width": "16",
+                },
+            }
+
+        result = self._context.result
+        if result is None:
+            result = RevisionStatusResult.WAITING
+        alt = "[%s]" % result.name
+        title = result.title
+        source = icon_map[result].get("src")
+        width = icon_map[result].get("width", "14")
+
+        return self.icon_template % {
+            "alt": alt,
+            "title": title,
+            "src": source,
+            "width": width,
+            }
+
+
 class BadgeDisplayAPI:
     """Adapter for IHasBadges to the images for the badges.
 
diff --git a/lib/lp/app/doc/tales.txt b/lib/lp/app/doc/tales.txt
index e68f86d..90c5080 100644
--- a/lib/lp/app/doc/tales.txt
+++ b/lib/lp/app/doc/tales.txt
@@ -229,6 +229,32 @@ But the 'failed to build' build is 16x14:
     >>> print(test_tales("build/image:icon", build=build))
     <img width="16" height="14"...src="/@@/build-failed" />
 
+Revision status reports have an icon for each result.
+
+    >>> from lp.code.enums import RevisionStatusResult
+
+    >>> report = factory.makeRevisionStatusReport()
+    >>> print(test_tales("report/image:icon", report=report))
+    <img width="14" height="14"...src="/@@/build-needed" />
+    >>> report.transitionToNewResult(RevisionStatusResult.WAITING)
+    >>> print(test_tales("report/image:icon", report=report))
+    <img width="14" height="14"...src="/@@/build-needed" />
+    >>> report.transitionToNewResult(RevisionStatusResult.RUNNING)
+    >>> print(test_tales("report/image:icon", report=report))
+    <img width="14" height="14"...src="/@@/processing" />
+    >>> report.transitionToNewResult(RevisionStatusResult.SUCCEEDED)
+    >>> print(test_tales("report/image:icon", report=report))
+    <img width="14" height="14"...src="/@@/yes" />
+    >>> report.transitionToNewResult(RevisionStatusResult.FAILED)
+    >>> print(test_tales("report/image:icon", report=report))
+    <img width="14" height="14"...src="/@@/no" />
+    >>> report.transitionToNewResult(RevisionStatusResult.SKIPPED)
+    >>> print(test_tales("report/image:icon", report=report))
+    <img width="14" height="14"...src="/@@/yes-gray" />
+    >>> report.transitionToNewResult(RevisionStatusResult.CANCELLED)
+    >>> print(test_tales("report/image:icon", report=report))
+    <img width="16" height="14"...src="/@@/build-failed" />
+
 All objects can be represented as a boolean icon.
 
     >>> print(test_tales("context/image:boolean", context=None))
diff --git a/lib/lp/code/browser/tests/test_branchmergeproposal.py b/lib/lp/code/browser/tests/test_branchmergeproposal.py
index 8e501cf..f774c48 100644
--- a/lib/lp/code/browser/tests/test_branchmergeproposal.py
+++ b/lib/lp/code/browser/tests/test_branchmergeproposal.py
@@ -1669,6 +1669,10 @@ class TestBranchMergeProposalView(TestCaseWithFactory):
             result_summary="120/120 tests passed",
             url="https://foo.com";,
             result=RevisionStatusResult.SUCCEEDED)
+        pending_report = self.factory.makeRevisionStatusReport(
+            user=bmp.source_git_repository.owner,
+            git_repository=bmp.source_git_repository,
+            title="Build", commit_sha1=sha1)
 
         self.useFixture(GitHostingFixture(log=[
             {
@@ -1695,12 +1699,18 @@ class TestBranchMergeProposalView(TestCaseWithFactory):
                     Tag(
                         "first report link", "a", text=report1.title,
                         attrs={"href": report1.url})))
-
             self.assertThat(
                 reports_section[0],
                 Tag(
                     "first report summary", "td",
                     text=report1.result_summary))
+            self.assertThat(
+                reports_section[0],
+                Within(
+                    Tag("pending report title", "td"),
+                    Tag(
+                        "pending report link", "a", text=pending_report.title,
+                        attrs={"href": None})))
 
             # Ensure we don't display an empty expander for those commits
             # that do not have status reports created for them - means we
diff --git a/lib/lp/code/browser/tests/test_gitref.py b/lib/lp/code/browser/tests/test_gitref.py
index 552e49a..18a94f1 100644
--- a/lib/lp/code/browser/tests/test_gitref.py
+++ b/lib/lp/code/browser/tests/test_gitref.py
@@ -191,20 +191,22 @@ class TestGitRefView(BrowserTestCase):
                                          paths=["refs/heads/branch"])
         log = self.makeCommitLog()
 
-        # create 2 status reports for 2 of the 5 commits available here
+        # create status reports for 2 of the 5 commits available here
         report1 = self.factory.makeRevisionStatusReport(
             user=repository.owner, git_repository=repository,
             title="CI", commit_sha1=log[0]["sha1"],
             result_summary="120/120 tests passed",
             url="https://foo1.com";,
             result=RevisionStatusResult.SUCCEEDED)
-
         report2 = self.factory.makeRevisionStatusReport(
             user=repository.owner, git_repository=repository,
             title="Lint", commit_sha1=log[1]["sha1"],
             result_summary="Invalid import in test_file.py",
             url="https://foo2.com";,
             result=RevisionStatusResult.FAILED)
+        pending_report = self.factory.makeRevisionStatusReport(
+            user=repository.owner, git_repository=repository,
+            title="Build", commit_sha1=log[1]["sha1"])
 
         self.hosting_fixture.getLog.result = list(log)
         # XXX jugmac00 2022-03-14
@@ -251,6 +253,13 @@ class TestGitRefView(BrowserTestCase):
                 soupmatchers.Tag(
                     "second report summary", "td",
                     text=report2.result_summary))
+            self.assertThat(
+                reports_section[1],
+                soupmatchers.Within(
+                    soupmatchers.Tag("pending report title", "td"),
+                    soupmatchers.Tag(
+                        "pending report link", "a", text=pending_report.title,
+                        attrs={"href": None})))
 
             # Ensure we don't display an empty expander for those commits
             # that do not have status reports created for them - means we
diff --git a/lib/lp/code/templates/git-macros.pt b/lib/lp/code/templates/git-macros.pt
index a990c18..997f96a 100644
--- a/lib/lp/code/templates/git-macros.pt
+++ b/lib/lp/code/templates/git-macros.pt
@@ -234,7 +234,7 @@
         <table class="listing" tal:condition="batch">
           <tbody>
             <tr tal:repeat="report batch">
-              <td tal:content="structure report/result/enumvalue:SUCCEEDED/image:boolean" />
+              <td class="icon" tal:content="structure report/image:icon" />
               <td><a tal:attributes="href report/url|nothing" tal:content="report/title" /></td>
               <td tal:content="report/result_summary" />
             </tr>
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index e145133..d8179b9 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -122,7 +122,6 @@ from lp.code.enums import (
     GitRepositoryType,
     RevisionControlSystems,
     RevisionStatusArtifactType,
-    RevisionStatusResult,
     TargetRevisionControlSystems,
     )
 from lp.code.errors import UnknownBranchTypeError
@@ -1876,8 +1875,6 @@ class BareLaunchpadObjectFactory(ObjectFactory):
                 commit_sha1 = hashlib.sha1(self.getUniqueBytes()).hexdigest()
         if result_summary is None:
             result_summary = self.getUniqueUnicode()
-        if result is None:
-            result = RevisionStatusResult.RUNNING
         return getUtility(IRevisionStatusReportSet).new(
             user, title, git_repository, commit_sha1, url,
             result_summary, result, ci_build=ci_build)