Propagate the highest vote for each label.
This will allow us to not wipe out +2s with +1s in the case of a context
user being set.
Change-Id: I314facf0a1154897dc6f1f75dde76562226d8654
diff --git a/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java b/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java
index 7cc7074..f56d464 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreator.java
@@ -28,6 +28,7 @@
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.common.MergeInput;
import com.google.gerrit.extensions.common.MergePatchSetInput;
import com.google.gerrit.extensions.common.RevisionInfo;
@@ -47,10 +48,12 @@
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.OptionalInt;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
@@ -200,7 +203,11 @@
return;
}
- Map<String, ApprovalInfo> approvals = event.getApprovals();
+ Map<String, LabelInfo> labels =
+ gApi.changes()
+ .id(change._number)
+ .get(EnumSet.of(ListChangesOption.DETAILED_LABELS))
+ .labels;
for (String downstreamBranch : downstreamBranches) {
try {
@@ -209,8 +216,21 @@
for (Integer changeNumber : existingDownstream) {
ChangeInfo downstreamChange =
gApi.changes().id(changeNumber).get(EnumSet.of(ListChangesOption.CURRENT_REVISION));
- for (Map.Entry<String, ApprovalInfo> label : approvals.entrySet()) {
- updateVote(downstreamChange, label.getKey(), label.getValue().value.shortValue());
+ for (Map.Entry<String, LabelInfo> labelEntry : labels.entrySet()) {
+ if (labelEntry.getValue().all.size() > 0) {
+ OptionalInt maxVote =
+ labelEntry
+ .getValue()
+ .all
+ .stream()
+ .filter(o -> o.value != null)
+ .mapToInt(i -> i.value)
+ .max();
+
+ if (maxVote.isPresent()) {
+ updateVote(downstreamChange, labelEntry.getKey(), (short) maxVote.getAsInt());
+ }
+ }
}
}
} catch (RestApiException | InvalidQueryParameterException e) {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java b/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java
index f9dca8a..552683d 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/automerger/DownstreamCreatorIT.java
@@ -24,6 +24,7 @@
import com.google.gerrit.acceptance.TestPlugin;
import com.google.gerrit.extensions.api.accounts.AccountApi;
import com.google.gerrit.extensions.api.changes.ChangeApi;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -770,6 +771,7 @@
@Test
public void testContextUser() throws Exception {
+ // Branch flow for contextUser is master -> ds_one -> ds_two
Project.NameKey manifestNameKey = defaultSetup();
// Create initial change
PushOneCommit.Result initialResult = createChange("subject", "filename", "echo Hello");
@@ -834,6 +836,82 @@
approve(dsOneChangeInfo.id);
}
+ @Test
+ public void testContextUser_downstreamHighestVote() throws Exception {
+ // Branch flow for contextUser is master -> ds_one -> ds_two
+ Project.NameKey manifestNameKey = defaultSetup();
+ // Create initial change
+ PushOneCommit.Result initialResult = createChange("subject", "filename", "echo Hello");
+ // Project name is scoped by test, so we need to get it from our initial change
+ Project.NameKey projectNameKey = initialResult.getChange().project();
+ String projectName = projectNameKey.get();
+ createBranch(new Branch.NameKey(projectName, "ds_one"));
+ createBranch(new Branch.NameKey(projectName, "ds_two"));
+ initialResult.assertOkStatus();
+ merge(initialResult);
+
+ // Create normalUserGroup, containing current user, and contextUserGroup, containing contextUser
+ String normalUserGroup = createGroup("normalUserGroup");
+ gApi.groups().id(normalUserGroup).addMembers(user.get().getAccountId().toString());
+ AccountApi contextUserApi = gApi.accounts().create("randomContextUser");
+ String contextUserGroup = createGroup("contextUserGroup");
+ gApi.groups().id(contextUserGroup).addMembers(contextUserApi.get().name);
+
+ // Grant +2 to context user, since it doesn't have it by default
+ grantLabel(
+ "Code-Review",
+ -2,
+ 2,
+ projectNameKey,
+ "refs/heads/*",
+ false,
+ AccountGroup.UUID.parse(gApi.groups().id(contextUserGroup).get().id),
+ false);
+ pushContextUserConfig(manifestNameKey.get(), projectName, contextUserApi.get()._accountId);
+
+ // After we upload our config, we upload a new patchset to create the downstreams
+ PushOneCommit.Result result = createChange("subject", "filename2", "echo Hello", "sometopic");
+ result.assertOkStatus();
+ // Check that there are the correct number of changes in the topic
+ List<ChangeInfo> changesInTopic =
+ gApi.changes()
+ .query("topic: " + gApi.changes().id(result.getChangeId()).topic())
+ .withOptions(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT)
+ .get();
+ assertThat(changesInTopic).hasSize(3);
+
+ List<ChangeInfo> sortedChanges = sortedChanges(changesInTopic);
+
+ // Check that downstream is at Code-Review 0
+ ChangeInfo dsOneChangeInfo = sortedChanges.get(0);
+ assertThat(dsOneChangeInfo.branch).isEqualTo("ds_one");
+ ChangeApi dsOneChange = gApi.changes().id(dsOneChangeInfo._number);
+ assertThat(getVote(dsOneChange, "Code-Review").value).isEqualTo(0);
+
+ // Try to +1 master and see it succeed to +1 master and ds_one
+ ChangeInfo masterChangeInfo = sortedChanges.get(2);
+ assertThat(masterChangeInfo.branch).isEqualTo("master");
+ ChangeApi masterChange = gApi.changes().id(masterChangeInfo._number);
+ assertThat(getVote(masterChange, "Code-Review").value).isEqualTo(0);
+ recommend(masterChangeInfo.id);
+ assertThat(getVote(masterChange, "Code-Review").value).isEqualTo(1);
+ assertThat(getVote(dsOneChange, "Code-Review").value).isEqualTo(1);
+
+ ChangeInfo dsTwoChangeInfo = sortedChanges.get(1);
+ assertThat(dsTwoChangeInfo.branch).isEqualTo("ds_two");
+ ChangeApi dsTwoChange = gApi.changes().id(dsTwoChangeInfo._number);
+ assertThat(getVote(dsTwoChange, "Code-Review").value).isEqualTo(1);
+
+ // +2 ds_one and see that it overrides the +1 of the contextUser
+ approve(dsOneChangeInfo.id);
+ assertThat(getVote(dsOneChange, "Code-Review").value).isEqualTo(2);
+ assertThat(getVote(dsTwoChange, "Code-Review").value).isEqualTo(2);
+ // +0 ds_one and see that it goes back to the +1 of the contextUser
+ gApi.changes().id(dsOneChangeInfo.id).revision("current").review(ReviewInput.noScore());
+ assertThat(getVote(dsOneChange, "Code-Review").value).isEqualTo(1);
+ assertThat(getVote(dsTwoChange, "Code-Review").value).isEqualTo(1);
+ }
+
private Project.NameKey defaultSetup() throws Exception {
Project.NameKey manifestNameKey = createProject("platform/manifest");
setupTestRepo("default.xml", manifestNameKey, "master", "default.xml");
@@ -919,7 +997,7 @@
cfg.setString("global", null, "manifestProject", manifestName);
cfg.setInt("global", null, "contextUserId", contextUserId);
cfg.setString("automerger", "master:ds_one", "setProjects", project);
- cfg.setString("automerger", "master:ds_two", "setProjects", project);
+ cfg.setString("automerger", "ds_one:ds_two", "setProjects", project);
PushOneCommit push =
pushFactory.create(
db, admin.getIdent(), allProjectRepo, "Subject", "automerger.config", cfg.toText());
@@ -928,8 +1006,17 @@
}
private ApprovalInfo getVote(ChangeApi change, String label) throws RestApiException {
- List<ApprovalInfo> approvals = change.get(EnumSet.of(ListChangesOption.DETAILED_LABELS)).labels.get(label).all;
- return approvals.get(approvals.size() - 1);
+ List<ApprovalInfo> approvals =
+ change.get(EnumSet.of(ListChangesOption.DETAILED_LABELS)).labels.get(label).all;
+ ApprovalInfo maxApproval = null;
+ for (ApprovalInfo approval : approvals) {
+ if (maxApproval == null) {
+ maxApproval = approval;
+ } else if (approval.value != null && approval.value > maxApproval.value) {
+ maxApproval = approval;
+ }
+ }
+ return maxApproval;
}
private List<ChangeInfo> sortedChanges(List<ChangeInfo> changes) {
diff --git a/src/test/resources/com/googlesource/gerrit/plugins/automerger/context_user.config b/src/test/resources/com/googlesource/gerrit/plugins/automerger/context_user.config
index 77f79b9..bb2d262 100644
--- a/src/test/resources/com/googlesource/gerrit/plugins/automerger/context_user.config
+++ b/src/test/resources/com/googlesource/gerrit/plugins/automerger/context_user.config
@@ -1,14 +1,12 @@
[automerger "master:ds_one"]
addProjects = platform/added/project
ignoreProjects = whoo
-[automerger "master:ds_two"]
+[automerger "ds_one:ds_two"]
mergeAll = true
addProjects = platform/added/project
ignoreProjects = whoo
setProjects = platform/some/project
setProjects = platform/other/project
-[automerger "ds_two:ds_three"]
- setProjects = platform/some/project
[global]
alwaysBlankMerge = .*Import translations. DO NOT MERGE.*
alwaysBlankMerge = .*DO NOT MERGE ANYWHERE.*