blob: 1038a147ba27d08cccd1e945854d5d073cf2f306 [file] [log] [blame]
// Copyright (C) 2021 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.testsuite.change;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.eclipse.jgit.lib.Constants.HEAD;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.entities.LabelId;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.RevisionApi;
import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.inject.Inject;
import java.util.List;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevCommit;
/** Helper to create changes of a certain {@link ChangeKind}. */
public class ChangeKindCreator {
private GerritApi gApi;
private PushOneCommit.Factory pushFactory;
private RequestScopeOperations requestScopeOperations;
private ProjectOperations projectOperations;
@Inject
private ChangeKindCreator(
GerritApi gApi,
PushOneCommit.Factory pushFactory,
RequestScopeOperations requestScopeOperations,
ProjectOperations projectOperations) {
this.gApi = gApi;
this.pushFactory = pushFactory;
this.requestScopeOperations = requestScopeOperations;
this.projectOperations = projectOperations;
}
/** Creates a change with the given {@link ChangeKind} and returns the change id. */
public String createChange(
ChangeKind kind, TestRepository<InMemoryRepository> testRepo, TestAccount user)
throws Exception {
switch (kind) {
case NO_CODE_CHANGE:
case REWORK:
case TRIVIAL_REBASE:
case NO_CHANGE:
return createChange(testRepo, user).getChangeId();
case MERGE_FIRST_PARENT_UPDATE:
return createChangeForMergeCommit(testRepo, user);
default:
throw new IllegalStateException("unexpected change kind: " + kind);
}
}
/** Updates a change with the given {@link ChangeKind}. */
public void updateChange(
String changeId,
ChangeKind changeKind,
TestRepository<InMemoryRepository> testRepo,
TestAccount user,
Project.NameKey project)
throws Exception {
switch (changeKind) {
case NO_CODE_CHANGE:
noCodeChange(changeId, testRepo, user);
return;
case REWORK:
rework(changeId, testRepo, user);
return;
case TRIVIAL_REBASE:
trivialRebase(changeId, testRepo, user, project);
return;
case MERGE_FIRST_PARENT_UPDATE:
updateFirstParent(changeId, testRepo, user);
return;
case NO_CHANGE:
noChange(changeId, testRepo, user);
return;
default:
assertWithMessage("unexpected change kind: " + changeKind).fail();
}
}
/**
* Creates a cherry pick of the provided change with the given {@link ChangeKind} and returns the
* change id.
*/
public String cherryPick(
String changeId,
ChangeKind changeKind,
TestRepository<InMemoryRepository> testRepo,
TestAccount user,
Project.NameKey project)
throws Exception {
switch (changeKind) {
case REWORK:
case TRIVIAL_REBASE:
break;
case NO_CODE_CHANGE:
case NO_CHANGE:
case MERGE_FIRST_PARENT_UPDATE:
default:
assertWithMessage("unexpected change kind: " + changeKind).fail();
}
testRepo.reset(projectOperations.project(project).getHead("master"));
PushOneCommit.Result r =
pushFactory
.create(
user.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
"other.txt",
"new content " + System.nanoTime())
.to("refs/for/master");
r.assertOkStatus();
vote(user, r.getChangeId(), 2, 1);
merge(r);
String subject =
ChangeKind.TRIVIAL_REBASE.equals(changeKind)
? PushOneCommit.SUBJECT
: "Reworked change " + System.nanoTime();
CherryPickInput in = new CherryPickInput();
in.destination = "master";
in.message = String.format("%s\n\nChange-Id: %s", subject, changeId);
ChangeInfo c = gApi.changes().id(changeId).current().cherryPick(in).get();
return c.changeId;
}
/** Creates a change that is a merge {@link ChangeKind} and returns the change id. */
public String createChangeForMergeCommit(
TestRepository<InMemoryRepository> testRepo, TestAccount user) throws Exception {
ObjectId initial = testRepo.getRepository().exactRef(HEAD).getLeaf().getObjectId();
PushOneCommit.Result parent1 = createChange("parent 1", "p1.txt", "content 1", testRepo, user);
testRepo.reset(initial);
PushOneCommit.Result parent2 = createChange("parent 2", "p2.txt", "content 2", testRepo, user);
testRepo.reset(parent1.getCommit());
PushOneCommit merge = pushFactory.create(user.newIdent(), testRepo);
merge.setParents(ImmutableList.of(parent1.getCommit(), parent2.getCommit()));
PushOneCommit.Result result = merge.to("refs/for/master");
result.assertOkStatus();
return result.getChangeId();
}
/** Update the first parent of a merge. */
public void updateFirstParent(
String changeId, TestRepository<InMemoryRepository> testRepo, TestAccount user)
throws Exception {
ChangeInfo c = detailedChange(changeId);
List<CommitInfo> parents = c.revisions.get(c.currentRevision).commit.parents;
String parent1 = parents.get(0).commit;
String parent2 = parents.get(1).commit;
RevCommit commitParent2 = testRepo.getRevWalk().parseCommit(ObjectId.fromString(parent2));
testRepo.reset(parent1);
PushOneCommit.Result newParent1 =
createChange("new parent 1", "p1-1.txt", "content 1-1", testRepo, user);
PushOneCommit merge = pushFactory.create(user.newIdent(), testRepo, changeId);
merge.setParents(ImmutableList.of(newParent1.getCommit(), commitParent2));
PushOneCommit.Result result = merge.to("refs/for/master");
result.assertOkStatus();
assertThat(getChangeKind(changeId)).isEqualTo(ChangeKind.MERGE_FIRST_PARENT_UPDATE);
}
/** Update the second parent of a merge. */
public void updateSecondParent(
String changeId, TestRepository<InMemoryRepository> testRepo, TestAccount user)
throws Exception {
ChangeInfo c = detailedChange(changeId);
List<CommitInfo> parents = c.revisions.get(c.currentRevision).commit.parents;
String parent1 = parents.get(0).commit;
String parent2 = parents.get(1).commit;
RevCommit commitParent1 = testRepo.getRevWalk().parseCommit(ObjectId.fromString(parent1));
testRepo.reset(parent2);
PushOneCommit.Result newParent2 =
createChange("new parent 2", "p2-2.txt", "content 2-2", testRepo, user);
PushOneCommit merge = pushFactory.create(user.newIdent(), testRepo, changeId);
merge.setParents(ImmutableList.of(commitParent1, newParent2.getCommit()));
PushOneCommit.Result result = merge.to("refs/for/master");
result.assertOkStatus();
assertThat(getChangeKind(changeId)).isEqualTo(ChangeKind.REWORK);
}
private void noCodeChange(
String changeId, TestRepository<InMemoryRepository> testRepo, TestAccount user)
throws Exception {
TestRepository<?>.CommitBuilder commitBuilder =
testRepo.amendRef("HEAD").insertChangeId(changeId.substring(1));
commitBuilder
.message("New subject " + System.nanoTime())
.author(user.newIdent())
.committer(new PersonIdent(user.newIdent(), testRepo.getDate()));
commitBuilder.create();
GitUtil.pushHead(testRepo, "refs/for/master", false);
assertThat(getChangeKind(changeId)).isEqualTo(ChangeKind.NO_CODE_CHANGE);
}
private void noChange(
String changeId, TestRepository<InMemoryRepository> testRepo, TestAccount user)
throws Exception {
ChangeInfo change = gApi.changes().id(changeId).get();
String commitMessage = change.revisions.get(change.currentRevision).commit.message;
TestRepository<?>.CommitBuilder commitBuilder =
testRepo.amendRef("HEAD").insertChangeId(changeId.substring(1));
commitBuilder
.message(commitMessage)
.author(user.newIdent())
.committer(new PersonIdent(user.newIdent(), testRepo.getDate()));
commitBuilder.create();
GitUtil.pushHead(testRepo, "refs/for/master", false);
assertThat(getChangeKind(changeId)).isEqualTo(ChangeKind.NO_CHANGE);
}
private void rework(
String changeId, TestRepository<InMemoryRepository> testRepo, TestAccount user)
throws Exception {
PushOneCommit push =
pushFactory.create(
user.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
PushOneCommit.FILE_NAME,
"new content " + System.nanoTime(),
changeId);
push.to("refs/for/master").assertOkStatus();
assertThat(getChangeKind(changeId)).isEqualTo(ChangeKind.REWORK);
}
private void trivialRebase(
String changeId,
TestRepository<InMemoryRepository> testRepo,
TestAccount user,
Project.NameKey project)
throws Exception {
requestScopeOperations.setApiUser(user.id());
testRepo.reset(projectOperations.project(project).getHead("master"));
PushOneCommit push =
pushFactory.create(
user.newIdent(),
testRepo,
"Other Change",
"a" + System.nanoTime() + ".txt",
PushOneCommit.FILE_CONTENT);
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
ReviewInput in = new ReviewInput().label(LabelId.CODE_REVIEW, 2).label(LabelId.VERIFIED, 1);
revision.review(in);
revision.submit();
gApi.changes().id(changeId).current().rebase();
assertThat(getChangeKind(changeId)).isEqualTo(ChangeKind.TRIVIAL_REBASE);
}
private ChangeKind getChangeKind(String changeId) throws Exception {
ChangeInfo c = gApi.changes().id(changeId).get(ListChangesOption.CURRENT_REVISION);
return c.revisions.get(c.currentRevision).kind;
}
private PushOneCommit.Result createChange(
TestRepository<InMemoryRepository> testRepo, TestAccount user) throws Exception {
PushOneCommit push = pushFactory.create(user.newIdent(), testRepo);
PushOneCommit.Result result = push.to("refs/for/master");
result.assertOkStatus();
return result;
}
private ChangeInfo detailedChange(String changeId) throws Exception {
return gApi.changes()
.id(changeId)
.get(
ListChangesOption.DETAILED_LABELS,
ListChangesOption.CURRENT_REVISION,
ListChangesOption.CURRENT_COMMIT);
}
private PushOneCommit.Result createChange(
String subject,
String fileName,
String content,
TestRepository<InMemoryRepository> testRepo,
TestAccount user)
throws Exception {
PushOneCommit push = pushFactory.create(user.newIdent(), testRepo, subject, fileName, content);
return push.to("refs/for/master");
}
private void vote(TestAccount user, String changeId, int codeReviewVote, int verifiedVote)
throws Exception {
requestScopeOperations.setApiUser(user.id());
ReviewInput in =
new ReviewInput()
.label(LabelId.CODE_REVIEW, codeReviewVote)
.label(LabelId.VERIFIED, verifiedVote);
gApi.changes().id(changeId).current().review(in);
}
private void merge(PushOneCommit.Result r) throws Exception {
gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
gApi.changes().id(r.getChangeId()).current().submit();
}
}