Merge "Add keyed properties for issue occurrences in footer lines"
diff --git a/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/util/IssueExtractor.java b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/util/IssueExtractor.java
index 57f51da..ea8a788 100644
--- a/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/util/IssueExtractor.java
+++ b/hooks-its/src/main/java/com/googlesource/gerrit/plugins/hooks/util/IssueExtractor.java
@@ -152,6 +152,25 @@
         }
       } else {
         body = StringUtils.join(lines, "\n", 1, footerStart - 1);
+
+        StringBuilder footerBuilder = new StringBuilder();
+        for (int lineIdx = footerStart; lineIdx < footerEnd; lineIdx++) {
+          String line = lines[lineIdx];
+
+          // Adding occurrences for footer keys
+          int colonIdx = line.indexOf(':');
+          if (colonIdx > 0) {
+            // tag of length at least 1
+            String tag = line.substring(0, colonIdx);
+            addIssuesOccurrence(line, "footer-" + tag, ret);
+          }
+
+          // Putting back together the footer to a single String
+          if (lineIdx > footerStart) {
+            footerBuilder.append('\n');
+          }
+          footerBuilder.append(line);
+        }
         footer = StringUtils.join(lines, "\n", footerStart, footerEnd);
       }
       if (body != null) {
diff --git a/hooks-its/src/test/java/com/googlesource/gerrit/plugins/hooks/util/IssueExtractorTest.java b/hooks-its/src/test/java/com/googlesource/gerrit/plugins/hooks/util/IssueExtractorTest.java
index 53d69db..5d18b0c 100644
--- a/hooks-its/src/test/java/com/googlesource/gerrit/plugins/hooks/util/IssueExtractorTest.java
+++ b/hooks-its/src/test/java/com/googlesource/gerrit/plugins/hooks/util/IssueExtractorTest.java
@@ -212,6 +212,7 @@
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitMultipleIssues() {
@@ -239,6 +240,7 @@
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitMultipleIssuesMultipleTimes() {
@@ -266,6 +268,7 @@
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitSingleIssueBody() {
@@ -294,6 +297,8 @@
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitSingleIssueFooter() {
@@ -315,13 +320,56 @@
         "1234567891123456789212345678931234567894");
 
     Map<String,Set<String>> expected = Maps.newHashMap();
-    expected.put("42", Sets.newHashSet("somewhere", "footer"));
+    expected.put("42", Sets.newHashSet("somewhere", "footer",
+        "footer-Footer"));
     assertEquals("Extracted issues do not match", expected, actual);
 
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+  }
+
+  public void testIssueIdsCommitMultipleIssuesFooter() {
+    expect(serverConfig.getString("commentLink", "ItsTestName", "match"))
+    .andReturn("bug#(\\d+)").atLeastOnce();
+
+    expect(commitMessageFetcher.fetchGuarded("testProject",
+        "1234567891123456789212345678931234567894")).andReturn(
+            "Subject does not reference a bug\n" +
+            "Body does not reference a bug\n" +
+            "\n" +
+            "KeyA: references bug#42\n" +
+            "KeyB: does not reference bug\n" +
+            "KeyC: references bug#176\n" +
+            "Unkeyed reference to bug#4711\n" +
+            "Change-Id: I1234567891123456789212345678931234567894\n" +
+            "KeyZ: references bug#256" );
+
+    replayMocks();
+
+    IssueExtractor issueExtractor = injector.getInstance(IssueExtractor.class);
+    Map<String,Set<String>> actual = issueExtractor.getIssueIds("testProject",
+        "1234567891123456789212345678931234567894");
+
+    Map<String,Set<String>> expected = Maps.newHashMap();
+    expected.put("42", Sets.newHashSet("somewhere", "footer", "footer-KeyA"));
+    expected.put("176", Sets.newHashSet("somewhere", "footer", "footer-KeyC"));
+    expected.put("256", Sets.newHashSet("somewhere", "footer", "footer-KeyZ"));
+    expected.put("4711", Sets.newHashSet("somewhere", "footer"));
+    assertEquals("Extracted issues do not match", expected, actual);
+
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitDifferentParts() {
@@ -347,13 +395,15 @@
     expected.put("16", Sets.newHashSet("somewhere", "body"));
     expected.put("42", Sets.newHashSet("somewhere", "subject"));
     expected.put("176", Sets.newHashSet("somewhere", "body"));
-    expected.put("4711", Sets.newHashSet("somewhere", "footer"));
+    expected.put("4711", Sets.newHashSet("somewhere", "footer", "footer-Bug"));
     assertEquals("Extracted issues do not match", expected, actual);
 
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitDifferentPartsEmptySubject() {
@@ -378,13 +428,15 @@
     Map<String,Set<String>> expected = Maps.newHashMap();
     expected.put("16", Sets.newHashSet("somewhere", "body"));
     expected.put("176", Sets.newHashSet("somewhere", "body"));
-    expected.put("4711", Sets.newHashSet("somewhere", "footer"));
+    expected.put("4711", Sets.newHashSet("somewhere", "footer", "footer-Bug"));
     assertEquals("Extracted issues do not match", expected, actual);
 
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitDifferentPartsLinePastFooter() {
@@ -410,13 +462,15 @@
     expected.put("16", Sets.newHashSet("somewhere", "body"));
     expected.put("42", Sets.newHashSet("somewhere", "subject"));
     expected.put("176", Sets.newHashSet("somewhere", "body"));
-    expected.put("4711", Sets.newHashSet("somewhere", "footer"));
+    expected.put("4711", Sets.newHashSet("somewhere", "footer", "footer-Bug"));
     assertEquals("Extracted issues do not match", expected, actual);
 
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitDifferentPartsLinesPastFooter() {
@@ -443,13 +497,15 @@
     expected.put("16", Sets.newHashSet("somewhere", "body"));
     expected.put("42", Sets.newHashSet("somewhere", "subject"));
     expected.put("176", Sets.newHashSet("somewhere", "body"));
-    expected.put("4711", Sets.newHashSet("somewhere", "footer"));
+    expected.put("4711", Sets.newHashSet("somewhere", "footer", "footer-Bug"));
     assertEquals("Extracted issues do not match", expected, actual);
 
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitDifferentPartsNoFooter() {
@@ -636,17 +692,20 @@
     Map<String,Set<String>> expected = Maps.newHashMap();
     expected.put("16", Sets.newHashSet("somewhere", "subject", "body"));
     expected.put("42", Sets.newHashSet("somewhere", "subject"));
-    expected.put("176", Sets.newHashSet("somewhere", "footer"));
+    expected.put("176", Sets.newHashSet("somewhere", "footer", "footer-Bug"));
     expected.put("1984", Sets.newHashSet("somewhere", "subject", "body",
-        "footer"));
+        "footer", "footer-Bug"));
     expected.put("4711", Sets.newHashSet("somewhere", "body"));
-    expected.put("5150", Sets.newHashSet("somewhere", "body", "footer"));
+    expected.put("5150", Sets.newHashSet("somewhere", "body", "footer",
+        "footer-Bug"));
     assertEquals("Extracted issues do not match", expected, actual);
 
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitWAddedEmptyFirst() {
@@ -703,6 +762,7 @@
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitWAddedSingleSubjectIssueSecondEmpty()
@@ -763,6 +823,8 @@
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitWAddedSingleSubjectIssueSecondSame()
@@ -822,6 +884,8 @@
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitWAddedSingleSubjectIssueSecondBody()
@@ -882,6 +946,8 @@
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitWAddedSingleSubjectIssueSecondFooter()
@@ -931,7 +997,8 @@
         "1234567891123456789212345678931234567894", currentPatchSetId);
 
     Map<String,Set<String>> expected = Maps.newHashMap();
-    expected.put("42", Sets.newHashSet("somewhere", "footer", "added@footer"));
+    expected.put("42", Sets.newHashSet("somewhere", "footer", "added@footer",
+        "footer-Bug", "added@footer-Bug"));
     assertEquals("Extracted issues do not match", expected, actual);
 
     assertLogMessageContains("Matching");
@@ -942,6 +1009,9 @@
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitWAddedSubjectFooter()
@@ -995,7 +1065,8 @@
 
     Map<String,Set<String>> expected = Maps.newHashMap();
     expected.put("42", Sets.newHashSet("somewhere", "subject", "added@subject",
-        "body", "footer", "added@footer"));
+        "body", "footer", "added@footer", "footer-Bug",
+        "added@footer-Bug"));
     assertEquals("Extracted issues do not match", expected, actual);
 
     assertLogMessageContains("Matching");
@@ -1006,6 +1077,9 @@
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   public void testIssueIdsCommitWAddedMultiple()
@@ -1061,7 +1135,8 @@
     Map<String,Set<String>> expected = Maps.newHashMap();
     expected.put("16", Sets.newHashSet("somewhere", "body", "added@body"));
     expected.put("42", Sets.newHashSet("somewhere", "subject", "added@subject",
-        "body", "footer", "added@footer"));
+        "body", "footer", "added@footer", "footer-Bug",
+        "added@footer-Bug"));
     assertEquals("Extracted issues do not match", expected, actual);
 
     assertLogMessageContains("Matching");
@@ -1072,6 +1147,10 @@
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
     assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
+    assertLogMessageContains("Matching");
   }
 
   @Override