1191 lines
42 KiB
Java
1191 lines
42 KiB
Java
// 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;
|
|
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
import static com.google.common.truth.TruthJUnit.assume;
|
|
import static com.google.gerrit.acceptance.GitUtil.initSsh;
|
|
import static com.google.gerrit.extensions.api.changes.SubmittedTogetherOption.NON_VISIBLE_CHANGES;
|
|
import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
|
|
import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
|
|
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
|
import static java.util.stream.Collectors.toList;
|
|
import static org.eclipse.jgit.lib.Constants.HEAD;
|
|
import static org.eclipse.jgit.lib.Constants.R_TAGS;
|
|
|
|
import com.google.common.base.Strings;
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.ImmutableMap;
|
|
import com.google.common.collect.Iterables;
|
|
import com.google.common.collect.Iterators;
|
|
import com.google.common.collect.Sets;
|
|
import com.google.common.primitives.Chars;
|
|
import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context;
|
|
import com.google.gerrit.common.Nullable;
|
|
import com.google.gerrit.common.data.AccessSection;
|
|
import com.google.gerrit.common.data.ContributorAgreement;
|
|
import com.google.gerrit.common.data.GroupReference;
|
|
import com.google.gerrit.common.data.Permission;
|
|
import com.google.gerrit.common.data.PermissionRule;
|
|
import com.google.gerrit.extensions.api.GerritApi;
|
|
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
|
import com.google.gerrit.extensions.api.changes.RevisionApi;
|
|
import com.google.gerrit.extensions.api.changes.SubmittedTogetherInfo;
|
|
import com.google.gerrit.extensions.api.groups.GroupApi;
|
|
import com.google.gerrit.extensions.api.groups.GroupInput;
|
|
import com.google.gerrit.extensions.api.projects.BranchApi;
|
|
import com.google.gerrit.extensions.api.projects.BranchInput;
|
|
import com.google.gerrit.extensions.api.projects.ProjectInput;
|
|
import com.google.gerrit.extensions.client.InheritableBoolean;
|
|
import com.google.gerrit.extensions.client.ListChangesOption;
|
|
import com.google.gerrit.extensions.client.ProjectWatchInfo;
|
|
import com.google.gerrit.extensions.client.SubmitType;
|
|
import com.google.gerrit.extensions.common.ActionInfo;
|
|
import com.google.gerrit.extensions.common.ChangeInfo;
|
|
import com.google.gerrit.extensions.common.ChangeType;
|
|
import com.google.gerrit.extensions.common.DiffInfo;
|
|
import com.google.gerrit.extensions.common.EditInfo;
|
|
import com.google.gerrit.extensions.restapi.BinaryResult;
|
|
import com.google.gerrit.extensions.restapi.IdString;
|
|
import com.google.gerrit.extensions.restapi.RestApiException;
|
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
|
import com.google.gerrit.reviewdb.client.Branch;
|
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
|
import com.google.gerrit.reviewdb.client.Project;
|
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
|
import com.google.gerrit.server.AnonymousUser;
|
|
import com.google.gerrit.server.ChangeFinder;
|
|
import com.google.gerrit.server.GerritPersonIdent;
|
|
import com.google.gerrit.server.IdentifiedUser;
|
|
import com.google.gerrit.server.OutputFormat;
|
|
import com.google.gerrit.server.PatchSetUtil;
|
|
import com.google.gerrit.server.account.AccountCache;
|
|
import com.google.gerrit.server.account.GroupCache;
|
|
import com.google.gerrit.server.change.Abandon;
|
|
import com.google.gerrit.server.change.ChangeResource;
|
|
import com.google.gerrit.server.change.FileContentUtil;
|
|
import com.google.gerrit.server.change.RevisionResource;
|
|
import com.google.gerrit.server.change.Revisions;
|
|
import com.google.gerrit.server.config.AllProjectsName;
|
|
import com.google.gerrit.server.config.CanonicalWebUrl;
|
|
import com.google.gerrit.server.config.GerritServerConfig;
|
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
|
import com.google.gerrit.server.git.ProjectConfig;
|
|
import com.google.gerrit.server.group.SystemGroupBackend;
|
|
import com.google.gerrit.server.index.change.ChangeIndex;
|
|
import com.google.gerrit.server.index.change.ChangeIndexCollection;
|
|
import com.google.gerrit.server.index.change.ChangeIndexer;
|
|
import com.google.gerrit.server.mail.send.EmailHeader;
|
|
import com.google.gerrit.server.notedb.ChangeNoteUtil;
|
|
import com.google.gerrit.server.notedb.ChangeNotes;
|
|
import com.google.gerrit.server.project.ChangeControl;
|
|
import com.google.gerrit.server.project.ProjectCache;
|
|
import com.google.gerrit.server.project.Util;
|
|
import com.google.gerrit.server.query.change.ChangeData;
|
|
import com.google.gerrit.server.query.change.InternalChangeQuery;
|
|
import com.google.gerrit.testutil.ConfigSuite;
|
|
import com.google.gerrit.testutil.FakeEmailSender;
|
|
import com.google.gerrit.testutil.FakeEmailSender.Message;
|
|
import com.google.gerrit.testutil.SshMode;
|
|
import com.google.gerrit.testutil.TempFileUtil;
|
|
import com.google.gerrit.testutil.TestNotesMigration;
|
|
import com.google.gson.Gson;
|
|
import com.google.gwtorm.server.OrmException;
|
|
import com.google.gwtorm.server.SchemaFactory;
|
|
import com.google.inject.Inject;
|
|
import com.google.inject.Provider;
|
|
import java.io.File;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.EnumSet;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.regex.Pattern;
|
|
import java.util.zip.ZipEntry;
|
|
import java.util.zip.ZipFile;
|
|
import org.eclipse.jgit.api.Git;
|
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
|
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
|
import org.eclipse.jgit.junit.TestRepository;
|
|
import org.eclipse.jgit.lib.Config;
|
|
import org.eclipse.jgit.lib.Constants;
|
|
import org.eclipse.jgit.lib.NullProgressMonitor;
|
|
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.RevTree;
|
|
import org.eclipse.jgit.revwalk.RevWalk;
|
|
import org.eclipse.jgit.transport.FetchResult;
|
|
import org.eclipse.jgit.transport.RefSpec;
|
|
import org.eclipse.jgit.transport.Transport;
|
|
import org.eclipse.jgit.transport.TransportBundleStream;
|
|
import org.eclipse.jgit.transport.URIish;
|
|
import org.junit.After;
|
|
import org.junit.AfterClass;
|
|
import org.junit.Before;
|
|
import org.junit.Rule;
|
|
import org.junit.rules.ExpectedException;
|
|
import org.junit.rules.TemporaryFolder;
|
|
import org.junit.rules.TestRule;
|
|
import org.junit.runner.Description;
|
|
import org.junit.runner.RunWith;
|
|
import org.junit.runners.model.Statement;
|
|
|
|
@RunWith(ConfigSuite.class)
|
|
public abstract class AbstractDaemonTest {
|
|
private static GerritServer commonServer;
|
|
|
|
@ConfigSuite.Parameter public Config baseConfig;
|
|
|
|
@ConfigSuite.Name private String configName;
|
|
|
|
@Inject protected AllProjectsName allProjects;
|
|
|
|
@Inject protected AccountCreator accounts;
|
|
|
|
@Inject private SchemaFactory<ReviewDb> reviewDbProvider;
|
|
|
|
@Inject protected GerritApi gApi;
|
|
|
|
@Inject protected AcceptanceTestRequestScope atrScope;
|
|
|
|
@Inject protected AccountCache accountCache;
|
|
|
|
@Inject protected IdentifiedUser.GenericFactory identifiedUserFactory;
|
|
|
|
@Inject protected PushOneCommit.Factory pushFactory;
|
|
|
|
@Inject protected MetaDataUpdate.Server metaDataUpdateFactory;
|
|
|
|
@Inject protected ProjectCache projectCache;
|
|
|
|
@Inject protected GroupCache groupCache;
|
|
|
|
@Inject protected GitRepositoryManager repoManager;
|
|
|
|
@Inject protected ChangeIndexer indexer;
|
|
|
|
@Inject protected Provider<InternalChangeQuery> queryProvider;
|
|
|
|
@Inject @CanonicalWebUrl protected Provider<String> canonicalWebUrl;
|
|
|
|
@Inject @GerritServerConfig protected Config cfg;
|
|
|
|
@Inject private InProcessProtocol inProcessProtocol;
|
|
|
|
@Inject private Provider<AnonymousUser> anonymousUser;
|
|
|
|
@Inject @GerritPersonIdent protected Provider<PersonIdent> serverIdent;
|
|
|
|
@Inject protected ChangeData.Factory changeDataFactory;
|
|
|
|
@Inject protected PatchSetUtil psUtil;
|
|
|
|
@Inject protected ChangeFinder changeFinder;
|
|
|
|
@Inject protected Revisions revisions;
|
|
|
|
@Inject protected FakeEmailSender sender;
|
|
|
|
@Inject protected ChangeNoteUtil changeNoteUtil;
|
|
|
|
@Inject protected ChangeResource.Factory changeResourceFactory;
|
|
|
|
@Inject protected SystemGroupBackend systemGroupBackend;
|
|
|
|
@Inject private EventRecorder.Factory eventRecorderFactory;
|
|
|
|
@Inject private ChangeIndexCollection changeIndexes;
|
|
|
|
protected TestRepository<InMemoryRepository> testRepo;
|
|
protected GerritServer server;
|
|
protected TestAccount admin;
|
|
protected TestAccount user;
|
|
protected RestSession adminRestSession;
|
|
protected RestSession userRestSession;
|
|
protected SshSession adminSshSession;
|
|
protected SshSession userSshSession;
|
|
protected ReviewDb db;
|
|
protected Project.NameKey project;
|
|
protected EventRecorder eventRecorder;
|
|
|
|
@Inject protected TestNotesMigration notesMigration;
|
|
|
|
@Inject protected ChangeNotes.Factory notesFactory;
|
|
|
|
@Inject protected Abandon changeAbandoner;
|
|
|
|
@Rule public ExpectedException exception = ExpectedException.none();
|
|
|
|
private String resourcePrefix;
|
|
private List<Repository> toClose;
|
|
private boolean useSsh;
|
|
|
|
@Rule
|
|
public TestRule testRunner =
|
|
new TestRule() {
|
|
@Override
|
|
public Statement apply(final Statement base, final Description description) {
|
|
return new Statement() {
|
|
@Override
|
|
public void evaluate() throws Throwable {
|
|
beforeTest(description);
|
|
try {
|
|
base.evaluate();
|
|
} finally {
|
|
afterTest();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
@Rule public TemporaryFolder tempSiteDir = new TemporaryFolder();
|
|
|
|
@Before
|
|
public void clearSender() {
|
|
sender.clear();
|
|
}
|
|
|
|
@Before
|
|
public void startEventRecorder() {
|
|
eventRecorder = eventRecorderFactory.create(admin);
|
|
}
|
|
|
|
@Before
|
|
public void assumeSshIfRequired() {
|
|
if (useSsh) {
|
|
// If the test uses ssh, we use assume() to make sure ssh is enabled on
|
|
// the test suite. JUnit will skip tests annotated with @UseSsh if we
|
|
// disable them using the command line flag.
|
|
assume().that(SshMode.useSsh()).isTrue();
|
|
}
|
|
}
|
|
|
|
@After
|
|
public void closeEventRecorder() {
|
|
eventRecorder.close();
|
|
}
|
|
|
|
@AfterClass
|
|
public static void stopCommonServer() throws Exception {
|
|
if (commonServer != null) {
|
|
try {
|
|
commonServer.stop();
|
|
} finally {
|
|
commonServer = null;
|
|
}
|
|
}
|
|
TempFileUtil.cleanup();
|
|
}
|
|
|
|
protected static Config submitWholeTopicEnabledConfig() {
|
|
Config cfg = new Config();
|
|
cfg.setBoolean("change", null, "submitWholeTopic", true);
|
|
return cfg;
|
|
}
|
|
|
|
protected static Config allowDraftsDisabledConfig() {
|
|
Config cfg = new Config();
|
|
cfg.setBoolean("change", null, "allowDrafts", false);
|
|
return cfg;
|
|
}
|
|
|
|
protected boolean isAllowDrafts() {
|
|
return cfg.getBoolean("change", "allowDrafts", true);
|
|
}
|
|
|
|
protected boolean isSubmitWholeTopicEnabled() {
|
|
return cfg.getBoolean("change", null, "submitWholeTopic", false);
|
|
}
|
|
|
|
protected boolean isContributorAgreementsEnabled() {
|
|
return cfg.getBoolean("auth", null, "contributorAgreements", false);
|
|
}
|
|
|
|
protected void beforeTest(Description description) throws Exception {
|
|
GerritServer.Description classDesc =
|
|
GerritServer.Description.forTestClass(description, configName);
|
|
GerritServer.Description methodDesc =
|
|
GerritServer.Description.forTestMethod(description, configName);
|
|
|
|
baseConfig.setString("gerrit", null, "tempSiteDir", tempSiteDir.getRoot().getPath());
|
|
baseConfig.setInt("receive", null, "changeUpdateThreads", 4);
|
|
if (classDesc.equals(methodDesc) && !classDesc.sandboxed() && !methodDesc.sandboxed()) {
|
|
if (commonServer == null) {
|
|
commonServer = GerritServer.start(classDesc, baseConfig);
|
|
}
|
|
server = commonServer;
|
|
} else {
|
|
server = GerritServer.start(methodDesc, baseConfig);
|
|
}
|
|
|
|
server.getTestInjector().injectMembers(this);
|
|
notesMigration.setFromEnv();
|
|
Transport.register(inProcessProtocol);
|
|
toClose = Collections.synchronizedList(new ArrayList<Repository>());
|
|
admin = accounts.admin();
|
|
user = accounts.user();
|
|
|
|
// Evict cached user state in case tests modify it.
|
|
accountCache.evict(admin.getId());
|
|
accountCache.evict(user.getId());
|
|
|
|
adminRestSession = new RestSession(server, admin);
|
|
userRestSession = new RestSession(server, user);
|
|
|
|
db = reviewDbProvider.open();
|
|
|
|
if (classDesc.useSsh() || methodDesc.useSsh()) {
|
|
useSsh = true;
|
|
if (SshMode.useSsh() && (adminSshSession == null || userSshSession == null)) {
|
|
// Create Ssh sessions
|
|
initSsh(admin);
|
|
Context ctx = newRequestContext(user);
|
|
atrScope.set(ctx);
|
|
userSshSession = ctx.getSession();
|
|
userSshSession.open();
|
|
ctx = newRequestContext(admin);
|
|
atrScope.set(ctx);
|
|
adminSshSession = ctx.getSession();
|
|
adminSshSession.open();
|
|
}
|
|
} else {
|
|
useSsh = false;
|
|
}
|
|
|
|
resourcePrefix =
|
|
UNSAFE_PROJECT_NAME
|
|
.matcher(description.getClassName() + "_" + description.getMethodName() + "_")
|
|
.replaceAll("");
|
|
|
|
Context ctx = newRequestContext(admin);
|
|
atrScope.set(ctx);
|
|
project = createProject(projectInput(description));
|
|
testRepo = cloneProject(project, getCloneAsAccount(description));
|
|
}
|
|
|
|
private TestAccount getCloneAsAccount(Description description) {
|
|
TestProjectInput ann = description.getAnnotation(TestProjectInput.class);
|
|
return accounts.get(ann != null ? ann.cloneAs() : "admin");
|
|
}
|
|
|
|
private ProjectInput projectInput(Description description) {
|
|
ProjectInput in = new ProjectInput();
|
|
TestProjectInput ann = description.getAnnotation(TestProjectInput.class);
|
|
in.name = name("project");
|
|
if (ann != null) {
|
|
in.parent = Strings.emptyToNull(ann.parent());
|
|
in.description = Strings.emptyToNull(ann.description());
|
|
in.createEmptyCommit = ann.createEmptyCommit();
|
|
in.submitType = ann.submitType();
|
|
in.useContentMerge = ann.useContributorAgreements();
|
|
in.useSignedOffBy = ann.useSignedOffBy();
|
|
in.useContentMerge = ann.useContentMerge();
|
|
} else {
|
|
// Defaults should match TestProjectConfig, omitting nullable values.
|
|
in.createEmptyCommit = true;
|
|
}
|
|
updateProjectInput(in);
|
|
return in;
|
|
}
|
|
|
|
private static final Pattern UNSAFE_PROJECT_NAME = Pattern.compile("[^a-zA-Z0-9._/-]+");
|
|
|
|
protected Git git() {
|
|
return testRepo.git();
|
|
}
|
|
|
|
protected InMemoryRepository repo() {
|
|
return testRepo.getRepository();
|
|
}
|
|
|
|
/**
|
|
* Return a resource name scoped to this test method.
|
|
*
|
|
* <p>Test methods in a single class by default share a running server. For any resource name you
|
|
* require to be unique to a test method, wrap it in a call to this method.
|
|
*
|
|
* @param name resource name (group, project, topic, etc.)
|
|
* @return name prefixed by a string unique to this test method.
|
|
*/
|
|
protected String name(String name) {
|
|
return resourcePrefix + name;
|
|
}
|
|
|
|
protected Project.NameKey createProject(String nameSuffix) throws RestApiException {
|
|
return createProject(nameSuffix, null);
|
|
}
|
|
|
|
protected Project.NameKey createProject(String nameSuffix, Project.NameKey parent)
|
|
throws RestApiException {
|
|
// Default for createEmptyCommit should match TestProjectConfig.
|
|
return createProject(nameSuffix, parent, true, null);
|
|
}
|
|
|
|
protected Project.NameKey createProject(
|
|
String nameSuffix, Project.NameKey parent, boolean createEmptyCommit)
|
|
throws RestApiException {
|
|
// Default for createEmptyCommit should match TestProjectConfig.
|
|
return createProject(nameSuffix, parent, createEmptyCommit, null);
|
|
}
|
|
|
|
protected Project.NameKey createProject(
|
|
String nameSuffix, Project.NameKey parent, SubmitType submitType) throws RestApiException {
|
|
// Default for createEmptyCommit should match TestProjectConfig.
|
|
return createProject(nameSuffix, parent, true, submitType);
|
|
}
|
|
|
|
protected Project.NameKey createProject(
|
|
String nameSuffix, Project.NameKey parent, boolean createEmptyCommit, SubmitType submitType)
|
|
throws RestApiException {
|
|
ProjectInput in = new ProjectInput();
|
|
in.name = name(nameSuffix);
|
|
in.parent = parent != null ? parent.get() : null;
|
|
in.submitType = submitType;
|
|
in.createEmptyCommit = createEmptyCommit;
|
|
return createProject(in);
|
|
}
|
|
|
|
private Project.NameKey createProject(ProjectInput in) throws RestApiException {
|
|
gApi.projects().create(in);
|
|
return new Project.NameKey(in.name);
|
|
}
|
|
|
|
/**
|
|
* Modify a project input before creating the initial test project.
|
|
*
|
|
* @param in input; may be modified in place.
|
|
*/
|
|
protected void updateProjectInput(ProjectInput in) {
|
|
// Default implementation does nothing.
|
|
}
|
|
|
|
protected TestRepository<InMemoryRepository> cloneProject(Project.NameKey p) throws Exception {
|
|
return cloneProject(p, admin);
|
|
}
|
|
|
|
protected TestRepository<InMemoryRepository> cloneProject(
|
|
Project.NameKey p, TestAccount testAccount) throws Exception {
|
|
InProcessProtocol.Context ctx =
|
|
new InProcessProtocol.Context(
|
|
reviewDbProvider, identifiedUserFactory, testAccount.getId(), p);
|
|
Repository repo = repoManager.openRepository(p);
|
|
toClose.add(repo);
|
|
return GitUtil.cloneProject(p, inProcessProtocol.register(ctx, repo).toString());
|
|
}
|
|
|
|
private void afterTest() throws Exception {
|
|
Transport.unregister(inProcessProtocol);
|
|
for (Repository repo : toClose) {
|
|
repo.close();
|
|
}
|
|
db.close();
|
|
if (adminSshSession != null) {
|
|
adminSshSession.close();
|
|
}
|
|
if (userSshSession != null) {
|
|
userSshSession.close();
|
|
}
|
|
if (server != commonServer) {
|
|
server.stop();
|
|
}
|
|
}
|
|
|
|
protected TestRepository<?>.CommitBuilder commitBuilder() throws Exception {
|
|
return testRepo.branch("HEAD").commit().insertChangeId();
|
|
}
|
|
|
|
protected TestRepository<?>.CommitBuilder amendBuilder() throws Exception {
|
|
ObjectId head = repo().exactRef("HEAD").getObjectId();
|
|
TestRepository<?>.CommitBuilder b = testRepo.amendRef("HEAD");
|
|
Optional<String> id = GitUtil.getChangeId(testRepo, head);
|
|
// TestRepository behaves like "git commit --amend -m foo", which does not
|
|
// preserve an existing Change-Id. Tests probably want this.
|
|
if (id.isPresent()) {
|
|
b.insertChangeId(id.get().substring(1));
|
|
} else {
|
|
b.insertChangeId();
|
|
}
|
|
return b;
|
|
}
|
|
|
|
protected PushOneCommit.Result createChange() throws Exception {
|
|
return createChange("refs/for/master");
|
|
}
|
|
|
|
protected PushOneCommit.Result createChange(String ref) throws Exception {
|
|
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
|
|
PushOneCommit.Result result = push.to(ref);
|
|
result.assertOkStatus();
|
|
return result;
|
|
}
|
|
|
|
protected PushOneCommit.Result createMergeCommitChange(String ref) throws Exception {
|
|
return createMergeCommitChange(ref, "foo");
|
|
}
|
|
|
|
protected PushOneCommit.Result createMergeCommitChange(String ref, String file) throws Exception {
|
|
ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
|
|
|
|
PushOneCommit.Result p1 =
|
|
pushFactory
|
|
.create(
|
|
db,
|
|
admin.getIdent(),
|
|
testRepo,
|
|
"parent 1",
|
|
ImmutableMap.of(file, "foo-1", "bar", "bar-1"))
|
|
.to(ref);
|
|
|
|
// reset HEAD in order to create a sibling of the first change
|
|
testRepo.reset(initial);
|
|
|
|
PushOneCommit.Result p2 =
|
|
pushFactory
|
|
.create(
|
|
db,
|
|
admin.getIdent(),
|
|
testRepo,
|
|
"parent 2",
|
|
ImmutableMap.of(file, "foo-2", "bar", "bar-2"))
|
|
.to(ref);
|
|
|
|
PushOneCommit m =
|
|
pushFactory.create(
|
|
db,
|
|
admin.getIdent(),
|
|
testRepo,
|
|
"merge",
|
|
ImmutableMap.of(file, "foo-1", "bar", "bar-2"));
|
|
m.setParents(ImmutableList.of(p1.getCommit(), p2.getCommit()));
|
|
PushOneCommit.Result result = m.to(ref);
|
|
result.assertOkStatus();
|
|
return result;
|
|
}
|
|
|
|
protected PushOneCommit.Result createDraftChange() throws Exception {
|
|
return pushTo("refs/drafts/master");
|
|
}
|
|
|
|
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/" + name(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 BranchApi createBranch(Branch.NameKey branch) throws Exception {
|
|
return gApi.projects()
|
|
.name(branch.getParentKey().get())
|
|
.branch(branch.get())
|
|
.create(new BranchInput());
|
|
}
|
|
|
|
protected BranchApi createBranchWithRevision(Branch.NameKey branch, String revision)
|
|
throws Exception {
|
|
BranchInput in = new BranchInput();
|
|
in.revision = revision;
|
|
return gApi.projects().name(branch.getParentKey().get()).branch(branch.get()).create(in);
|
|
}
|
|
|
|
private static final List<Character> RANDOM =
|
|
Chars.asList(new char[] {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'});
|
|
|
|
protected PushOneCommit.Result amendChange(String changeId) throws Exception {
|
|
return amendChange(changeId, "refs/for/master");
|
|
}
|
|
|
|
protected PushOneCommit.Result amendChange(String changeId, String ref) throws Exception {
|
|
return amendChange(changeId, ref, admin, testRepo);
|
|
}
|
|
|
|
protected PushOneCommit.Result amendChange(
|
|
String changeId, String ref, TestAccount testAccount, TestRepository<?> repo)
|
|
throws Exception {
|
|
Collections.shuffle(RANDOM);
|
|
PushOneCommit push =
|
|
pushFactory.create(
|
|
db,
|
|
testAccount.getIdent(),
|
|
repo,
|
|
PushOneCommit.SUBJECT,
|
|
PushOneCommit.FILE_NAME,
|
|
new String(Chars.toArray(RANDOM)),
|
|
changeId);
|
|
return push.to(ref);
|
|
}
|
|
|
|
protected void merge(PushOneCommit.Result r) throws Exception {
|
|
revision(r).review(ReviewInput.approve());
|
|
revision(r).submit();
|
|
}
|
|
|
|
protected PushOneCommit.Result amendChangeAsDraft(String changeId) throws Exception {
|
|
return amendChange(changeId, "refs/drafts/master");
|
|
}
|
|
|
|
protected ChangeInfo info(String id) throws RestApiException {
|
|
return gApi.changes().id(id).info();
|
|
}
|
|
|
|
protected ChangeInfo get(String id) throws RestApiException {
|
|
return gApi.changes().id(id).get();
|
|
}
|
|
|
|
protected Optional<EditInfo> getEdit(String id) throws RestApiException {
|
|
return gApi.changes().id(id).edit().get();
|
|
}
|
|
|
|
protected ChangeInfo get(String id, ListChangesOption... options) throws RestApiException {
|
|
return gApi.changes()
|
|
.id(id)
|
|
.get(Sets.newEnumSet(Arrays.asList(options), ListChangesOption.class));
|
|
}
|
|
|
|
protected List<ChangeInfo> query(String q) throws RestApiException {
|
|
return gApi.changes().query(q).get();
|
|
}
|
|
|
|
private Context newRequestContext(TestAccount account) {
|
|
return atrScope.newContext(
|
|
reviewDbProvider,
|
|
new SshSession(server, account),
|
|
identifiedUserFactory.create(account.getId()));
|
|
}
|
|
|
|
protected Context setApiUser(TestAccount account) {
|
|
return atrScope.set(newRequestContext(account));
|
|
}
|
|
|
|
protected Context setApiUserAnonymous() {
|
|
return atrScope.set(atrScope.newContext(reviewDbProvider, null, anonymousUser.get()));
|
|
}
|
|
|
|
protected Context disableDb() {
|
|
notesMigration.setFailOnLoad(true);
|
|
return atrScope.disableDb();
|
|
}
|
|
|
|
protected void enableDb(Context preDisableContext) {
|
|
notesMigration.setFailOnLoad(false);
|
|
atrScope.set(preDisableContext);
|
|
}
|
|
|
|
protected void disableChangeIndexWrites() {
|
|
for (ChangeIndex i : changeIndexes.getWriteIndexes()) {
|
|
if (!(i instanceof ReadOnlyChangeIndex)) {
|
|
changeIndexes.addWriteIndex(new ReadOnlyChangeIndex(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void enableChangeIndexWrites() {
|
|
for (ChangeIndex i : changeIndexes.getWriteIndexes()) {
|
|
if (i instanceof ReadOnlyChangeIndex) {
|
|
changeIndexes.addWriteIndex(((ReadOnlyChangeIndex) i).unwrap());
|
|
}
|
|
}
|
|
}
|
|
|
|
protected static Gson newGson() {
|
|
return OutputFormat.JSON_COMPACT.newGson();
|
|
}
|
|
|
|
protected RevisionApi revision(PushOneCommit.Result r) throws Exception {
|
|
return gApi.changes().id(r.getChangeId()).current();
|
|
}
|
|
|
|
protected void allow(String permission, AccountGroup.UUID id, String ref) throws Exception {
|
|
ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
|
|
Util.allow(cfg, permission, id, ref);
|
|
saveProjectConfig(project, cfg);
|
|
}
|
|
|
|
protected void allowGlobalCapabilities(AccountGroup.UUID id, String... capabilityNames)
|
|
throws Exception {
|
|
allowGlobalCapabilities(id, Arrays.asList(capabilityNames));
|
|
}
|
|
|
|
protected void allowGlobalCapabilities(AccountGroup.UUID id, Iterable<String> capabilityNames)
|
|
throws Exception {
|
|
ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig();
|
|
for (String capabilityName : capabilityNames) {
|
|
Util.allow(cfg, capabilityName, id);
|
|
}
|
|
saveProjectConfig(allProjects, cfg);
|
|
}
|
|
|
|
protected void removeGlobalCapabilities(AccountGroup.UUID id, String... capabilityNames)
|
|
throws Exception {
|
|
removeGlobalCapabilities(id, Arrays.asList(capabilityNames));
|
|
}
|
|
|
|
protected void removeGlobalCapabilities(AccountGroup.UUID id, Iterable<String> capabilityNames)
|
|
throws Exception {
|
|
ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig();
|
|
for (String capabilityName : capabilityNames) {
|
|
Util.remove(cfg, capabilityName, id);
|
|
}
|
|
saveProjectConfig(allProjects, cfg);
|
|
}
|
|
|
|
protected void setUseContributorAgreements(InheritableBoolean value) throws Exception {
|
|
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
|
|
ProjectConfig config = ProjectConfig.read(md);
|
|
config.getProject().setUseContributorAgreements(value);
|
|
config.commit(md);
|
|
projectCache.evict(config.getProject());
|
|
}
|
|
}
|
|
|
|
protected void setUseSignedOffBy(InheritableBoolean value) throws Exception {
|
|
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
|
|
ProjectConfig config = ProjectConfig.read(md);
|
|
config.getProject().setUseSignedOffBy(value);
|
|
config.commit(md);
|
|
projectCache.evict(config.getProject());
|
|
}
|
|
}
|
|
|
|
protected void deny(String permission, AccountGroup.UUID id, String ref) throws Exception {
|
|
deny(project, permission, id, ref);
|
|
}
|
|
|
|
protected void deny(Project.NameKey p, String permission, AccountGroup.UUID id, String ref)
|
|
throws Exception {
|
|
ProjectConfig cfg = projectCache.checkedGet(p).getConfig();
|
|
Util.deny(cfg, permission, id, ref);
|
|
saveProjectConfig(p, cfg);
|
|
}
|
|
|
|
protected PermissionRule block(String permission, AccountGroup.UUID id, String ref)
|
|
throws Exception {
|
|
return block(permission, id, ref, project);
|
|
}
|
|
|
|
protected PermissionRule block(
|
|
String permission, AccountGroup.UUID id, String ref, Project.NameKey project)
|
|
throws Exception {
|
|
ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
|
|
PermissionRule rule = Util.block(cfg, permission, id, ref);
|
|
saveProjectConfig(project, cfg);
|
|
return rule;
|
|
}
|
|
|
|
protected void saveProjectConfig(Project.NameKey p, ProjectConfig cfg) throws Exception {
|
|
try (MetaDataUpdate md = metaDataUpdateFactory.create(p)) {
|
|
md.setAuthor(identifiedUserFactory.create(admin.getId()));
|
|
cfg.commit(md);
|
|
}
|
|
projectCache.evict(cfg.getProject());
|
|
}
|
|
|
|
protected void saveProjectConfig(ProjectConfig cfg) throws Exception {
|
|
saveProjectConfig(project, cfg);
|
|
}
|
|
|
|
protected void grant(String permission, Project.NameKey project, String ref)
|
|
throws RepositoryNotFoundException, IOException, ConfigInvalidException {
|
|
grant(permission, project, ref, false);
|
|
}
|
|
|
|
protected void grant(String permission, Project.NameKey project, String ref, boolean force)
|
|
throws RepositoryNotFoundException, IOException, ConfigInvalidException {
|
|
AccountGroup adminGroup = groupCache.get(new AccountGroup.NameKey("Administrators"));
|
|
grant(permission, project, ref, force, adminGroup.getGroupUUID());
|
|
}
|
|
|
|
protected void grant(
|
|
String permission,
|
|
Project.NameKey project,
|
|
String ref,
|
|
boolean force,
|
|
AccountGroup.UUID groupUUID)
|
|
throws RepositoryNotFoundException, IOException, ConfigInvalidException {
|
|
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
|
|
md.setMessage(String.format("Grant %s on %s", permission, ref));
|
|
ProjectConfig config = ProjectConfig.read(md);
|
|
AccessSection s = config.getAccessSection(ref, true);
|
|
Permission p = s.getPermission(permission, true);
|
|
PermissionRule rule = Util.newRule(config, groupUUID);
|
|
rule.setForce(force);
|
|
p.add(rule);
|
|
config.commit(md);
|
|
projectCache.evict(config.getProject());
|
|
}
|
|
}
|
|
|
|
protected void removePermission(String permission, Project.NameKey project, String ref)
|
|
throws IOException, ConfigInvalidException {
|
|
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
|
|
md.setMessage(String.format("Remove %s on %s", permission, ref));
|
|
ProjectConfig config = ProjectConfig.read(md);
|
|
AccessSection s = config.getAccessSection(ref, true);
|
|
Permission p = s.getPermission(permission, true);
|
|
p.getRules().clear();
|
|
config.commit(md);
|
|
projectCache.evict(config.getProject());
|
|
}
|
|
}
|
|
|
|
protected void blockRead(String ref) throws Exception {
|
|
block(Permission.READ, REGISTERED_USERS, ref);
|
|
}
|
|
|
|
protected void blockForgeCommitter(Project.NameKey project, String ref) throws Exception {
|
|
ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
|
|
Util.block(cfg, Permission.FORGE_COMMITTER, REGISTERED_USERS, ref);
|
|
saveProjectConfig(project, cfg);
|
|
}
|
|
|
|
protected PushOneCommit.Result pushTo(String ref) throws Exception {
|
|
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
|
|
return push.to(ref);
|
|
}
|
|
|
|
protected void approve(String id) throws Exception {
|
|
gApi.changes().id(id).revision("current").review(ReviewInput.approve());
|
|
}
|
|
|
|
protected void recommend(String id) throws Exception {
|
|
gApi.changes().id(id).revision("current").review(ReviewInput.recommend());
|
|
}
|
|
|
|
protected Map<String, ActionInfo> getActions(String id) throws Exception {
|
|
return gApi.changes().id(id).revision(1).actions();
|
|
}
|
|
|
|
protected String getETag(String id) throws Exception {
|
|
return gApi.changes().id(id).current().etag();
|
|
}
|
|
|
|
private static Iterable<String> changeIds(Iterable<ChangeInfo> changes) {
|
|
return Iterables.transform(changes, i -> i.changeId);
|
|
}
|
|
|
|
protected void assertSubmittedTogether(String chId, String... expected) throws Exception {
|
|
List<ChangeInfo> actual = gApi.changes().id(chId).submittedTogether();
|
|
SubmittedTogetherInfo info =
|
|
gApi.changes().id(chId).submittedTogether(EnumSet.of(NON_VISIBLE_CHANGES));
|
|
|
|
assertThat(info.nonVisibleChanges).isEqualTo(0);
|
|
assertThat(actual).hasSize(expected.length);
|
|
assertThat(changeIds(actual)).containsExactly((Object[]) expected).inOrder();
|
|
assertThat(changeIds(info.changes)).containsExactly((Object[]) expected).inOrder();
|
|
}
|
|
|
|
protected PatchSet getPatchSet(PatchSet.Id psId) throws OrmException {
|
|
return changeDataFactory.create(db, project, psId.getParentKey()).patchSet(psId);
|
|
}
|
|
|
|
protected IdentifiedUser user(TestAccount testAccount) {
|
|
return identifiedUserFactory.create(testAccount.getId());
|
|
}
|
|
|
|
protected RevisionResource parseCurrentRevisionResource(String changeId) throws Exception {
|
|
ChangeResource cr = parseChangeResource(changeId);
|
|
int psId = cr.getChange().currentPatchSetId().get();
|
|
return revisions.parse(cr, IdString.fromDecoded(Integer.toString(psId)));
|
|
}
|
|
|
|
protected RevisionResource parseRevisionResource(String changeId, int n) throws Exception {
|
|
return revisions.parse(
|
|
parseChangeResource(changeId), IdString.fromDecoded(Integer.toString(n)));
|
|
}
|
|
|
|
protected RevisionResource parseRevisionResource(PushOneCommit.Result r) throws Exception {
|
|
PatchSet.Id psId = r.getPatchSetId();
|
|
return parseRevisionResource(psId.getParentKey().toString(), psId.get());
|
|
}
|
|
|
|
protected ChangeResource parseChangeResource(String changeId) throws Exception {
|
|
List<ChangeControl> ctls = changeFinder.find(changeId, atrScope.get().getUser());
|
|
assertThat(ctls).hasSize(1);
|
|
return changeResourceFactory.create(ctls.get(0));
|
|
}
|
|
|
|
protected String createGroup(String name) throws Exception {
|
|
return createGroup(name, "Administrators");
|
|
}
|
|
|
|
protected String createGroup(String name, String owner) throws Exception {
|
|
name = name(name);
|
|
GroupInput in = new GroupInput();
|
|
in.name = name;
|
|
in.ownerId = owner;
|
|
gApi.groups().create(in);
|
|
return name;
|
|
}
|
|
|
|
protected RevCommit getHead(Repository repo, String name) throws Exception {
|
|
try (RevWalk rw = new RevWalk(repo)) {
|
|
Ref r = repo.exactRef(name);
|
|
return r != null ? rw.parseCommit(r.getObjectId()) : null;
|
|
}
|
|
}
|
|
|
|
protected RevCommit getHead(Repository repo) throws Exception {
|
|
return getHead(repo, "HEAD");
|
|
}
|
|
|
|
protected RevCommit getRemoteHead(Project.NameKey project, String branch) throws Exception {
|
|
try (Repository repo = repoManager.openRepository(project)) {
|
|
return getHead(repo, branch.startsWith(Constants.R_REFS) ? branch : "refs/heads/" + branch);
|
|
}
|
|
}
|
|
|
|
protected RevCommit getRemoteHead(String project, String branch) throws Exception {
|
|
return getRemoteHead(new Project.NameKey(project), branch);
|
|
}
|
|
|
|
protected RevCommit getRemoteHead() throws Exception {
|
|
return getRemoteHead(project, "master");
|
|
}
|
|
|
|
protected void grantTagPermissions() throws Exception {
|
|
grant(Permission.CREATE, project, R_TAGS + "*");
|
|
grant(Permission.CREATE_TAG, project, R_TAGS + "*");
|
|
grant(Permission.CREATE_SIGNED_TAG, project, R_TAGS + "*");
|
|
}
|
|
|
|
protected void assertMailFrom(Message message, String email) throws Exception {
|
|
assertThat(message.headers()).containsKey("Reply-To");
|
|
EmailHeader.String replyTo = (EmailHeader.String) message.headers().get("Reply-To");
|
|
assertThat(replyTo.getString()).isEqualTo(email);
|
|
}
|
|
|
|
protected ContributorAgreement configureContributorAgreement(boolean autoVerify)
|
|
throws Exception {
|
|
ContributorAgreement ca;
|
|
if (autoVerify) {
|
|
String g = createGroup("cla-test-group");
|
|
GroupApi groupApi = gApi.groups().id(g);
|
|
groupApi.description("CLA test group");
|
|
AccountGroup caGroup = groupCache.get(new AccountGroup.UUID(groupApi.detail().id));
|
|
GroupReference groupRef = GroupReference.forGroup(caGroup);
|
|
PermissionRule rule = new PermissionRule(groupRef);
|
|
rule.setAction(PermissionRule.Action.ALLOW);
|
|
ca = new ContributorAgreement("cla-test");
|
|
ca.setAutoVerify(groupRef);
|
|
ca.setAccepted(ImmutableList.of(rule));
|
|
} else {
|
|
ca = new ContributorAgreement("cla-test-no-auto-verify");
|
|
}
|
|
ca.setDescription("description");
|
|
ca.setAgreementUrl("agreement-url");
|
|
|
|
ProjectConfig cfg = projectCache.checkedGet(allProjects).getConfig();
|
|
cfg.replace(ca);
|
|
saveProjectConfig(allProjects, cfg);
|
|
return ca;
|
|
}
|
|
|
|
/**
|
|
* Fetches each bundle into a newly cloned repository, then it applies the bundle, and returns the
|
|
* resulting tree id.
|
|
*/
|
|
protected Map<Branch.NameKey, RevTree> fetchFromBundles(BinaryResult bundles) throws Exception {
|
|
|
|
assertThat(bundles.getContentType()).isEqualTo("application/x-zip");
|
|
|
|
File tempfile = File.createTempFile("test", null);
|
|
bundles.writeTo(new FileOutputStream(tempfile));
|
|
|
|
Map<Branch.NameKey, RevTree> ret = new HashMap<>();
|
|
try (ZipFile readback = new ZipFile(tempfile); ) {
|
|
for (ZipEntry entry : ImmutableList.copyOf(Iterators.forEnumeration(readback.entries()))) {
|
|
String bundleName = entry.getName();
|
|
InputStream bundleStream = readback.getInputStream(entry);
|
|
|
|
int len = bundleName.length();
|
|
assertThat(bundleName).endsWith(".git");
|
|
String repoName = bundleName.substring(0, len - 4);
|
|
Project.NameKey proj = new Project.NameKey(repoName);
|
|
TestRepository<?> localRepo = cloneProject(proj);
|
|
|
|
try (TransportBundleStream tbs =
|
|
new TransportBundleStream(
|
|
localRepo.getRepository(), new URIish(bundleName), bundleStream); ) {
|
|
|
|
FetchResult fr =
|
|
tbs.fetch(
|
|
NullProgressMonitor.INSTANCE,
|
|
Arrays.asList(new RefSpec("refs/*:refs/preview/*")));
|
|
for (Ref r : fr.getAdvertisedRefs()) {
|
|
String branchName = r.getName();
|
|
Branch.NameKey n = new Branch.NameKey(proj, branchName);
|
|
|
|
RevCommit c = localRepo.getRevWalk().parseCommit(r.getObjectId());
|
|
ret.put(n, c.getTree());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/** Assert that the given branches have the given tree ids. */
|
|
protected void assertRevTrees(Project.NameKey proj, Map<Branch.NameKey, RevTree> trees)
|
|
throws Exception {
|
|
TestRepository<?> localRepo = cloneProject(proj);
|
|
GitUtil.fetch(localRepo, "refs/*:refs/*");
|
|
Map<String, Ref> refs = localRepo.getRepository().getAllRefs();
|
|
Map<Branch.NameKey, RevTree> refValues = new HashMap<>();
|
|
|
|
for (Branch.NameKey b : trees.keySet()) {
|
|
if (!b.getParentKey().equals(proj)) {
|
|
continue;
|
|
}
|
|
|
|
Ref r = refs.get(b.get());
|
|
assertThat(r).isNotNull();
|
|
RevWalk rw = localRepo.getRevWalk();
|
|
RevCommit c = rw.parseCommit(r.getObjectId());
|
|
refValues.put(b, c.getTree());
|
|
|
|
assertThat(trees.get(b)).isEqualTo(refValues.get(b));
|
|
}
|
|
assertThat(refValues.keySet()).containsAnyIn(trees.keySet());
|
|
}
|
|
|
|
protected void assertDiffForNewFile(
|
|
DiffInfo diff, RevCommit commit, String path, String expectedContentSideB) throws Exception {
|
|
List<String> expectedLines = new ArrayList<>();
|
|
for (String line : expectedContentSideB.split("\n")) {
|
|
expectedLines.add(line);
|
|
}
|
|
|
|
assertThat(diff.binary).isNull();
|
|
assertThat(diff.changeType).isEqualTo(ChangeType.ADDED);
|
|
assertThat(diff.diffHeader).isNotNull();
|
|
assertThat(diff.intralineStatus).isNull();
|
|
assertThat(diff.webLinks).isNull();
|
|
|
|
assertThat(diff.metaA).isNull();
|
|
assertThat(diff.metaB).isNotNull();
|
|
assertThat(diff.metaB.commitId).isEqualTo(commit.name());
|
|
|
|
String expectedContentType = "text/plain";
|
|
if (COMMIT_MSG.equals(path)) {
|
|
expectedContentType = FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE;
|
|
} else if (MERGE_LIST.equals(path)) {
|
|
expectedContentType = FileContentUtil.TEXT_X_GERRIT_MERGE_LIST;
|
|
}
|
|
assertThat(diff.metaB.contentType).isEqualTo(expectedContentType);
|
|
|
|
assertThat(diff.metaB.lines).isEqualTo(expectedLines.size());
|
|
assertThat(diff.metaB.name).isEqualTo(path);
|
|
assertThat(diff.metaB.webLinks).isNull();
|
|
|
|
assertThat(diff.content).hasSize(1);
|
|
DiffInfo.ContentEntry contentEntry = diff.content.get(0);
|
|
assertThat(contentEntry.b).containsExactlyElementsIn(expectedLines).inOrder();
|
|
assertThat(contentEntry.a).isNull();
|
|
assertThat(contentEntry.ab).isNull();
|
|
assertThat(contentEntry.common).isNull();
|
|
assertThat(contentEntry.editA).isNull();
|
|
assertThat(contentEntry.editB).isNull();
|
|
assertThat(contentEntry.skip).isNull();
|
|
}
|
|
|
|
protected TestRepository<?> createProjectWithPush(
|
|
String name, @Nullable Project.NameKey parent, SubmitType submitType) throws Exception {
|
|
Project.NameKey project = createProject(name, parent, true, submitType);
|
|
grant(Permission.PUSH, project, "refs/heads/*");
|
|
grant(Permission.SUBMIT, project, "refs/for/refs/heads/*");
|
|
return cloneProject(project);
|
|
}
|
|
|
|
protected void assertPermitted(ChangeInfo info, String label, Integer... expected) {
|
|
assertThat(info.permittedLabels).isNotNull();
|
|
Collection<String> strs = info.permittedLabels.get(label);
|
|
if (expected.length == 0) {
|
|
assertThat(strs).isNull();
|
|
} else {
|
|
assertThat(strs.stream().map(s -> Integer.valueOf(s.trim())).collect(toList()))
|
|
.containsExactlyElementsIn(Arrays.asList(expected));
|
|
}
|
|
}
|
|
|
|
protected void assertNotifyTo(TestAccount expected) {
|
|
assertThat(sender.getMessages()).hasSize(1);
|
|
Message m = sender.getMessages().get(0);
|
|
assertThat(m.rcpt()).containsExactly(expected.emailAddress);
|
|
assertThat(((EmailHeader.AddressList) m.headers().get("To")).getAddressList())
|
|
.containsExactly(expected.emailAddress);
|
|
assertThat(m.headers().get("CC").isEmpty()).isTrue();
|
|
}
|
|
|
|
protected void assertNotifyCc(TestAccount expected) {
|
|
assertThat(sender.getMessages()).hasSize(1);
|
|
Message m = sender.getMessages().get(0);
|
|
assertThat(m.rcpt()).containsExactly(expected.emailAddress);
|
|
assertThat(m.headers().get("To").isEmpty()).isTrue();
|
|
assertThat(((EmailHeader.AddressList) m.headers().get("CC")).getAddressList())
|
|
.containsExactly(expected.emailAddress);
|
|
}
|
|
|
|
protected void assertNotifyBcc(TestAccount expected) {
|
|
assertThat(sender.getMessages()).hasSize(1);
|
|
Message m = sender.getMessages().get(0);
|
|
assertThat(m.rcpt()).containsExactly(expected.emailAddress);
|
|
assertThat(m.headers().get("To").isEmpty()).isTrue();
|
|
assertThat(m.headers().get("CC").isEmpty()).isTrue();
|
|
}
|
|
|
|
protected void watch(String project, String filter) throws RestApiException {
|
|
List<ProjectWatchInfo> projectsToWatch = new ArrayList<>();
|
|
ProjectWatchInfo pwi = new ProjectWatchInfo();
|
|
pwi.project = project;
|
|
pwi.filter = filter;
|
|
pwi.notifyAbandonedChanges = true;
|
|
pwi.notifyNewChanges = true;
|
|
pwi.notifyAllComments = true;
|
|
projectsToWatch.add(pwi);
|
|
gApi.accounts().self().setWatchedProjects(projectsToWatch);
|
|
}
|
|
}
|