blob: 8702755de849318cab575dd2c5e90a4da9bb8b30 [file] [log] [blame]
// Copyright (C) 2020 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.server.submit;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.SubmoduleSubscription;
import com.google.gerrit.entities.SubscribeSection;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.submit.SubscriptionGraph.DefaultFactory;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class SubscriptionGraphTest {
private static final String TEST_PATH = "test/path";
private static final Project.NameKey SUPER_PROJECT = Project.nameKey("Superproject");
private static final Project.NameKey SUB_PROJECT = Project.nameKey("Subproject");
private static final BranchNameKey SUPER_BRANCH =
BranchNameKey.create(SUPER_PROJECT, "refs/heads/one");
private static final BranchNameKey SUB_BRANCH =
BranchNameKey.create(SUB_PROJECT, "refs/heads/one");
private InMemoryRepositoryManager repoManager = new InMemoryRepositoryManager();
private MergeOpRepoManager mergeOpRepoManager;
@Mock GitModules.Factory mockGitModulesFactory;
@Mock ProjectCache mockProjectCache;
@Mock ProjectState mockProjectState;
@Before
public void setUp() throws Exception {
when(mockProjectCache.get(any())).thenReturn(Optional.of(mockProjectState));
mergeOpRepoManager = new MergeOpRepoManager(repoManager, mockProjectCache, null, null);
GitModules emptyMockGitModules = mock(GitModules.class);
when(mockGitModulesFactory.create(any(), any())).thenReturn(emptyMockGitModules);
TestRepository<Repository> superProject = createRepo(SUPER_PROJECT);
TestRepository<Repository> submoduleProject = createRepo(SUB_PROJECT);
// Make sure that SUPER_BRANCH and SUB_BRANCH can be subscribed.
allowSubscription(SUPER_BRANCH);
allowSubscription(SUB_BRANCH);
setSubscription(SUB_BRANCH, ImmutableList.of(SUPER_BRANCH));
setSubscription(SUPER_BRANCH, ImmutableList.of());
createBranch(
superProject, SUPER_BRANCH, superProject.commit().message("Initial commit").create());
createBranch(
submoduleProject, SUB_BRANCH, submoduleProject.commit().message("Initial commit").create());
}
@Test
public void oneSuperprojectOneSubmodule() throws Exception {
SubscriptionGraph.Factory factory = new DefaultFactory(mockGitModulesFactory, mockProjectCache);
SubscriptionGraph subscriptionGraph =
factory.compute(ImmutableSet.of(SUB_BRANCH), mergeOpRepoManager);
assertThat(subscriptionGraph.getAffectedSuperProjects()).containsExactly(SUPER_PROJECT);
assertThat(subscriptionGraph.getAffectedSuperBranches(SUPER_PROJECT))
.containsExactly(SUPER_BRANCH);
assertThat(subscriptionGraph.getSubscriptions(SUPER_BRANCH))
.containsExactly(new SubmoduleSubscription(SUPER_BRANCH, SUB_BRANCH, TEST_PATH));
assertThat(subscriptionGraph.hasSuperproject(SUB_BRANCH)).isTrue();
assertThat(subscriptionGraph.getSortedSuperprojectAndSubmoduleBranches())
.containsExactly(SUB_BRANCH, SUPER_BRANCH)
.inOrder();
}
@Test
public void circularSubscription() throws Exception {
SubscriptionGraph.Factory factory = new DefaultFactory(mockGitModulesFactory, mockProjectCache);
setSubscription(SUPER_BRANCH, ImmutableList.of(SUB_BRANCH));
SubmoduleConflictException e =
assertThrows(
SubmoduleConflictException.class,
() -> factory.compute(ImmutableSet.of(SUB_BRANCH), mergeOpRepoManager));
String expectedErrorMessage =
"Subproject,refs/heads/one->Superproject,refs/heads/one->Subproject,refs/heads/one";
assertThat(e).hasMessageThat().contains(expectedErrorMessage);
}
@Test
public void multipleSuperprojectsToMultipleSubmodules() throws Exception {
// Create superprojects and subprojects.
Project.NameKey superProject1 = Project.nameKey("superproject1");
Project.NameKey superProject2 = Project.nameKey("superproject2");
Project.NameKey subProject1 = Project.nameKey("subproject1");
Project.NameKey subProject2 = Project.nameKey("subproject2");
TestRepository<Repository> superProjectRepo1 = createRepo(superProject1);
TestRepository<Repository> superProjectRepo2 = createRepo(superProject2);
TestRepository<Repository> submoduleRepo1 = createRepo(subProject1);
TestRepository<Repository> submoduleRepo2 = createRepo(subProject2);
// Initialize super branches.
BranchNameKey superBranch1 = BranchNameKey.create(superProject1, "refs/heads/one");
BranchNameKey superBranch2 = BranchNameKey.create(superProject2, "refs/heads/one");
createBranch(
superProjectRepo1,
superBranch1,
superProjectRepo1.commit().message("Initial commit").create());
createBranch(
superProjectRepo2,
superBranch2,
superProjectRepo2.commit().message("Initial commit").create());
// Initialize sub branches.
BranchNameKey submoduleBranch1 = BranchNameKey.create(subProject1, "refs/heads/one");
BranchNameKey submoduleBranch2 = BranchNameKey.create(subProject1, "refs/heads/two");
BranchNameKey submoduleBranch3 = BranchNameKey.create(subProject2, "refs/heads/one");
createBranch(
submoduleRepo1, submoduleBranch1, submoduleRepo1.commit().message("Commit1").create());
createBranch(
submoduleRepo1, submoduleBranch2, submoduleRepo1.commit().message("Commit2").create());
createBranch(
submoduleRepo2, submoduleBranch3, submoduleRepo2.commit().message("Commit1").create());
allowSubscription(submoduleBranch1);
allowSubscription(submoduleBranch2);
allowSubscription(submoduleBranch3);
// Initialize subscriptions.
setSubscription(submoduleBranch1, ImmutableList.of(superBranch1, superBranch2));
setSubscription(submoduleBranch2, ImmutableList.of(superBranch1));
setSubscription(submoduleBranch3, ImmutableList.of(superBranch1, superBranch2));
SubscriptionGraph.Factory factory = new DefaultFactory(mockGitModulesFactory, mockProjectCache);
SubscriptionGraph subscriptionGraph =
factory.compute(ImmutableSet.of(submoduleBranch1, submoduleBranch2), mergeOpRepoManager);
assertThat(subscriptionGraph.getAffectedSuperProjects())
.containsExactly(superProject1, superProject2);
assertThat(subscriptionGraph.getAffectedSuperBranches(superProject1))
.containsExactly(superBranch1);
assertThat(subscriptionGraph.getAffectedSuperBranches(superProject2))
.containsExactly(superBranch2);
assertThat(subscriptionGraph.getSubscriptions(superBranch1))
.containsExactly(
new SubmoduleSubscription(superBranch1, submoduleBranch1, TEST_PATH),
new SubmoduleSubscription(superBranch1, submoduleBranch2, TEST_PATH));
assertThat(subscriptionGraph.getSubscriptions(superBranch2))
.containsExactly(new SubmoduleSubscription(superBranch2, submoduleBranch1, TEST_PATH));
assertThat(subscriptionGraph.hasSuperproject(submoduleBranch1)).isTrue();
assertThat(subscriptionGraph.hasSuperproject(submoduleBranch2)).isTrue();
assertThat(subscriptionGraph.hasSuperproject(submoduleBranch3)).isFalse();
assertThat(subscriptionGraph.getSortedSuperprojectAndSubmoduleBranches())
.containsExactly(submoduleBranch2, submoduleBranch1, superBranch2, superBranch1)
.inOrder();
}
private TestRepository<Repository> createRepo(Project.NameKey project) throws Exception {
Repository repo = repoManager.createRepository(project);
return new TestRepository<>(repo);
}
private void createBranch(TestRepository<Repository> repo, BranchNameKey branch, RevCommit commit)
throws Exception {
repo.update(branch.branch(), commit);
}
private void allowSubscription(BranchNameKey branch) {
SubscribeSection.Builder s = SubscribeSection.builder(branch.project());
s.addMultiMatchRefSpec("refs/heads/*:refs/heads/*");
when(mockProjectState.getSubscribeSections(branch)).thenReturn(ImmutableSet.of(s.build()));
}
private void setSubscription(
BranchNameKey submoduleBranch, List<BranchNameKey> superprojectBranches) {
List<SubmoduleSubscription> subscriptions =
superprojectBranches.stream()
.map(
(targetBranch) ->
new SubmoduleSubscription(targetBranch, submoduleBranch, TEST_PATH))
.collect(Collectors.toList());
GitModules mockGitModules = mock(GitModules.class);
when(mockGitModules.subscribedTo(submoduleBranch)).thenReturn(subscriptions);
when(mockGitModulesFactory.create(submoduleBranch, mergeOpRepoManager))
.thenReturn(mockGitModules);
}
}