[fx][gerrit-submit] Give details why a CL can't be submitted.

If a CL can't be submitted, update the error message from:

  CL 123 can not be submitted.

to:

  CL 123 can not be submitted: CL has unresolved comments.

Change-Id: Ie04d4fbeee07e6da887dceb3414a06998b4f8c02
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/443798
Testability-Review: David Greenaway <dgreenaway@google.com>
Reviewed-by: Adrian Danis <adanis@google.com>
Commit-Queue: David Greenaway <dgreenaway@google.com>
diff --git a/tools/devshell/contrib/gerrit-submit-lib/submit.py b/tools/devshell/contrib/gerrit-submit-lib/submit.py
index 8ccee09..1507a0c 100755
--- a/tools/devshell/contrib/gerrit-submit-lib/submit.py
+++ b/tools/devshell/contrib/gerrit-submit-lib/submit.py
@@ -29,6 +29,7 @@
   MERGED = 7
 
   def description(self) -> str:
+    # Return a brief description of a CL's state.
     return {
         ChangeStatus.UNKNOWN: 'unknown',
         ChangeStatus.MISSING_VOTES: 'missing votes',
@@ -51,6 +52,15 @@
         ChangeStatus.MERGED: ansi.gray,
     }[self]
 
+  def submit_error_description(self) -> Optional[str]:
+    # Return an explanation of why this CL cannot be submitted.
+    return {
+        ChangeStatus.UNKNOWN: 'unknown error',
+        ChangeStatus.MISSING_VOTES: 'CL is missing votes',
+        ChangeStatus.UNRESOLVED_COMMENTS: 'CL has unresolved comments',
+    }.get(self)
+
+
 
 class Change:
   """A Gerrit Changelist."""
@@ -202,7 +212,8 @@
   """Ensure that the given list of changes are submittable."""
   for cl in changes:
     if cl.status != ChangeStatus.MERGED and not should_submit(cl):
-      raise SubmitError("CL %d can not be submitted." % cl.id)
+      raise SubmitError("CL %d can not be submitted: %s" % (
+          cl.id, cl.status.submit_error_description() or "unknown error"))
 
 
 def submit_changes(
diff --git a/tools/devshell/contrib/gerrit-submit-lib/submit_test.py b/tools/devshell/contrib/gerrit-submit-lib/submit_test.py
index ed46079c..3211ef4 100755
--- a/tools/devshell/contrib/gerrit-submit-lib/submit_test.py
+++ b/tools/devshell/contrib/gerrit-submit-lib/submit_test.py
@@ -208,6 +208,18 @@
     ])
 
 
+  def test_submit_cl_error_description(self) -> None:
+    c = submit.Change('abc', {'status': 'NEW', 'submittable': True, 'unresolved_comment_count': 2})
+    print(c.status.submit_error_description())
+    assert c.status.submit_error_description() == "CL has unresolved comments"
+
+    c = submit.Change('42', {'status': 'NEW', 'submittable': False})
+    assert c.status.submit_error_description() == "CL is missing votes"
+
+    c = submit.Change('42', {'status': 'NEW', 'submittable': True})
+    assert c.status.submit_error_description() is None
+
+
 class TestTypes(unittest.TestCase):
   def test_types(self) -> None:
     self_dir = os.path.dirname(os.path.realpath(__file__))