com.google.gerrit.acceptance.rest.change.AbstractSubmit.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.acceptance.rest.change.AbstractSubmit.java

Source

// 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.rest.change;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;

import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.TestProjectInput;
import com.google.gerrit.common.EventListener;
import com.google.gerrit.common.EventSource;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.events.ChangeMergedEvent;
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.testutil.ConfigSuite;
import com.google.gson.reflect.TypeToken;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;

import org.apache.http.HttpStatus;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;

public abstract class AbstractSubmit extends AbstractDaemonTest {
    @ConfigSuite.Config
    public static Config submitWholeTopicEnabled() {
        return submitWholeTopicEnabledConfig();
    }

    private Map<String, String> mergeResults;

    @Inject
    private ChangeNotes.Factory notesFactory;

    @Inject
    private ApprovalsUtil approvalsUtil;

    @Inject
    private IdentifiedUser.GenericFactory factory;

    @Inject
    EventSource source;

    @Before
    public void setUp() throws Exception {
        mergeResults = Maps.newHashMap();
        CurrentUser listenerUser = factory.create(user.id);
        source.addEventListener(new EventListener() {

            @Override
            public void onEvent(Event event) {
                if (event instanceof ChangeMergedEvent) {
                    ChangeMergedEvent changeMergedEvent = (ChangeMergedEvent) event;
                    mergeResults.put(changeMergedEvent.change.number, changeMergedEvent.newRev);
                }
            }

        }, listenerUser);
    }

    @After
    public void cleanup() {
        db.close();
    }

    protected abstract SubmitType getSubmitType();

    @Test
    @TestProjectInput(createEmptyCommit = false)
    public void submitToEmptyRepo() throws Exception {
        PushOneCommit.Result change = createChange();
        submit(change.getChangeId());
        assertThat(getRemoteHead().getId()).isEqualTo(change.getCommitId());
    }

    @Test
    public void submitWholeTopic() throws Exception {
        assume().that(isSubmitWholeTopicEnabled()).isTrue();
        PushOneCommit.Result change1 = createChange("Change 1", "a.txt", "content", "test-topic");
        PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "content", "test-topic");
        PushOneCommit.Result change3 = createChange("Change 3", "c.txt", "content", "test-topic");
        approve(change1.getChangeId());
        approve(change2.getChangeId());
        approve(change3.getChangeId());
        submit(change3.getChangeId());
        change1.assertChange(Change.Status.MERGED, "test-topic", admin);
        change2.assertChange(Change.Status.MERGED, "test-topic", admin);
        change3.assertChange(Change.Status.MERGED, "test-topic", admin);
        // Check for the exact change to have the correct submitter.
        assertSubmitter(change3);
        // Also check submitters for changes submitted via the topic relationship.
        assertSubmitter(change1);
        assertSubmitter(change2);
    }

    private void assertSubmitter(PushOneCommit.Result change) throws Exception {
        ChangeInfo info = get(change.getChangeId(), ListChangesOption.MESSAGES);
        assertThat(info.messages).isNotNull();
        assertThat(info.messages).hasSize(3);
        if (getSubmitType() == SubmitType.CHERRY_PICK) {
            assertThat(Iterables.getLast(info.messages).message)
                    .startsWith("Change has been successfully cherry-picked as ");
        } else {
            assertThat(Iterables.getLast(info.messages).message)
                    .isEqualTo("Change has been successfully merged by Administrator");
        }
    }

    @Override
    protected void updateProjectInput(ProjectInput in) {
        in.submitType = getSubmitType();
        if (in.useContentMerge == InheritableBoolean.INHERIT) {
            in.useContentMerge = InheritableBoolean.FALSE;
        }
    }

    protected PushOneCommit.Result createChange(String subject, String fileName, String content) throws Exception {
        PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, subject, fileName, content);
        return push.to("refs/for/master");
    }

    protected PushOneCommit.Result createChange(String subject, String fileName, String content, String topic)
            throws Exception {
        PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, subject, fileName, content);
        return push.to("refs/for/master/" + topic);
    }

    protected PushOneCommit.Result createChange(TestRepository<?> repo, String branch, String subject,
            String fileName, String content, String topic) throws Exception {
        PushOneCommit push = pushFactory.create(db, admin.getIdent(), repo, subject, fileName, content);
        return push.to("refs/for/" + branch + "/" + name(topic));
    }

    protected void submit(String changeId) throws Exception {
        submit(changeId, HttpStatus.SC_OK, null);
    }

    protected void submitWithConflict(String changeId, String expectedError) throws Exception {
        submit(changeId, HttpStatus.SC_CONFLICT, expectedError);
    }

    private void submit(String changeId, int expectedStatus, String msg) throws Exception {
        approve(changeId);
        SubmitInput subm = new SubmitInput();
        RestResponse r = adminSession.post("/changes/" + changeId + "/submit", subm);
        assertThat(r.getStatusCode()).isEqualTo(expectedStatus);
        if (expectedStatus == HttpStatus.SC_OK) {
            checkArgument(msg == null, "msg must be null for successful submits");
            ChangeInfo change = newGson().fromJson(r.getReader(), new TypeToken<ChangeInfo>() {
            }.getType());
            assertThat(change.status).isEqualTo(ChangeStatus.MERGED);

            checkMergeResult(change);
        } else {
            checkArgument(!Strings.isNullOrEmpty(msg),
                    "msg must be a valid string " + "containing an error message for unsuccessful submits");
            assertThat(r.getEntityContent()).isEqualTo(msg);
        }
        r.consume();
    }

    private void checkMergeResult(ChangeInfo change) throws IOException {
        // Get the revision of the branch after the submit to compare with the
        // newRev of the ChangeMergedEvent.
        RestResponse b = adminSession.get("/projects/" + change.project + "/branches/" + change.branch);
        if (b.getStatusCode() == HttpStatus.SC_OK) {
            BranchInfo branch = newGson().fromJson(b.getReader(), new TypeToken<BranchInfo>() {
            }.getType());
            assertThat(mergeResults).isNotEmpty();
            String newRev = mergeResults.get(Integer.toString(change._number));
            assertThat(newRev).isNotNull();
            assertThat(branch.revision).isEqualTo(newRev);
        }
        b.consume();
    }

    protected void assertCurrentRevision(String changeId, int expectedNum, ObjectId expectedId) throws Exception {
        ChangeInfo c = get(changeId, CURRENT_REVISION);
        assertThat(c.currentRevision).isEqualTo(expectedId.name());
        assertThat(c.revisions.get(expectedId.name())._number).isEqualTo(expectedNum);
        try (Repository repo = repoManager.openRepository(new Project.NameKey(c.project))) {
            Ref ref = repo.getRef(new PatchSet.Id(new Change.Id(c._number), expectedNum).toRefName());
            assertThat(ref).isNotNull();
            assertThat(ref.getObjectId()).isEqualTo(expectedId);
        }
    }

    protected void assertNew(String changeId) throws Exception {
        assertThat(get(changeId).status).isEqualTo(ChangeStatus.NEW);
    }

    protected void assertApproved(String changeId) throws Exception {
        ChangeInfo c = get(changeId, DETAILED_LABELS);
        LabelInfo cr = c.labels.get("Code-Review");
        assertThat(cr.all).hasSize(1);
        assertThat(cr.all.get(0).value).isEqualTo(2);
        assertThat(new Account.Id(cr.all.get(0)._accountId)).isEqualTo(admin.getId());
    }

    protected void assertPersonEquals(PersonIdent expected, PersonIdent actual) {
        assertThat(actual.getEmailAddress()).isEqualTo(expected.getEmailAddress());
        assertThat(actual.getName()).isEqualTo(expected.getName());
        assertThat(actual.getTimeZone()).isEqualTo(expected.getTimeZone());
    }

    protected void assertSubmitter(String changeId, int psId) throws OrmException {
        ChangeNotes cn = notesFactory.create(getOnlyElement(queryProvider.get().byKeyPrefix(changeId)).change());
        PatchSetApproval submitter = approvalsUtil.getSubmitter(db, cn, new PatchSet.Id(cn.getChangeId(), psId));
        assertThat(submitter.isSubmit()).isTrue();
        assertThat(submitter.getAccountId()).isEqualTo(admin.getId());
    }

    protected void assertNoSubmitter(String changeId, int psId) throws OrmException {
        ChangeNotes cn = notesFactory.create(getOnlyElement(queryProvider.get().byKeyPrefix(changeId)).change());
        PatchSetApproval submitter = approvalsUtil.getSubmitter(db, cn, new PatchSet.Id(cn.getChangeId(), psId));
        assertThat(submitter).isNull();
    }

    protected void assertCherryPick(TestRepository<?> testRepo, boolean contentMerge) throws IOException {
        assertRebase(testRepo, contentMerge);
        RevCommit remoteHead = getRemoteHead();
        assertThat(remoteHead.getFooterLines("Reviewed-On")).isNotEmpty();
        assertThat(remoteHead.getFooterLines("Reviewed-By")).isNotEmpty();
    }

    protected void assertRebase(TestRepository<?> testRepo, boolean contentMerge) throws IOException {
        Repository repo = testRepo.getRepository();
        RevCommit localHead = getHead(repo);
        RevCommit remoteHead = getRemoteHead();
        assert_().withFailureMessage(String.format("%s not equal %s", localHead.name(), remoteHead.name()))
                .that(localHead.getId()).isNotEqualTo(remoteHead.getId());
        assertThat(remoteHead.getParentCount()).isEqualTo(1);
        if (!contentMerge) {
            assertThat(getLatestRemoteDiff()).isEqualTo(getLatestDiff(repo));
        }
        assertThat(remoteHead.getShortMessage()).isEqualTo(localHead.getShortMessage());
    }

    private RevCommit getHead(Repository repo) throws IOException {
        return getHead(repo, "HEAD");
    }

    protected RevCommit getRemoteHead(Project.NameKey project, String branch) throws IOException {
        try (Repository repo = repoManager.openRepository(project)) {
            return getHead(repo, "refs/heads/" + branch);
        }
    }

    protected RevCommit getRemoteHead() throws IOException {
        return getRemoteHead(project, "master");
    }

    protected List<RevCommit> getRemoteLog(Project.NameKey project, String branch) throws IOException {
        try (Repository repo = repoManager.openRepository(project); RevWalk rw = new RevWalk(repo)) {
            rw.markStart(rw.parseCommit(repo.getRef("refs/heads/" + branch).getObjectId()));
            return Lists.newArrayList(rw);
        }
    }

    protected List<RevCommit> getRemoteLog() throws IOException {
        return getRemoteLog(project, "master");
    }

    private RevCommit getHead(Repository repo, String name) throws IOException {
        try (RevWalk rw = new RevWalk(repo)) {
            return rw.parseCommit(repo.getRef(name).getObjectId());
        }
    }

    private String getLatestDiff(Repository repo) throws IOException {
        ObjectId oldTreeId = repo.resolve("HEAD~1^{tree}");
        ObjectId newTreeId = repo.resolve("HEAD^{tree}");
        return getLatestDiff(repo, oldTreeId, newTreeId);
    }

    private String getLatestRemoteDiff() throws IOException {
        try (Repository repo = repoManager.openRepository(project); RevWalk rw = new RevWalk(repo)) {
            ObjectId oldTreeId = repo.resolve("refs/heads/master~1^{tree}");
            ObjectId newTreeId = repo.resolve("refs/heads/master^{tree}");
            return getLatestDiff(repo, oldTreeId, newTreeId);
        }
    }

    private String getLatestDiff(Repository repo, ObjectId oldTreeId, ObjectId newTreeId) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try (DiffFormatter fmt = new DiffFormatter(out)) {
            fmt.setRepository(repo);
            fmt.format(oldTreeId, newTreeId);
            fmt.flush();
            return out.toString();
        }
    }
}