blob: 8f5ee7a16306511cc0d9a4661cced02ad8a62f8c [file] [log] [blame]
// Copyright (C) 2017 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.googlesource.gerrit.plugins.findowners;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.entities.RefNames.REFS_CONFIG;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.api.projects.BranchApi;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.junit.Before;
/** Abstract base class for find-owners plugin integration tests. */
public abstract class FindOwners extends LightweightPluginDaemonTest {
@Inject protected Emails emails;
@Inject protected PermissionBackend permissionBackend;
@Inject protected ProjectOperations projectOperations;
@Inject protected PatchListCache patchListCache;
protected static final String PLUGIN_NAME = "find-owners";
protected Config config;
@Before
public void setConfig() {
config = new Config(pluginConfig, null, null, null, null);
}
protected String oneOwnerList(String email) {
return "owners:[" + ownerJson(email) + "]";
}
protected String ownerJson(String email) {
return "{email:" + email + ",weights:[1,0,0]}";
}
protected String ownerJson(String email, int w1, int w2, int w3) {
return "{email:" + email + ",weights:[" + w1 + "," + w2 + "," + w3 + "]}";
}
protected ChangeInfo newChangeInfo(String subject) throws Exception {
ChangeInput in = new ChangeInput();
in.project = project.get();
in.branch = "master";
in.subject = subject;
in.topic = "";
in.status = ChangeStatus.NEW;
return gApi.changes().create(in).get();
}
protected String getFindOwnersResponse(ChangeInfo info) throws Exception {
return filteredJson(
userRestSession.get("/changes/" + info._number + "/revisions/1/" + PLUGIN_NAME));
}
protected String getOwnersResponse(ChangeInfo info) throws Exception {
return filteredJson(userRestSession.get("/changes/" + info._number + "/owners"));
}
protected String getOwnersResponse(PushOneCommit.Result change) throws Exception {
return filteredJson(userRestSession.get("/changes/" + change.getChangeId() + "/owners"));
}
protected String getOwnersDebugResponse(PushOneCommit.Result change) throws Exception {
return filteredJson(
userRestSession.get("/changes/" + change.getChangeId() + "/owners?debug=1"));
}
protected void approveSubmit(PushOneCommit.Result change) throws Exception {
approve(change.getChangeId());
gApi.changes().id(change.getChangeId()).current().submit(new SubmitInput());
}
protected PushOneCommit.Result addFile(String subject, String file, String content)
throws Exception {
PushOneCommit.Result c = createChange(subject, file, content);
approveSubmit(c);
return c;
}
protected void switchProject(Project.NameKey p) throws Exception {
project = p;
testRepo = cloneProject(project);
}
protected void blockRead(Project.NameKey p) throws Exception {
projectOperations
.project(p)
.forUpdate()
.add(block(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
.update();
}
protected org.eclipse.jgit.lib.Config readProjectConfig() throws Exception {
git().fetch().setRefSpecs(new RefSpec(REFS_CONFIG + ":" + REFS_CONFIG)).call();
testRepo.reset(RefNames.REFS_CONFIG);
RevWalk rw = testRepo.getRevWalk();
RevTree tree = rw.parseTree(testRepo.getRepository().resolve("HEAD"));
try (TreeWalk treeWalk = new TreeWalk(rw.getObjectReader())) {
treeWalk.setFilter(PathFilterGroup.createFromStrings("project.config"));
treeWalk.reset(tree);
boolean hasProjectConfig = treeWalk.next();
if (!hasProjectConfig) {
return new org.eclipse.jgit.lib.Config();
}
}
RevObject obj = rw.parseAny(testRepo.get(tree, "project.config"));
ObjectLoader loader = rw.getObjectReader().open(obj);
String text = new String(loader.getCachedBytes(), UTF_8);
org.eclipse.jgit.lib.Config cfg = new org.eclipse.jgit.lib.Config();
cfg.fromText(text);
return cfg;
}
protected void setProjectConfig(String var, String value) throws Exception {
org.eclipse.jgit.lib.Config cfg = readProjectConfig();
cfg.setString("plugin", PLUGIN_NAME, var, value);
assertThat(cfg.getString("plugin", PLUGIN_NAME, var)).isEqualTo(value);
PushOneCommit.Result commit =
pushFactory
.create(
admin.newIdent(), // normal user cannot change refs/meta/config
testRepo,
"Update project config",
"project.config",
cfg.toText())
.to("refs/for/" + REFS_CONFIG);
commit.assertOkStatus();
approveSubmit(commit);
testRepo = cloneProject(project); // reset the testRepo after cfg change
}
protected int checkApproval(PushOneCommit.Result r) throws Exception {
Project.NameKey project = r.getChange().project();
Cache cache = getCache().init(0, 0);
OwnersDb db =
cache.get(
true,
null,
projectCache.get(project).orElseThrow(illegalState(project)),
accountCache,
emails,
repoManager,
r.getChange(),
1);
Checker c =
new Checker(
accountCache,
patchListCache,
repoManager,
emails,
pluginConfig,
null,
r.getChange(),
1);
return c.findApproval(db);
}
// To simplify test case code, the REST API returned JSON string is filtered to
// remove '\t', ' ' and '"', but replace "\\\"" with '"',
// replace '\n' with space, remove "owner_revision:*" and "HostName:*",
// and replace some unicode with simple characters.
protected static String filteredJson(String json) {
return json.replaceAll("[\t ]", "")
.replace("\\\"", "\t")
.replace("\"", "")
.replace('\t', '"')
.replace('\n', ' ')
.replaceAll("owner_revision:[^ ]* ", "")
.replaceAll("HostName:[^ ]*, ", "")
.replace("\\u0027", "'")
.replace("\\u003d", "=")
.replace("\\u003e", ">");
}
protected static String filteredJson(RestResponse response) throws Exception {
return filteredJson(response.getEntityContent());
}
protected String myProjectName(String test, String project) {
return this.getClass().getName() + "_" + test + "_" + project;
}
protected String idProject(String test, String project) {
return idProject(myProjectName(test, project));
}
protected String idProject(String name) {
// Expected string of "id": "name",,
return "\"id\":\"" + name + "\",";
}
protected static void verifyRestResult(
RestResult result, int voteLevel, int patchset, int changeNumber, boolean addDebugMsg)
throws Exception {
assertThat(result.minOwnerVoteLevel).isEqualTo(voteLevel);
assertThat(result.patchset).isEqualTo(patchset);
assertThat(result.change).isEqualTo(changeNumber);
assertThat(result.addDebugMsg).isEqualTo(addDebugMsg);
if (addDebugMsg) {
assertThat(result.dbgmsgs).isNotNull();
} else {
assertThat(result.dbgmsgs).isNull();
}
}
protected BranchApi createBranch(String branch) throws Exception {
return createBranch(BranchNameKey.create(project, branch));
}
protected PushOneCommit.Result createChangeInBranch(
String branch, String subject, String fileName, String content) throws Exception {
PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content);
return push.to("refs/for/" + branch);
}
protected Project.NameKey newProject(String name) {
return newProject(name, Project.nameKey("All-Projects"));
}
protected Project.NameKey newProject(String name, Project.NameKey parent) {
return projectOperations.newProject().parent(parent).name(name).create();
}
protected String projectOwnersFileName(Project.NameKey name) {
// This function is called repeatedly in ConfigIT without recreating config.
// So, here we recreate config, to get the latest owners file name.
setConfig();
return config.getOwnersFileName(projectCache.get(name).orElseThrow(illegalState(name)));
}
protected Cache getCache() {
return Cache.getInstance(config, repoManager);
}
}