diff options
Diffstat (limited to 'javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java')
-rw-r--r-- | javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java | 382 |
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"); + } +} |