| // Copyright (C) 2023 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.git; |
| |
| import com.google.common.collect.ImmutableMap; |
| import com.google.gerrit.acceptance.AbstractDaemonTest; |
| import com.google.gerrit.acceptance.PushOneCommit; |
| import com.google.gerrit.entities.BooleanProjectConfig; |
| import com.google.gerrit.extensions.api.changes.ReviewInput; |
| import com.google.gerrit.extensions.client.InheritableBoolean; |
| import com.google.gerrit.extensions.client.SubmitType; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| import org.eclipse.jgit.junit.TestRepository; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.revwalk.RevTree; |
| import org.eclipse.jgit.treewalk.TreeWalk; |
| import org.eclipse.jgit.treewalk.filter.TreeFilter; |
| import org.eclipse.jgit.util.RawParseUtils; |
| |
| /** |
| * Base class for different tests for implicit merge. |
| * |
| * <p>Provides shared methods for tests changes and branches manipulations. |
| */ |
| public abstract class AbstractImplicitMergeTest extends AbstractDaemonTest { |
| |
| /** Creates and pushes a simple approved changes without files and with specified parents. */ |
| protected PushOneCommit.Result createApprovedChange(String targetBranch, RevCommit... parents) |
| throws Exception { |
| PushOneCommit.Result result = pushTo("refs/for/" + targetBranch, ImmutableMap.of(), parents); |
| gApi.changes().id(result.getChangeId()).current().review(ReviewInput.approve()); |
| return result; |
| } |
| |
| /** Creates and pushes simple approved changes without files and with specified parents. */ |
| protected PushOneCommit.Result createApprovedChange( |
| String targetBranch, PushOneCommit.Result... parents) throws Exception { |
| return createApprovedChange( |
| targetBranch, |
| Arrays.stream(parents).map(PushOneCommit.Result::getCommit).toArray(RevCommit[]::new)); |
| } |
| |
| /** Creates and pushes a commit with specified files and parents. */ |
| protected PushOneCommit.Result pushTo( |
| String ref, ImmutableMap<String, String> files, RevCommit... parents) throws Exception { |
| PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, "Some commit", files); |
| push.setParents(List.of(parents)); |
| PushOneCommit.Result result = push.to(ref); |
| result.assertOkStatus(); |
| return result; |
| } |
| |
| /** |
| * Creates a change in the in-memory repository but doesn't push it to gerrit. |
| * |
| * <p>The method can be used to create chain of changes. The last change in the chain can be |
| * created using {@link #createApprovedChange} or {@link #pushTo} methods - these method will push |
| * the whole chain to gerrit as a single git push operations. |
| */ |
| protected RevCommit createChangeWithoutPush( |
| String changeId, ImmutableMap<String, String> files, RevCommit... parents) throws Exception { |
| TestRepository<?>.CommitBuilder commitBuilder = |
| testRepo |
| .commit() |
| .message("Change " + changeId) |
| // The passed changeId starts with 'I', but insertChangeId expects id without 'I'. |
| .insertChangeId(changeId.substring(1)); |
| for (RevCommit parent : parents) { |
| commitBuilder.parent(parent); |
| } |
| for (Map.Entry<String, String> entry : files.entrySet()) { |
| commitBuilder.add(entry.getKey(), entry.getValue()); |
| } |
| |
| return commitBuilder.create(); |
| } |
| |
| protected void setRejectImplicitMerges() throws Exception { |
| setRejectImplicitMerges(/*reject=*/ true); |
| } |
| |
| protected void setRejectImplicitMerges(boolean reject) throws Exception { |
| try (ProjectConfigUpdate u = updateProject(project)) { |
| u.getConfig() |
| .updateProject( |
| p -> |
| p.setBooleanConfig( |
| BooleanProjectConfig.REJECT_IMPLICIT_MERGES, |
| reject ? InheritableBoolean.TRUE : InheritableBoolean.FALSE)); |
| u.save(); |
| } |
| } |
| |
| protected void setSubmitType(SubmitType submitType) throws Exception { |
| try (ProjectConfigUpdate u = updateProject(project)) { |
| u.getConfig().updateProject(p -> p.setSubmitType(submitType)); |
| u.save(); |
| } |
| } |
| |
| protected ImmutableMap<String, String> getRemoteBranchRootPathContent(String refName) |
| throws Exception { |
| String revision = gApi.projects().name(project.get()).branch(refName).get().revision; |
| testRepo.git().fetch().setRemote("origin").call(); |
| RevTree revTree = |
| testRepo.getRepository().parseCommit(testRepo.getRepository().resolve(revision)).getTree(); |
| try (TreeWalk tw = new TreeWalk(testRepo.getRepository())) { |
| tw.setFilter(TreeFilter.ALL); |
| tw.setRecursive(false); |
| tw.reset(revTree); |
| ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); |
| while (tw.next()) { |
| String path = tw.getPathString(); |
| String content = |
| RawParseUtils.decode( |
| testRepo.getRepository().open(tw.getObjectId(0)).getCachedBytes(Integer.MAX_VALUE)); |
| builder.put(path, content); |
| } |
| return builder.buildOrThrow(); |
| } |
| } |
| |
| protected PushOneCommit.Result push(String ref, String subject, String fileName, String content) |
| throws Exception { |
| PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content); |
| return push.to(ref); |
| } |
| } |