Java tutorial
// Copyright (C) 2013 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.api.revision; import static com.google.common.truth.Truth.assertThat; import static com.google.gerrit.acceptance.PushOneCommit.FILE_CONTENT; import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME; import static com.google.gerrit.acceptance.PushOneCommit.PATCH; import static org.eclipse.jgit.lib.Constants.HEAD; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.TestAccount; import com.google.gerrit.extensions.api.changes.ChangeApi; import com.google.gerrit.extensions.api.changes.CherryPickInput; import com.google.gerrit.extensions.api.changes.DraftApi; import com.google.gerrit.extensions.api.changes.DraftInput; import com.google.gerrit.extensions.api.changes.ReviewInput; import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput; import com.google.gerrit.extensions.api.changes.RevisionApi; import com.google.gerrit.extensions.api.changes.SubmitInput; import com.google.gerrit.extensions.api.projects.BranchInput; import com.google.gerrit.extensions.client.SubmitType; import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeMessageInfo; import com.google.gerrit.extensions.common.CommentInfo; import com.google.gerrit.extensions.common.DiffInfo; import com.google.gerrit.extensions.common.MergeableInfo; import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BinaryResult; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.reviewdb.client.Patch; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.RefUpdate; import org.junit.Before; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @NoHttpd public class RevisionIT extends AbstractDaemonTest { private TestAccount admin2; @Before public void setUp() throws Exception { admin2 = accounts.admin2(); } @Test public void reviewTriplet() throws Exception { PushOneCommit.Result r = createChange(); gApi.changes().id(project.get() + "~master~" + r.getChangeId()).revision(r.getCommit().name()) .review(ReviewInput.approve()); } @Test public void reviewCurrent() throws Exception { PushOneCommit.Result r = createChange(); gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve()); } @Test public void reviewNumber() throws Exception { PushOneCommit.Result r = createChange(); gApi.changes().id(r.getChangeId()).revision(1).review(ReviewInput.approve()); r = updateChange(r, "new content"); gApi.changes().id(r.getChangeId()).revision(2).review(ReviewInput.approve()); } @Test public void submit() throws Exception { PushOneCommit.Result r = createChange(); gApi.changes().id(project.get() + "~master~" + r.getChangeId()).current().review(ReviewInput.approve()); gApi.changes().id(project.get() + "~master~" + r.getChangeId()).current().submit(); } @Test(expected = AuthException.class) public void submitOnBehalfOf() throws Exception { PushOneCommit.Result r = createChange(); gApi.changes().id(project.get() + "~master~" + r.getChangeId()).current().review(ReviewInput.approve()); SubmitInput in = new SubmitInput(); in.onBehalfOf = admin2.email; gApi.changes().id(project.get() + "~master~" + r.getChangeId()).current().submit(in); } @Test public void deleteDraft() throws Exception { PushOneCommit.Result r = createDraft(); gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).delete(); } @Test public void cherryPick() throws Exception { PushOneCommit.Result r = pushTo("refs/for/master%topic=someTopic"); CherryPickInput in = new CherryPickInput(); in.destination = "foo"; in.message = "it goes to stable branch"; gApi.projects().name(project.get()).branch(in.destination).create(new BranchInput()); ChangeApi orig = gApi.changes().id(project.get() + "~master~" + r.getChangeId()); assertThat(orig.get().messages).hasSize(1); ChangeApi cherry = orig.revision(r.getCommit().name()).cherryPick(in); assertThat(orig.get().messages).hasSize(2); String cherryPickedRevision = cherry.get().currentRevision; String expectedMessage = String.format( "Patch Set 1: Cherry Picked\n\n" + "This patchset was cherry picked to branch %s as commit %s", in.destination, cherryPickedRevision); Iterator<ChangeMessageInfo> origIt = orig.get().messages.iterator(); origIt.next(); assertThat(origIt.next().message).isEqualTo(expectedMessage); assertThat(cherry.get().messages).hasSize(1); Iterator<ChangeMessageInfo> cherryIt = cherry.get().messages.iterator(); expectedMessage = "Patch Set 1: Cherry Picked from branch master."; assertThat(cherryIt.next().message).isEqualTo(expectedMessage); assertThat(cherry.get().subject).contains(in.message); assertThat(cherry.get().topic).isEqualTo("someTopic-foo"); cherry.current().review(ReviewInput.approve()); cherry.current().submit(); } @Test public void cherryPickwithNoTopic() throws Exception { PushOneCommit.Result r = pushTo("refs/for/master"); CherryPickInput in = new CherryPickInput(); in.destination = "foo"; in.message = "it goes to stable branch"; gApi.projects().name(project.get()).branch(in.destination).create(new BranchInput()); ChangeApi orig = gApi.changes().id(project.get() + "~master~" + r.getChangeId()); ChangeApi cherry = orig.revision(r.getCommit().name()).cherryPick(in); assertThat(cherry.get().topic).isNull(); cherry.current().review(ReviewInput.approve()); cherry.current().submit(); } @Test public void cherryPickToSameBranch() throws Exception { PushOneCommit.Result r = createChange(); CherryPickInput in = new CherryPickInput(); in.destination = "master"; in.message = "it generates a new patch set\n\nChange-Id: " + r.getChangeId(); ChangeInfo cherryInfo = gApi.changes().id(project.get() + "~master~" + r.getChangeId()) .revision(r.getCommit().name()).cherryPick(in).get(); assertThat(cherryInfo.messages).hasSize(2); Iterator<ChangeMessageInfo> cherryIt = cherryInfo.messages.iterator(); assertThat(cherryIt.next().message).isEqualTo("Uploaded patch set 1."); assertThat(cherryIt.next().message).isEqualTo("Uploaded patch set 2."); } @Test public void cherryPickToSameBranchWithRebase() throws Exception { // Push a new change, then merge it PushOneCommit.Result baseChange = createChange(); String triplet = project.get() + "~master~" + baseChange.getChangeId(); RevisionApi baseRevision = gApi.changes().id(triplet).current(); baseRevision.review(ReviewInput.approve()); baseRevision.submit(); // Push a new change (change 1) PushOneCommit.Result r1 = createChange(); // Push another new change (change 2) String subject = "Test change\n\n" + "Change-Id: Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, subject, "another_file.txt", "another content"); PushOneCommit.Result r2 = push.to("refs/for/master"); // Change 2's parent should be change 1 assertThat(r2.getCommit().getParents()[0].name()).isEqualTo(r1.getCommit().name()); // Cherry pick change 2 onto the same branch triplet = project.get() + "~master~" + r2.getChangeId(); ChangeApi orig = gApi.changes().id(triplet); CherryPickInput in = new CherryPickInput(); in.destination = "master"; in.message = subject; ChangeApi cherry = orig.revision(r2.getCommit().name()).cherryPick(in); ChangeInfo cherryInfo = cherry.get(); assertThat(cherryInfo.messages).hasSize(2); Iterator<ChangeMessageInfo> cherryIt = cherryInfo.messages.iterator(); assertThat(cherryIt.next().message).isEqualTo("Uploaded patch set 1."); assertThat(cherryIt.next().message).isEqualTo("Uploaded patch set 2."); // Parent of change 2 should now be the change that was merged, i.e. // change 2 is rebased onto the head of the master branch. String newParent = cherryInfo.revisions.get(cherryInfo.currentRevision).commit.parents.get(0).commit; assertThat(newParent).isEqualTo(baseChange.getCommit().name()); } @Test public void cherryPickIdenticalTree() throws Exception { PushOneCommit.Result r = createChange(); CherryPickInput in = new CherryPickInput(); in.destination = "foo"; in.message = "it goes to stable branch"; gApi.projects().name(project.get()).branch(in.destination).create(new BranchInput()); ChangeApi orig = gApi.changes().id(project.get() + "~master~" + r.getChangeId()); assertThat(orig.get().messages).hasSize(1); ChangeApi cherry = orig.revision(r.getCommit().name()).cherryPick(in); assertThat(orig.get().messages).hasSize(2); assertThat(cherry.get().subject).contains(in.message); cherry.current().review(ReviewInput.approve()); cherry.current().submit(); exception.expect(ResourceConflictException.class); exception.expectMessage("Cherry pick failed: identical tree"); orig.revision(r.getCommit().name()).cherryPick(in); } @Test public void cherryPickConflict() throws Exception { PushOneCommit.Result r = createChange(); CherryPickInput in = new CherryPickInput(); in.destination = "foo"; in.message = "it goes to stable branch"; gApi.projects().name(project.get()).branch(in.destination).create(new BranchInput()); PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, PushOneCommit.FILE_NAME, "another content"); push.to("refs/heads/foo"); String triplet = project.get() + "~master~" + r.getChangeId(); ChangeApi orig = gApi.changes().id(triplet); assertThat(orig.get().messages).hasSize(1); exception.expect(ResourceConflictException.class); exception.expectMessage("Cherry pick failed: merge conflict"); orig.revision(r.getCommit().name()).cherryPick(in); } @Test public void canRebase() throws Exception { PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); PushOneCommit.Result r1 = push.to("refs/for/master"); merge(r1); push = pushFactory.create(db, admin.getIdent(), testRepo); PushOneCommit.Result r2 = push.to("refs/for/master"); boolean canRebase = gApi.changes().id(r2.getChangeId()).revision(r2.getCommit().name()).canRebase(); assertThat(canRebase).isFalse(); merge(r2); testRepo.reset(r1.getCommit()); push = pushFactory.create(db, admin.getIdent(), testRepo); PushOneCommit.Result r3 = push.to("refs/for/master"); canRebase = gApi.changes().id(r3.getChangeId()).revision(r3.getCommit().name()).canRebase(); assertThat(canRebase).isTrue(); } @Test public void setUnsetReviewedFlag() throws Exception { PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); PushOneCommit.Result r = push.to("refs/for/master"); gApi.changes().id(r.getChangeId()).current().setReviewed(PushOneCommit.FILE_NAME, true); assertThat(Iterables.getOnlyElement(gApi.changes().id(r.getChangeId()).current().reviewed())) .isEqualTo(PushOneCommit.FILE_NAME); gApi.changes().id(r.getChangeId()).current().setReviewed(PushOneCommit.FILE_NAME, false); assertThat(gApi.changes().id(r.getChangeId()).current().reviewed()).isEmpty(); } @Test public void mergeable() throws Exception { ObjectId initial = repo().getRef(HEAD).getLeaf().getObjectId(); PushOneCommit push1 = pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, PushOneCommit.FILE_NAME, "push 1 content"); PushOneCommit.Result r1 = push1.to("refs/for/master"); assertMergeable(r1.getChangeId(), true); merge(r1); // Reset HEAD to initial so the new change is a merge conflict. RefUpdate ru = repo().updateRef(HEAD); ru.setNewObjectId(initial); assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED); PushOneCommit push2 = pushFactory.create(db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, PushOneCommit.FILE_NAME, "push 2 content"); PushOneCommit.Result r2 = push2.to("refs/for/master"); assertMergeable(r2.getChangeId(), false); // TODO(dborowitz): Test for other-branches. } @Test public void files() throws Exception { PushOneCommit.Result r = createChange(); assertThat(Iterables.all(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files().keySet(), new Predicate<String>() { @Override public boolean apply(String file) { return file.matches(FILE_NAME + '|' + Patch.COMMIT_MSG); } })).isTrue(); } @Test public void diff() throws Exception { PushOneCommit.Result r = createChange(); DiffInfo diff = gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).file(FILE_NAME).diff(); assertThat(diff.metaA).isNull(); assertThat(diff.metaB.lines).isEqualTo(1); } @Test public void content() throws Exception { PushOneCommit.Result r = createChange(); BinaryResult bin = gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).file(FILE_NAME) .content(); ByteArrayOutputStream os = new ByteArrayOutputStream(); bin.writeTo(os); String res = new String(os.toByteArray(), StandardCharsets.UTF_8); assertThat(res).isEqualTo(FILE_CONTENT); } private void assertMergeable(String id, boolean expected) throws Exception { MergeableInfo m = gApi.changes().id(id).current().mergeable(); assertThat(m.mergeable).isEqualTo(expected); assertThat(m.submitType).isEqualTo(SubmitType.MERGE_IF_NECESSARY); assertThat(m.mergeableInto).isNull(); ChangeInfo c = gApi.changes().id(id).info(); assertThat(c.mergeable).isEqualTo(expected); } @Test public void drafts() throws Exception { PushOneCommit.Result r = createChange(); DraftInput in = new DraftInput(); in.line = 1; in.message = "nit: trailing whitespace"; in.path = FILE_NAME; DraftApi draftApi = gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).createDraft(in); assertThat(draftApi.get().message).isEqualTo(in.message); assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).draft(draftApi.get().id) .get().message).isEqualTo(in.message); assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).drafts()).hasSize(1); in.message = "good catch!"; assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).draft(draftApi.get().id) .update(in).message).isEqualTo(in.message); assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).draft(draftApi.get().id) .get().author.email).isEqualTo(admin.email); draftApi.delete(); assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).drafts()).isEmpty(); } @Test public void comments() throws Exception { PushOneCommit.Result r = createChange(); CommentInput in = new CommentInput(); in.line = 1; in.message = "nit: trailing whitespace"; in.path = FILE_NAME; ReviewInput reviewInput = new ReviewInput(); Map<String, List<CommentInput>> comments = new HashMap<>(); comments.put(FILE_NAME, Collections.singletonList(in)); reviewInput.comments = comments; reviewInput.message = "comment test"; gApi.changes().id(r.getChangeId()).current().review(reviewInput); Map<String, List<CommentInfo>> out = gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()) .comments(); assertThat(out).hasSize(1); CommentInfo comment = Iterables.getOnlyElement(out.get(FILE_NAME)); assertThat(comment.message).isEqualTo(in.message); assertThat(comment.author.email).isEqualTo(admin.email); assertThat(comment.path).isNull(); List<CommentInfo> list = gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).commentsAsList(); assertThat(list).hasSize(1); CommentInfo comment2 = list.get(0); assertThat(comment2.path).isEqualTo(FILE_NAME); assertThat(comment2.line).isEqualTo(comment.line); assertThat(comment2.message).isEqualTo(comment.message); assertThat(comment2.author.email).isEqualTo(comment.author.email); assertThat( gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).comment(comment.id).get().message) .isEqualTo(in.message); } @Test public void patch() throws Exception { PushOneCommit.Result r = createChange(); ChangeApi changeApi = gApi.changes().id(r.getChangeId()); BinaryResult bin = changeApi.revision(r.getCommit().name()).patch(); ByteArrayOutputStream os = new ByteArrayOutputStream(); bin.writeTo(os); String res = new String(os.toByteArray(), StandardCharsets.UTF_8); ChangeInfo change = changeApi.get(); RevisionInfo rev = change.revisions.get(change.currentRevision); DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); String date = df.format(rev.commit.author.date); assertThat(res).isEqualTo(String.format(PATCH, r.getCommitId().name(), date, r.getChangeId())); } private void merge(PushOneCommit.Result r) throws Exception { revision(r).review(ReviewInput.approve()); revision(r).submit(); } private PushOneCommit.Result updateChange(PushOneCommit.Result r, String content) throws Exception { PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, "test commit", "a.txt", content, r.getChangeId()); return push.to("refs/for/master"); } private PushOneCommit.Result createDraft() throws Exception { PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo); return push.to("refs/drafts/master"); } }