summaryrefslogtreecommitdiffstats
path: root/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
diff options
context:
space:
mode:
Diffstat (limited to 'javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java')
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java382
1 files changed, 382 insertions, 0 deletions
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
new file mode 100644
index 0000000000..e9ac07a481
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
@@ -0,0 +1,382 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.extensions.client.ChangeStatus;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.restapi.change.Submit;
+import com.google.gerrit.server.submit.ChangeSet;
+import com.google.gerrit.server.submit.MergeSuperSet;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+@NoHttpd
+public class SubmitResolvingMergeCommitIT extends AbstractDaemonTest {
+ @Inject private Provider<MergeSuperSet> mergeSuperSet;
+
+ @Inject private Submit submit;
+
+ @ConfigSuite.Default
+ public static Config submitWholeTopicEnabled() {
+ return submitWholeTopicEnabledConfig();
+ }
+
+ @Test
+ public void resolvingMergeCommitAtEndOfChain() throws Exception {
+ /*
+ A <- B <- C <------- D
+ ^ ^
+ | |
+ E <- F <- G <- H <-- M*
+
+ G has a conflict with C and is resolved in M which is a merge
+ commit of H and D.
+ */
+
+ PushOneCommit.Result a = createChange("A");
+ PushOneCommit.Result b =
+ createChange("B", "new.txt", "No conflict line", ImmutableList.of(a.getCommit()));
+ PushOneCommit.Result c = createChange("C", ImmutableList.of(b.getCommit()));
+ PushOneCommit.Result d = createChange("D", ImmutableList.of(c.getCommit()));
+
+ PushOneCommit.Result e = createChange("E", ImmutableList.of(a.getCommit()));
+ PushOneCommit.Result f = createChange("F", ImmutableList.of(e.getCommit()));
+ PushOneCommit.Result g =
+ createChange("G", "new.txt", "Conflicting line", ImmutableList.of(f.getCommit()));
+ PushOneCommit.Result h = createChange("H", ImmutableList.of(g.getCommit()));
+
+ approve(a.getChangeId());
+ approve(b.getChangeId());
+ approve(c.getChangeId());
+ approve(d.getChangeId());
+ submit(d.getChangeId());
+
+ approve(e.getChangeId());
+ approve(f.getChangeId());
+ approve(g.getChangeId());
+ approve(h.getChangeId());
+
+ assertMergeable(e.getChange());
+ assertMergeable(f.getChange());
+ assertNotMergeable(g.getChange());
+ assertNotMergeable(h.getChange());
+
+ PushOneCommit.Result m =
+ createChange(
+ "M", "new.txt", "Resolved conflict", ImmutableList.of(d.getCommit(), h.getCommit()));
+ approve(m.getChangeId());
+
+ assertChangeSetMergeable(m.getChange(), true);
+
+ assertMergeable(m.getChange());
+ submit(m.getChangeId());
+
+ assertMerged(e.getChangeId());
+ assertMerged(f.getChangeId());
+ assertMerged(g.getChangeId());
+ assertMerged(h.getChangeId());
+ assertMerged(m.getChangeId());
+ }
+
+ @Test
+ public void resolvingMergeCommitComingBeforeConflict() throws Exception {
+ /*
+ A <- B <- C <- D
+ ^ ^
+ | |
+ E <- F* <- G
+
+ F is a merge commit of E and B and resolves any conflict.
+ However G is conflicting with C.
+ */
+
+ PushOneCommit.Result a = createChange("A");
+ PushOneCommit.Result b =
+ createChange("B", "new.txt", "No conflict line", ImmutableList.of(a.getCommit()));
+ PushOneCommit.Result c =
+ createChange("C", "new.txt", "No conflict line #2", ImmutableList.of(b.getCommit()));
+ PushOneCommit.Result d = createChange("D", ImmutableList.of(c.getCommit()));
+ PushOneCommit.Result e =
+ createChange("E", "new.txt", "Conflicting line", ImmutableList.of(a.getCommit()));
+ PushOneCommit.Result f =
+ createChange(
+ "F", "new.txt", "Resolved conflict", ImmutableList.of(b.getCommit(), e.getCommit()));
+ PushOneCommit.Result g =
+ createChange("G", "new.txt", "Conflicting line #2", ImmutableList.of(f.getCommit()));
+
+ assertMergeable(e.getChange());
+
+ approve(a.getChangeId());
+ approve(b.getChangeId());
+ submit(b.getChangeId());
+
+ assertNotMergeable(e.getChange());
+ assertMergeable(f.getChange());
+ assertMergeable(g.getChange());
+
+ approve(c.getChangeId());
+ approve(d.getChangeId());
+ submit(d.getChangeId());
+
+ approve(e.getChangeId());
+ approve(f.getChangeId());
+ approve(g.getChangeId());
+
+ assertNotMergeable(g.getChange());
+ assertChangeSetMergeable(g.getChange(), false);
+ }
+
+ @Test
+ public void resolvingMergeCommitWithTopics() throws Exception {
+ /*
+ Project1:
+ A <- B <-- C <---
+ ^ ^ |
+ | | |
+ E <- F* <- G <- L*
+
+ G clashes with C, and F resolves the clashes between E and B.
+ Later, L resolves the clashes between C and G.
+
+ Project2:
+ H <- I
+ ^ ^
+ | |
+ J <- K*
+
+ J clashes with I, and K resolves all problems.
+ G, K and L are in the same topic.
+ */
+ assume().that(isSubmitWholeTopicEnabled()).isTrue();
+
+ String project1Name = name("Project1");
+ String project2Name = name("Project2");
+ gApi.projects().create(project1Name);
+ gApi.projects().create(project2Name);
+ TestRepository<InMemoryRepository> project1 = cloneProject(new Project.NameKey(project1Name));
+ TestRepository<InMemoryRepository> project2 = cloneProject(new Project.NameKey(project2Name));
+
+ PushOneCommit.Result a = createChange(project1, "A");
+ PushOneCommit.Result b =
+ createChange(project1, "B", "new.txt", "No conflict line", ImmutableList.of(a.getCommit()));
+ PushOneCommit.Result c =
+ createChange(
+ project1, "C", "new.txt", "No conflict line #2", ImmutableList.of(b.getCommit()));
+
+ approve(a.getChangeId());
+ approve(b.getChangeId());
+ approve(c.getChangeId());
+ submit(c.getChangeId());
+
+ PushOneCommit.Result e =
+ createChange(project1, "E", "new.txt", "Conflicting line", ImmutableList.of(a.getCommit()));
+ PushOneCommit.Result f =
+ createChange(
+ project1,
+ "F",
+ "new.txt",
+ "Resolved conflict",
+ ImmutableList.of(b.getCommit(), e.getCommit()));
+ PushOneCommit.Result g =
+ createChange(
+ project1,
+ "G",
+ "new.txt",
+ "Conflicting line #2",
+ ImmutableList.of(f.getCommit()),
+ "refs/for/master/" + name("topic1"));
+
+ PushOneCommit.Result h = createChange(project2, "H");
+ PushOneCommit.Result i =
+ createChange(project2, "I", "new.txt", "No conflict line", ImmutableList.of(h.getCommit()));
+ PushOneCommit.Result j =
+ createChange(project2, "J", "new.txt", "Conflicting line", ImmutableList.of(h.getCommit()));
+ PushOneCommit.Result k =
+ createChange(
+ project2,
+ "K",
+ "new.txt",
+ "Sadly conflicting topic-wise",
+ ImmutableList.of(i.getCommit(), j.getCommit()),
+ "refs/for/master/" + name("topic1"));
+
+ approve(h.getChangeId());
+ approve(i.getChangeId());
+ submit(i.getChangeId());
+
+ approve(e.getChangeId());
+ approve(f.getChangeId());
+ approve(g.getChangeId());
+ approve(j.getChangeId());
+ approve(k.getChangeId());
+
+ assertChangeSetMergeable(g.getChange(), false);
+ assertChangeSetMergeable(k.getChange(), false);
+
+ PushOneCommit.Result l =
+ createChange(
+ project1,
+ "L",
+ "new.txt",
+ "Resolving conflicts again",
+ ImmutableList.of(c.getCommit(), g.getCommit()),
+ "refs/for/master/" + name("topic1"));
+
+ approve(l.getChangeId());
+ assertChangeSetMergeable(l.getChange(), true);
+
+ submit(l.getChangeId());
+ assertMerged(c.getChangeId());
+ assertMerged(g.getChangeId());
+ assertMerged(k.getChangeId());
+ }
+
+ @Test
+ public void resolvingMergeCommitAtEndOfChainAndNotUpToDate() throws Exception {
+ /*
+ A <-- B
+ \
+ C <- D
+ \ /
+ E
+
+ B is the target branch, and D should be merged with B, but one
+ of C conflicts with B
+ */
+
+ PushOneCommit.Result a = createChange("A");
+ PushOneCommit.Result b =
+ createChange("B", "new.txt", "No conflict line", ImmutableList.of(a.getCommit()));
+
+ approve(a.getChangeId());
+ approve(b.getChangeId());
+ submit(b.getChangeId());
+
+ PushOneCommit.Result c =
+ createChange("C", "new.txt", "Create conflicts", ImmutableList.of(a.getCommit()));
+ PushOneCommit.Result e = createChange("E", ImmutableList.of(c.getCommit()));
+ PushOneCommit.Result d =
+ createChange(
+ "D", "new.txt", "Resolves conflicts", ImmutableList.of(c.getCommit(), e.getCommit()));
+
+ approve(c.getChangeId());
+ approve(e.getChangeId());
+ approve(d.getChangeId());
+ assertNotMergeable(d.getChange());
+ assertChangeSetMergeable(d.getChange(), false);
+ }
+
+ private void submit(String changeId) throws Exception {
+ gApi.changes().id(changeId).current().submit();
+ }
+
+ private void assertChangeSetMergeable(ChangeData change, boolean expected)
+ throws MissingObjectException, IncorrectObjectTypeException, IOException, OrmException,
+ PermissionBackendException {
+ ChangeSet cs = mergeSuperSet.get().completeChangeSet(db, change.change(), user(admin));
+ assertThat(submit.unmergeableChanges(cs).isEmpty()).isEqualTo(expected);
+ }
+
+ private void assertMergeable(ChangeData change) throws Exception {
+ change.setMergeable(null);
+ assertThat(change.isMergeable()).isTrue();
+ }
+
+ private void assertNotMergeable(ChangeData change) throws Exception {
+ change.setMergeable(null);
+ assertThat(change.isMergeable()).isFalse();
+ }
+
+ private void assertMerged(String changeId) throws Exception {
+ assertThat(gApi.changes().id(changeId).get().status).isEqualTo(ChangeStatus.MERGED);
+ }
+
+ private PushOneCommit.Result createChange(
+ TestRepository<?> repo,
+ String subject,
+ String fileName,
+ String content,
+ List<RevCommit> parents,
+ String ref)
+ throws Exception {
+ PushOneCommit push = pushFactory.create(db, admin.getIdent(), repo, subject, fileName, content);
+
+ if (!parents.isEmpty()) {
+ push.setParents(parents);
+ }
+
+ PushOneCommit.Result result;
+ if (fileName.isEmpty()) {
+ result = push.execute(ref);
+ } else {
+ result = push.to(ref);
+ }
+ result.assertOkStatus();
+ return result;
+ }
+
+ private PushOneCommit.Result createChange(TestRepository<?> repo, String subject)
+ throws Exception {
+ return createChange(repo, subject, "x", "x", new ArrayList<RevCommit>(), "refs/for/master");
+ }
+
+ private PushOneCommit.Result createChange(
+ TestRepository<?> repo,
+ String subject,
+ String fileName,
+ String content,
+ List<RevCommit> parents)
+ throws Exception {
+ return createChange(repo, subject, fileName, content, parents, "refs/for/master");
+ }
+
+ @Override
+ protected PushOneCommit.Result createChange(String subject) throws Exception {
+ return createChange(
+ testRepo, subject, "", "", Collections.<RevCommit>emptyList(), "refs/for/master");
+ }
+
+ private PushOneCommit.Result createChange(String subject, List<RevCommit> parents)
+ throws Exception {
+ return createChange(testRepo, subject, "", "", parents, "refs/for/master");
+ }
+
+ private PushOneCommit.Result createChange(
+ String subject, String fileName, String content, List<RevCommit> parents) throws Exception {
+ return createChange(testRepo, subject, fileName, content, parents, "refs/for/master");
+ }
+}