pre-upload.py: enforce cq-depend/change-id positions

The CQ rejects CLs that have Cq-Depend's in places other
than the same paragraph as Change-Id, as shown in this
rejection message:

Your CL may be incorrectly using CQ feature(s):
 * Line 19: u'Cq-Depend: chromium:1626803, chromium:1626804,
 chromium:1626805, chromium:1626806, chromium:1620554, chromium:1627304,
 chromium:1627305': CQ-Depend must be in the last paragraph (like
 Change-Id:)

so checking it earlier can save one roundtrip.

BUG=none
TEST=tested both correct and incorrect input

Change-Id: I679785db2a62c0d1acbbf93239049588c6ae8921
Reviewed-on: https://chromium-review.googlesource.com/1639368
Commit-Ready: Luigi Semenzato <semenzato@chromium.org>
Tested-by: Luigi Semenzato <semenzato@chromium.org>
Legacy-Commit-Queue: Commit Bot <commit-bot@chromium.org>
Reviewed-by: Luigi Semenzato <semenzato@chromium.org>
diff --git a/pre-upload.py b/pre-upload.py
index db6edc2..441d873 100755
--- a/pre-upload.py
+++ b/pre-upload.py
@@ -100,7 +100,6 @@
 # excluded from presubmit checks. Lines beginning with '#' are ignored.
 _IGNORE_FILE = '.presubmitignore'
 
-
 # Exceptions
 
 
@@ -560,12 +559,21 @@
 
 def _check_change_has_valid_cq_depend(_project, commit):
   """Check for a correctly formatted Cq-Depend field in the commit message."""
+  desc = _get_commit_desc(commit)
   msg = 'Changelist has invalid Cq-Depend target.'
   example = 'Example: Cq-Depend: chromium:1234, chrome-internal:2345'
   try:
-    patch.GetPaladinDeps(_get_commit_desc(commit))
+    patch.GetPaladinDeps(desc)
   except ValueError as ex:
     return HookFailure(msg, [example, str(ex)])
+  # Check that Cq-Depend is in the same paragraph as Change-Id.
+  msg = 'Cq-Depend (or CQ-DEPEND) is not in the same paragraph as Change-Id.'
+  paragraphs = desc.split('\n\n')
+  for paragraph in paragraphs:
+    if (re.search(r'^Cq-Depend:', paragraph, re.M) or
+        re.search(r'^CQ-DEPEND=', paragraph, re.M)) \
+        and not re.search('^Change-Id:', paragraph, re.M):
+      return HookFailure(msg)
 
 
 def _check_change_is_contribution(_project, commit):
diff --git a/pre-upload_unittest.py b/pre-upload_unittest.py
index 7ad2504..99b695c 100755
--- a/pre-upload_unittest.py
+++ b/pre-upload_unittest.py
@@ -1053,12 +1053,13 @@
 
   def testNormal(self):
     """Accept valid Cq-Depends line."""
-    self.assertMessageAccepted('\nCq-Depend: chromium:1234\n')
+    self.assertMessageAccepted('\nCq-Depend: chromium:1234\nChange-Id: I123')
 
   def testInvalid(self):
     """Reject invalid Cq-Depends line."""
     self.assertMessageRejected('\nCq-Depend=chromium=1234\n')
     self.assertMessageRejected('\nCq-Depend: None\n')
+    self.assertMessageRejected('\nCq-Depend: chromium:1234\n\nChange-Id: I123')
 
 
 class CheckCommitMessageContribution(CommitMessageTestCase):