LCOV - code coverage report
Current view: top level - acceptance - PushOneCommit.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 143 162 88.3 %
Date: 2022-11-19 15:00:39 Functions: 47 50 94.0 %

          Line data    Source code
       1             : // Copyright (C) 2013 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.acceptance;
      16             : 
      17             : import static com.google.common.truth.Truth.assertThat;
      18             : import static com.google.common.truth.Truth.assertWithMessage;
      19             : import static com.google.gerrit.acceptance.GitUtil.pushHead;
      20             : import static org.junit.Assert.assertEquals;
      21             : 
      22             : import com.google.common.base.Strings;
      23             : import com.google.common.collect.ImmutableList;
      24             : import com.google.common.collect.ImmutableMap;
      25             : import com.google.common.collect.Iterables;
      26             : import com.google.common.collect.Sets;
      27             : import com.google.errorprone.annotations.CanIgnoreReturnValue;
      28             : import com.google.gerrit.common.Nullable;
      29             : import com.google.gerrit.common.UsedAt;
      30             : import com.google.gerrit.common.UsedAt.Project;
      31             : import com.google.gerrit.entities.Account;
      32             : import com.google.gerrit.entities.Change;
      33             : import com.google.gerrit.entities.PatchSet;
      34             : import com.google.gerrit.server.approval.ApprovalsUtil;
      35             : import com.google.gerrit.server.notedb.ChangeNotes;
      36             : import com.google.gerrit.server.notedb.ReviewerStateInternal;
      37             : import com.google.gerrit.server.query.change.ChangeData;
      38             : import com.google.gerrit.server.query.change.InternalChangeQuery;
      39             : import com.google.inject.Provider;
      40             : import com.google.inject.assistedinject.Assisted;
      41             : import com.google.inject.assistedinject.AssistedInject;
      42             : import java.util.Arrays;
      43             : import java.util.List;
      44             : import java.util.Map;
      45             : import java.util.concurrent.atomic.AtomicInteger;
      46             : import org.eclipse.jgit.api.TagCommand;
      47             : import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
      48             : import org.eclipse.jgit.dircache.DirCacheEntry;
      49             : import org.eclipse.jgit.junit.TestRepository;
      50             : import org.eclipse.jgit.lib.FileMode;
      51             : import org.eclipse.jgit.lib.ObjectId;
      52             : import org.eclipse.jgit.lib.PersonIdent;
      53             : import org.eclipse.jgit.revwalk.RevBlob;
      54             : import org.eclipse.jgit.revwalk.RevCommit;
      55             : import org.eclipse.jgit.transport.PushResult;
      56             : import org.eclipse.jgit.transport.RemoteRefUpdate;
      57             : import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
      58             : 
      59             : public class PushOneCommit {
      60             :   public static final String SUBJECT = "test commit";
      61             :   public static final String FILE_NAME = "a.txt";
      62             :   public static final String FILE_CONTENT = "some content";
      63             :   public static final String PATCH_FILE_ONLY =
      64             :       "diff --git a/a.txt b/a.txt\n"
      65             :           + "new file mode 100644\n"
      66             :           + "index 0000000..f0eec86\n"
      67             :           + "--- /dev/null\n"
      68             :           + "+++ b/a.txt\n"
      69             :           + "@@ -0,0 +1 @@\n"
      70             :           + "+some content\n"
      71             :           + "\\ No newline at end of file\n";
      72             :   public static final String PATCH =
      73             :       "From %s Mon Sep 17 00:00:00 2001\n"
      74             :           + "From: Administrator <admin@example.com>\n"
      75             :           + "Date: %s\n"
      76             :           + "Subject: [PATCH] test commit\n"
      77             :           + "\n"
      78             :           + "Change-Id: %s\n"
      79             :           + "---\n"
      80             :           + "\n"
      81             :           + PATCH_FILE_ONLY;
      82             : 
      83             :   public interface Factory {
      84             :     PushOneCommit create(PersonIdent i, TestRepository<?> testRepo);
      85             : 
      86             :     PushOneCommit create(
      87             :         PersonIdent i, TestRepository<?> testRepo, @Assisted("changeId") String changeId);
      88             : 
      89             :     PushOneCommit create(
      90             :         PersonIdent i,
      91             :         TestRepository<?> testRepo,
      92             :         @Assisted("subject") String subject,
      93             :         @Assisted("fileName") String fileName,
      94             :         @Assisted("content") String content);
      95             : 
      96             :     PushOneCommit create(
      97             :         PersonIdent i,
      98             :         TestRepository<?> testRepo,
      99             :         @Assisted String subject,
     100             :         @Assisted Map<String, String> files);
     101             : 
     102             :     @UsedAt(Project.PLUGIN_CODE_OWNERS)
     103             :     PushOneCommit create(
     104             :         PersonIdent i,
     105             :         TestRepository<?> testRepo,
     106             :         @Assisted("subject") String subject,
     107             :         @Assisted Map<String, String> files,
     108             :         @Assisted("changeId") String changeId);
     109             : 
     110             :     PushOneCommit create(
     111             :         PersonIdent i,
     112             :         TestRepository<?> testRepo,
     113             :         @Assisted("subject") String subject,
     114             :         @Assisted("fileName") String fileName,
     115             :         @Assisted("content") String content,
     116             :         @Assisted("changeId") String changeId);
     117             :   }
     118             : 
     119             :   public static class Tag {
     120             :     public String name;
     121             : 
     122           0 :     public Tag(String name) {
     123           0 :       this.name = name;
     124           0 :     }
     125             :   }
     126             : 
     127             :   public static class AnnotatedTag extends Tag {
     128             :     public String message;
     129             :     public PersonIdent tagger;
     130             : 
     131             :     public AnnotatedTag(String name, String message, PersonIdent tagger) {
     132           0 :       super(name);
     133           0 :       this.message = message;
     134           0 :       this.tagger = tagger;
     135           0 :     }
     136             :   }
     137             : 
     138          88 :   private static final AtomicInteger CHANGE_ID_COUNTER = new AtomicInteger();
     139             : 
     140             :   private static String nextChangeId() {
     141             :     // Tests use a variety of mechanisms for setting temporary timestamps, so we can't guarantee
     142             :     // that the PersonIdent (or any other field used by the Change-Id generator) for any two test
     143             :     // methods in the same acceptance test class are going to be different. But tests generally
     144             :     // assume that Change-Ids are unique unless otherwise specified. So, don't even bother trying to
     145             :     // reuse JGit's Change-Id generator, just do the simplest possible thing and convert a counter
     146             :     // to hex.
     147          88 :     return String.format("%040x", CHANGE_ID_COUNTER.incrementAndGet());
     148             :   }
     149             : 
     150             :   private final ChangeNotes.Factory notesFactory;
     151             :   private final ApprovalsUtil approvalsUtil;
     152             :   private final Provider<InternalChangeQuery> queryProvider;
     153             :   private final TestRepository<?> testRepo;
     154             : 
     155             :   private final String subject;
     156             :   private final Map<String, String> files;
     157             :   private String changeId;
     158             :   private Tag tag;
     159             :   private boolean force;
     160             :   private List<String> pushOptions;
     161             : 
     162             :   private final TestRepository<?>.CommitBuilder commitBuilder;
     163             : 
     164             :   @AssistedInject
     165             :   PushOneCommit(
     166             :       ChangeNotes.Factory notesFactory,
     167             :       ApprovalsUtil approvalsUtil,
     168             :       Provider<InternalChangeQuery> queryProvider,
     169             :       @Assisted PersonIdent i,
     170             :       @Assisted TestRepository<?> testRepo)
     171             :       throws Exception {
     172          71 :     this(notesFactory, approvalsUtil, queryProvider, i, testRepo, SUBJECT, FILE_NAME, FILE_CONTENT);
     173          71 :   }
     174             : 
     175             :   @AssistedInject
     176             :   PushOneCommit(
     177             :       ChangeNotes.Factory notesFactory,
     178             :       ApprovalsUtil approvalsUtil,
     179             :       Provider<InternalChangeQuery> queryProvider,
     180             :       @Assisted PersonIdent i,
     181             :       @Assisted TestRepository<?> testRepo,
     182             :       @Assisted("changeId") String changeId)
     183             :       throws Exception {
     184           6 :     this(
     185             :         notesFactory,
     186             :         approvalsUtil,
     187             :         queryProvider,
     188             :         i,
     189             :         testRepo,
     190             :         SUBJECT,
     191             :         FILE_NAME,
     192             :         FILE_CONTENT,
     193             :         changeId);
     194           6 :   }
     195             : 
     196             :   @AssistedInject
     197             :   PushOneCommit(
     198             :       ChangeNotes.Factory notesFactory,
     199             :       ApprovalsUtil approvalsUtil,
     200             :       Provider<InternalChangeQuery> queryProvider,
     201             :       @Assisted PersonIdent i,
     202             :       @Assisted TestRepository<?> testRepo,
     203             :       @Assisted("subject") String subject,
     204             :       @Assisted("fileName") String fileName,
     205             :       @Assisted("content") String content)
     206             :       throws Exception {
     207          83 :     this(notesFactory, approvalsUtil, queryProvider, i, testRepo, subject, fileName, content, null);
     208          83 :   }
     209             : 
     210             :   @AssistedInject
     211             :   PushOneCommit(
     212             :       ChangeNotes.Factory notesFactory,
     213             :       ApprovalsUtil approvalsUtil,
     214             :       Provider<InternalChangeQuery> queryProvider,
     215             :       @Assisted PersonIdent i,
     216             :       @Assisted TestRepository<?> testRepo,
     217             :       @Assisted String subject,
     218             :       @Assisted Map<String, String> files)
     219             :       throws Exception {
     220          22 :     this(notesFactory, approvalsUtil, queryProvider, i, testRepo, subject, files, null);
     221          22 :   }
     222             : 
     223             :   @AssistedInject
     224             :   PushOneCommit(
     225             :       ChangeNotes.Factory notesFactory,
     226             :       ApprovalsUtil approvalsUtil,
     227             :       Provider<InternalChangeQuery> queryProvider,
     228             :       @Assisted PersonIdent i,
     229             :       @Assisted TestRepository<?> testRepo,
     230             :       @Assisted("subject") String subject,
     231             :       @Assisted("fileName") String fileName,
     232             :       @Assisted("content") String content,
     233             :       @Nullable @Assisted("changeId") String changeId)
     234             :       throws Exception {
     235          84 :     this(
     236             :         notesFactory,
     237             :         approvalsUtil,
     238             :         queryProvider,
     239             :         i,
     240             :         testRepo,
     241             :         subject,
     242          84 :         ImmutableMap.of(fileName, content),
     243             :         changeId);
     244          84 :   }
     245             : 
     246             :   @AssistedInject
     247             :   PushOneCommit(
     248             :       ChangeNotes.Factory notesFactory,
     249             :       ApprovalsUtil approvalsUtil,
     250             :       Provider<InternalChangeQuery> queryProvider,
     251             :       @Assisted PersonIdent i,
     252             :       @Assisted TestRepository<?> testRepo,
     253             :       @Assisted("subject") String subject,
     254             :       @Assisted Map<String, String> files,
     255             :       @Nullable @Assisted("changeId") String changeId)
     256          88 :       throws Exception {
     257          88 :     this.testRepo = testRepo;
     258          88 :     this.notesFactory = notesFactory;
     259          88 :     this.approvalsUtil = approvalsUtil;
     260          88 :     this.queryProvider = queryProvider;
     261          88 :     this.subject = subject;
     262          88 :     this.files = files;
     263          88 :     this.changeId = changeId;
     264          88 :     if (changeId != null) {
     265          37 :       commitBuilder = testRepo.amendRef("HEAD").insertChangeId(changeId.substring(1));
     266             :     } else {
     267          88 :       if (subject.contains("\nChange-Id: ")) {
     268           1 :         commitBuilder = testRepo.amendRef("HEAD");
     269             :       } else {
     270          88 :         commitBuilder = testRepo.branch("HEAD").commit().insertChangeId(nextChangeId());
     271             :       }
     272             :     }
     273          88 :     commitBuilder.message(subject).author(i).committer(new PersonIdent(i, testRepo.getDate()));
     274          88 :   }
     275             : 
     276             :   public PushOneCommit setParents(List<RevCommit> parents) throws Exception {
     277          21 :     commitBuilder.noParents();
     278          21 :     for (RevCommit p : parents) {
     279          19 :       commitBuilder.parent(p);
     280          19 :     }
     281          21 :     return this;
     282             :   }
     283             : 
     284             :   @CanIgnoreReturnValue
     285             :   public PushOneCommit setTopLevelTreeId(ObjectId treeId) throws Exception {
     286           1 :     commitBuilder.setTopLevelTree(treeId);
     287           1 :     return this;
     288             :   }
     289             : 
     290             :   public PushOneCommit setParent(RevCommit parent) throws Exception {
     291           3 :     commitBuilder.noParents();
     292           3 :     commitBuilder.parent(parent);
     293           3 :     return this;
     294             :   }
     295             : 
     296             :   public PushOneCommit noParent() throws Exception {
     297           2 :     commitBuilder.noParents();
     298           2 :     return this;
     299             :   }
     300             : 
     301             :   public PushOneCommit addFile(String path, String content, int fileMode) throws Exception {
     302           2 :     RevBlob blobId = testRepo.blob(content);
     303           2 :     commitBuilder.edit(
     304           2 :         new PathEdit(path) {
     305             :           @Override
     306             :           public void apply(DirCacheEntry ent) {
     307           2 :             ent.setFileMode(FileMode.fromBits(fileMode));
     308           2 :             ent.setObjectId(blobId);
     309           2 :           }
     310             :         });
     311           2 :     return this;
     312             :   }
     313             : 
     314             :   public PushOneCommit addSymlink(String path, String target) throws Exception {
     315           2 :     RevBlob blobId = testRepo.blob(target);
     316           2 :     commitBuilder.edit(
     317           2 :         new PathEdit(path) {
     318             :           @Override
     319             :           public void apply(DirCacheEntry ent) {
     320           2 :             ent.setFileMode(FileMode.SYMLINK);
     321           2 :             ent.setObjectId(blobId);
     322           2 :           }
     323             :         });
     324           2 :     return this;
     325             :   }
     326             : 
     327             :   public PushOneCommit addGitSubmodule(String modulePath, ObjectId commitId) {
     328           2 :     commitBuilder.edit(
     329           2 :         new PathEdit(modulePath) {
     330             :           @Override
     331             :           public void apply(DirCacheEntry ent) {
     332           2 :             ent.setFileMode(FileMode.GITLINK);
     333           2 :             ent.setObjectId(commitId);
     334           2 :           }
     335             :         });
     336           2 :     return this;
     337             :   }
     338             : 
     339             :   public PushOneCommit rmFile(String filename) {
     340           3 :     commitBuilder.rm(filename);
     341           3 :     return this;
     342             :   }
     343             : 
     344             :   public Result to(String ref) throws Exception {
     345          88 :     for (Map.Entry<String, String> e : files.entrySet()) {
     346          88 :       commitBuilder.add(e.getKey(), e.getValue());
     347          88 :     }
     348          88 :     return execute(ref);
     349             :   }
     350             : 
     351             :   public Result rm(String ref) throws Exception {
     352           5 :     for (String fileName : files.keySet()) {
     353           5 :       commitBuilder.rm(fileName);
     354           5 :     }
     355           5 :     return execute(ref);
     356             :   }
     357             : 
     358             :   public Result execute(String ref) throws Exception {
     359          88 :     RevCommit c = commitBuilder.create();
     360          88 :     if (changeId == null) {
     361          88 :       changeId = GitUtil.getChangeId(testRepo, c).get();
     362             :     }
     363          88 :     if (tag != null) {
     364           0 :       TagCommand tagCommand = testRepo.git().tag().setName(tag.name);
     365           0 :       if (tag instanceof AnnotatedTag) {
     366           0 :         AnnotatedTag annotatedTag = (AnnotatedTag) tag;
     367           0 :         tagCommand
     368           0 :             .setAnnotated(true)
     369           0 :             .setMessage(annotatedTag.message)
     370           0 :             .setTagger(annotatedTag.tagger);
     371           0 :       } else {
     372           0 :         tagCommand.setAnnotated(false);
     373             :       }
     374           0 :       tagCommand.call();
     375             :     }
     376          88 :     return new Result(ref, pushHead(testRepo, ref, tag != null, force, pushOptions), c, subject);
     377             :   }
     378             : 
     379             :   public void setTag(Tag tag) {
     380           0 :     this.tag = tag;
     381           0 :   }
     382             : 
     383             :   public void setForce(boolean force) {
     384           8 :     this.force = force;
     385           8 :   }
     386             : 
     387             :   public List<String> getPushOptions() {
     388           3 :     return pushOptions;
     389             :   }
     390             : 
     391             :   public void setPushOptions(List<String> pushOptions) {
     392           4 :     this.pushOptions = pushOptions;
     393           4 :   }
     394             : 
     395             :   public void noParents() {
     396           2 :     commitBuilder.noParents();
     397           2 :   }
     398             : 
     399             :   public class Result {
     400             :     private final String ref;
     401             :     private final PushResult result;
     402             :     private final RevCommit commit;
     403             :     private final String resSubj;
     404             : 
     405          88 :     private Result(String ref, PushResult resSubj, RevCommit commit, String subject) {
     406          88 :       this.ref = ref;
     407          88 :       this.result = resSubj;
     408          88 :       this.commit = commit;
     409          88 :       this.resSubj = subject;
     410          88 :     }
     411             : 
     412             :     public ChangeData getChange() {
     413          53 :       return Iterables.getOnlyElement(queryProvider.get().byKeyPrefix(changeId));
     414             :     }
     415             : 
     416             :     public PatchSet getPatchSet() {
     417           8 :       return getChange().currentPatchSet();
     418             :     }
     419             : 
     420             :     public PatchSet.Id getPatchSetId() {
     421          16 :       return getChange().change().currentPatchSetId();
     422             :     }
     423             : 
     424             :     public String getChangeId() {
     425          73 :       return changeId;
     426             :     }
     427             : 
     428             :     public RevCommit getCommit() {
     429          54 :       return commit;
     430             :     }
     431             : 
     432             :     public void assertPushOptions(List<String> pushOptions) {
     433           3 :       assertEquals(pushOptions, getPushOptions());
     434           3 :     }
     435             : 
     436             :     public void assertChange(
     437             :         Change.Status expectedStatus, String expectedTopic, TestAccount... expectedReviewers) {
     438          11 :       assertChange(
     439          11 :           expectedStatus, expectedTopic, Arrays.asList(expectedReviewers), ImmutableList.of());
     440          11 :     }
     441             : 
     442             :     public void assertChange(
     443             :         Change.Status expectedStatus,
     444             :         String expectedTopic,
     445             :         List<TestAccount> expectedReviewers,
     446             :         List<TestAccount> expectedCcs) {
     447          11 :       Change c = getChange().change();
     448          11 :       assertThat(c.getSubject()).isEqualTo(resSubj);
     449          11 :       assertThat(c.getStatus()).isEqualTo(expectedStatus);
     450          11 :       assertThat(Strings.emptyToNull(c.getTopic())).isEqualTo(expectedTopic);
     451          11 :       assertReviewers(c, ReviewerStateInternal.REVIEWER, expectedReviewers);
     452          11 :       assertReviewers(c, ReviewerStateInternal.CC, expectedCcs);
     453          11 :     }
     454             : 
     455             :     private void assertReviewers(
     456             :         Change c, ReviewerStateInternal state, List<TestAccount> expectedReviewers) {
     457          11 :       Iterable<Account.Id> actualIds =
     458          11 :           approvalsUtil.getReviewers(notesFactory.createChecked(c)).byState(state);
     459          11 :       assertThat(actualIds)
     460          11 :           .containsExactlyElementsIn(Sets.newHashSet(TestAccount.ids(expectedReviewers)));
     461          11 :     }
     462             : 
     463             :     public void assertOkStatus() {
     464          78 :       assertStatus(Status.OK, null);
     465          78 :     }
     466             : 
     467             :     public void assertErrorStatus(String expectedMessage) {
     468          15 :       assertStatus(Status.REJECTED_OTHER_REASON, expectedMessage);
     469          15 :     }
     470             : 
     471             :     public void assertErrorStatus() {
     472          10 :       RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
     473          10 :       assertThat(refUpdate).isNotNull();
     474          10 :       assertWithMessage(message(refUpdate))
     475          10 :           .that(refUpdate.getStatus())
     476          10 :           .isEqualTo(Status.REJECTED_OTHER_REASON);
     477          10 :     }
     478             : 
     479             :     private void assertStatus(Status expectedStatus, String expectedMessage) {
     480          78 :       RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
     481          78 :       assertThat(refUpdate).isNotNull();
     482          78 :       assertWithMessage(message(refUpdate)).that(refUpdate.getStatus()).isEqualTo(expectedStatus);
     483          78 :       if (expectedMessage == null) {
     484          78 :         assertThat(refUpdate.getMessage()).isNull();
     485             :       } else {
     486          15 :         assertThat(refUpdate.getMessage()).contains(expectedMessage);
     487             :       }
     488          78 :     }
     489             : 
     490             :     public void assertMessage(String expectedMessage) {
     491          11 :       RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
     492          11 :       assertThat(refUpdate).isNotNull();
     493          11 :       assertThat(message(refUpdate).toLowerCase()).contains(expectedMessage.toLowerCase());
     494          11 :     }
     495             : 
     496             :     public void assertNotMessage(String message) {
     497           4 :       RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
     498           4 :       assertThat(message(refUpdate).toLowerCase()).doesNotContain(message.toLowerCase());
     499           4 :     }
     500             : 
     501             :     public String getMessage() {
     502           4 :       RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
     503           4 :       assertThat(refUpdate).isNotNull();
     504           4 :       return message(refUpdate);
     505             :     }
     506             : 
     507             :     private String message(RemoteRefUpdate refUpdate) {
     508          79 :       StringBuilder b = new StringBuilder();
     509          79 :       if (refUpdate.getMessage() != null) {
     510          18 :         b.append(refUpdate.getMessage());
     511          18 :         b.append("\n");
     512             :       }
     513          79 :       b.append(result.getMessages());
     514          79 :       return b.toString();
     515             :     }
     516             :   }
     517             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750