Simplify tests and split FindOwnersIT tests
* Split fast JUnit tests from slow IT tests.
* Factor out TestWatcher code into its own Watcher class.
* Change newProject default to return simple project names
under All-Projects.
* Split FindOwnersIT tests into 5 smaller *IT.java files.
Change-Id: Ica347c5d06e36b52b5c301e891d0837844d666aa
diff --git a/BUILD b/BUILD
index 8c3dff9..15148a1 100644
--- a/BUILD
+++ b/BUILD
@@ -47,12 +47,44 @@
],
)
+java_library(
+ name = "find-owners-junit",
+ testonly = 1,
+ srcs = glob(["src/test/java/**/Watcher.java"]),
+ deps = PLUGIN_TEST_DEPS,
+)
+
+java_library(
+ name = "find-owners-IT",
+ testonly = 1,
+ srcs = glob(["src/test/java/**/FindOwners.java"]),
+ deps = PLUGIN_TEST_DEPS + [
+ ":find-owners-junit",
+ ":find-owners-lib",
+ ":find-owners__plugin",
+ ],
+)
+
+# Separate fast junit tests from slow interation (IT) tests.
junit_tests(
- name = "findowners_tests",
- srcs = glob(["src/test/java/**/*.java"]),
+ name = "findowners_junit_tests",
+ srcs = glob(["src/test/java/**/*Test.java"]),
tags = ["findowners"],
deps = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
"@commons-io//jar",
+ ":find-owners-junit",
+ ":find-owners-lib",
+ ],
+)
+
+junit_tests(
+ name = "findowners_IT_tests",
+ srcs = glob(["src/test/java/**/*IT.java"]),
+ tags = ["findowners"],
+ deps = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
+ "@commons-io//jar",
+ ":find-owners-IT",
+ ":find-owners-junit",
":find-owners-lib",
":find-owners-prolog-rules",
":find-owners__plugin",
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/ApiIT.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/ApiIT.java
new file mode 100644
index 0000000..4bcde45
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/ApiIT.java
@@ -0,0 +1,185 @@
+// Copyright (C) 2019 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.common.truth.Truth8.assertThat;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.common.collect.Multimap;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.extensions.api.accounts.EmailInput;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.change.ChangeResource;
+import java.util.Collection;
+import java.util.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+
+/** Test find-owners plugin top level APIs and dependent Gerrit APIs. */
+@TestPlugin(name = "find-owners", sysModule = "com.googlesource.gerrit.plugins.findowners.Module")
+public class ApiIT extends FindOwners {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ @Rule public Watcher watcher = new Watcher(logger);
+
+ @Test
+ public void getOwnersTest() throws Exception {
+ ChangeInfo info1 = newChangeInfo("test1 GetOwners");
+ ChangeInfo info2 = newChangeInfo("test2 GetOwners");
+ assertThat(info2._number).isEqualTo(info1._number + 1);
+ String expected =
+ ")]}' { minOwnerVoteLevel:1, addDebugMsg:false, change:"
+ + info1._number
+ + ", patchset:1, file2owners:{}, reviewers:[], owners:[], files:[] }";
+ Cache cache = getCache().init(0, 10); // reset, no Cache
+ assertThat(cache.size()).isEqualTo(0L);
+ // GetOwners GET API
+ assertThat(getOwnersResponse(info1)).isEqualTo(expected);
+ assertThat(cache.size()).isEqualTo(0L);
+ // find-owners GET API
+ assertThat(getFindOwnersResponse(info1)).isEqualTo(expected);
+ cache.init(10, 5); // create the Cache
+ assertThat(cache.size()).isEqualTo(0L);
+ assertThat(getOwnersResponse(info1)).isEqualTo(expected);
+ assertThat(getFindOwnersResponse(info1)).isEqualTo(expected);
+ assertThat(cache.size()).isEqualTo(1L);
+ }
+
+ @Test
+ public void requestErrorTest() throws Exception {
+ PushOneCommit.Result c1 = createChange("1", "t.c", "##");
+ assertThat(getOwnersResponse(c1)).contains("owners:[], files:[ t.c ]");
+ int id = c1.getChange().getId().get();
+ // Correct change id.
+ String result = userRestSession.get("/changes/" + id + "/owners").getEntityContent();
+ assertThat(filteredJson(result)).contains("owners:[], files:[ t.c ]");
+ // Wrong change number, 404 not found.
+ RestResponse response = userRestSession.get("/changes/" + (id + 1) + "/owners");
+ assertThat(response.getStatusCode()).isEqualTo(404);
+ assertThat(response.getEntityContent()).isEqualTo("Not found: " + (id + 1));
+ // Wrong request parameter, 400 not a valid option
+ response = userRestSession.get("/changes/" + id + "/owners?xyz=3");
+ assertThat(response.getStatusCode()).isEqualTo(400);
+ assertThat(response.getEntityContent()).isEqualTo("\"--xyz\" is not a valid option");
+ // Wrong patchset parameter, no content
+ response = userRestSession.get("/changes/" + id + "/owners?patchset=2");
+ assertThat(response.getStatusCode()).isEqualTo(204);
+ assertThat(response.hasContent()).isFalse();
+ }
+
+ @Test
+ public void authorDefaultVoteTest() throws Exception {
+ // CL author has default +1 owner vote.
+ addFile("1", "d1/OWNERS", user.email + "\n"); // d1 owned by user
+ addFile("2", "d2/OWNERS", admin.email + "\n"); // d2 owned by admin
+ // admin is the author of CLs created by createChange.
+ PushOneCommit.Result r1 = createChange("r1", "d1/t.c", "Hello1");
+ PushOneCommit.Result r2 = createChange("r2", "d2/t.c", "Hello2");
+ PushOneCommit.Result r3 = createChange("r3", "d3/t.c", "Hello3");
+ assertThat(checkApproval(r1)).isEqualTo(-1); // owner is not change author
+ assertThat(checkApproval(r2)).isEqualTo(1); // owner is change author, default +1
+ assertThat(checkApproval(r3)).isEqualTo(0); // no owner is found in d3
+ }
+
+ @Test
+ public void actionApplyTest() throws Exception {
+ Cache cache = getCache().init(0, 10);
+ assertThat(cache.size()).isEqualTo(0);
+ // TODO: create ChangeInput in a new project.
+ ChangeInfo changeInfo = newChangeInfo("test Action.apply");
+ ChangeResource cr = parseChangeResource(changeInfo.changeId);
+ Action.Parameters param = new Action.Parameters();
+ Action action =
+ new Action(
+ pluginConfig,
+ null,
+ changeDataFactory,
+ accountCache,
+ emails,
+ repoManager,
+ projectCache);
+ Response<RestResult> response = action.apply(cr, param);
+ RestResult result = response.value();
+ verifyRestResult(result, 1, 1, changeInfo._number, false);
+ param.debug = true;
+ response = action.apply(cr, param);
+ result = response.value();
+ verifyRestResult(result, 1, 1, changeInfo._number, true);
+ assertThat(result.dbgmsgs.user).isEqualTo("?");
+ assertThat(result.dbgmsgs.project).isEqualTo(changeInfo.project);
+ // changeInfo.branch is "master" but result.dbgmsgs.branch is "refs/heads/master".
+ assertThat(result.dbgmsgs.branch).contains(changeInfo.branch);
+ assertThat(result.dbgmsgs.path2owners).isEmpty();
+ assertThat(result.dbgmsgs.owner2paths).isEmpty();
+ assertThat(result.file2owners).isEmpty();
+ assertThat(result.reviewers).isEmpty();
+ assertThat(result.owners).isEmpty();
+ assertThat(result.files).isEmpty();
+ // TODO: find expected value of ownerRevision.
+ assertThat(cache.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void accountTest() throws Exception {
+ String[] users = {"user1", "user2", "user3"};
+ String[] emails1 = {"abc@g.com", "abc+xyz@g.com", "xyz-team+review@g.com"};
+ String[] emails2 = {"abc@goog.com", "abc+xyz2@g.com", "xyz-team@goog.com"};
+ // Create accounts with given user name, first and second email addresses.
+ for (int i = 0; i < users.length; i++) {
+ accountCreator.create(users[i], emails1[i], "FullName " + users[i]).getId();
+ EmailInput input = new EmailInput();
+ input.email = emails2[i];
+ input.noConfirmation = true;
+ gApi.accounts().id(users[i]).addEmail(input);
+ }
+ // Find accounts with given first and second email addresses.
+ // OwnersDb uses either emails.getAccountFor or getAccountsFor to get preferred email addresses.
+ Multimap<String, Account.Id> map1 = emails.getAccountsFor(emails1);
+ Multimap<String, Account.Id> map2 = emails.getAccountsFor(emails2);
+ for (int i = 0; i < users.length; i++) {
+ Collection<Account.Id> ids1 = emails.getAccountFor(emails1[i]);
+ Collection<Account.Id> ids2 = emails.getAccountFor(emails2[i]);
+ Collection<Account.Id> ids3 = map1.get(emails1[i]);
+ Collection<Account.Id> ids4 = map2.get(emails2[i]);
+ assertThat(ids1).hasSize(1);
+ assertThat(ids2).hasSize(1);
+ assertThat(ids3).hasSize(1);
+ assertThat(ids4).hasSize(1);
+ Account.Id id1 = ids1.iterator().next();
+ Account.Id id2 = ids2.iterator().next();
+ Account.Id id3 = ids3.iterator().next();
+ Account.Id id4 = ids4.iterator().next();
+ assertThat(id1).isEqualTo(id2); // Both emails should find the same account.
+ assertThat(id1).isEqualTo(id3);
+ assertThat(id1).isEqualTo(id4);
+ // Action.getReviewers and Checker.getVotes use accountCache to get email address.
+ Optional<Account> account = accountCache.get(id1).map(AccountState::getAccount);
+ assertThat(account).named("account %s", id1).isPresent();
+ assertThat(account.get().getPreferredEmail()).isEqualTo(emails1[i]);
+ }
+ // Wrong or non-existing email address.
+ String[] wrongEmails = {"nobody", "@g.com", "nobody@g.com", "*"};
+ Multimap<String, Account.Id> email2ids = emails.getAccountsFor(wrongEmails);
+ for (String email : wrongEmails) {
+ assertThat(emails.getAccountFor(email)).isEmpty();
+ assertThat(email2ids).doesNotContainKey(email);
+ }
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/ConfigIT.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/ConfigIT.java
new file mode 100644
index 0000000..73e137d
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/ConfigIT.java
@@ -0,0 +1,206 @@
+// Copyright (C) 2019 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 com.google.common.flogger.FluentLogger;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.reviewdb.client.Project;
+import org.junit.Rule;
+import org.junit.Test;
+
+/** Test find-owners plugin config variables. */
+@TestPlugin(name = "find-owners", sysModule = "com.googlesource.gerrit.plugins.findowners.Module")
+public class ConfigIT extends FindOwners {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ @Rule public Watcher watcher = new Watcher(logger);
+
+ @Test
+ public void projectInheritanceTest() throws Exception {
+ Project.NameKey pA = newProject("pA");
+ Project.NameKey pB = newProject("pB", pA);
+ Project.NameKey pC = newProject("pC", pB);
+ assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS");
+ assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS");
+ assertThat(projectOwnersFileName(pC)).isEqualTo("OWNERS");
+ switchProject(pA);
+ setProjectConfig("ownersFileName", "OWNERS_A");
+ assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS_A");
+ assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS_A");
+ assertThat(projectOwnersFileName(pC)).isEqualTo("OWNERS_A");
+ switchProject(pC);
+ setProjectConfig("ownersFileName", "OWNERS_C");
+ assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS_A");
+ assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS_A");
+ assertThat(projectOwnersFileName(pC)).isEqualTo("OWNERS_C");
+ switchProject(pB);
+ setProjectConfig("ownersFileName", "OWNERS_B");
+ assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS_A");
+ assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS_B");
+ assertThat(projectOwnersFileName(pC)).isEqualTo("OWNERS_C");
+ switchProject(pC);
+ }
+
+ @Test
+ public void ownersFileNameTest() throws Exception {
+ // Default project is something like ....FindOwnersIT..._project
+ Project.NameKey pA = newProject("Project_A");
+ Project.NameKey pB = newProject("Project_B");
+ // Add OWNERS and OWNERS.alpha file to Project_A.
+ switchProject(pA);
+ createBranch("BranchX");
+ addFile("1", "OWNERS", "per-file *.c=x@x\n"); // default owner x@x
+ addFile("2", "OWNERS.alpha", "per-file *.c=a@a\n"); // alpha owner a@a
+ PushOneCommit.Result cA = createChange("cA", "tA.c", "Hello A!");
+ PushOneCommit.Result cX = createChangeInBranch("BranchX", "cX", "tX.c", "Hello X!");
+ // Add OWNERS and OWNERS.beta file to Project_B.
+ switchProject(pB);
+ createBranch("BranchY");
+ addFile("3", "OWNERS", "per-file *.c=y@y\n"); // default owner y@y
+ addFile("4", "OWNERS.beta", "per-file *.c=b@b\n"); // beta owner b@b
+ PushOneCommit.Result cB = createChange("cB", "tB.c", "Hello B!");
+ PushOneCommit.Result cY = createChangeInBranch("BranchY", "cY", "tY.c", "Hello Y!");
+
+ // Default owners file name is "OWNERS".
+ assertThat(Config.OWNERS).isEqualTo("OWNERS");
+ assertThat(config.getDefaultOwnersFileName()).isEqualTo("OWNERS");
+ assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS");
+ assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS");
+
+ String ownerX = oneOwnerList("x@x");
+ String ownerY = oneOwnerList("y@y");
+ String cAResponse = getOwnersDebugResponse(cA);
+ String cXResponse = getOwnersDebugResponse(cX);
+ String cBResponse = getOwnersDebugResponse(cB);
+ String cYResponse = getOwnersDebugResponse(cY);
+ assertThat(cAResponse).contains(ownerX + ", files:[ tA.c ]");
+ assertThat(cBResponse).contains(ownerY + ", files:[ tB.c ]");
+ assertThat(cXResponse).contains(", files:[ tX.c ]");
+ assertThat(cYResponse).contains(", files:[ tY.c ]");
+ assertThat(cXResponse).doesNotContain(ownerX);
+ assertThat(cYResponse).doesNotContain(ownerY);
+ assertThat(cAResponse).contains("branch:refs/heads/master");
+ assertThat(cBResponse).contains("branch:refs/heads/master");
+ assertThat(cXResponse).contains("branch:refs/heads/BranchX");
+ assertThat(cYResponse).contains("branch:refs/heads/BranchY");
+ assertThat(cAResponse).contains("ownersFileName:OWNERS, ");
+ assertThat(cBResponse).contains("ownersFileName:OWNERS, ");
+ assertThat(cXResponse).contains("ownersFileName:OWNERS, ");
+ assertThat(cYResponse).contains("ownersFileName:OWNERS, ");
+
+ // pA and pB use default OWNERS file name.
+ // cA and cB logs should not contain anything about Missing/Found root.
+ assertThat(cAResponse).doesNotContain("root");
+ assertThat(cBResponse).doesNotContain("root");
+ // cX and cY are not for the master branch.
+ // They should not contain anything about Missing/Found root.
+ assertThat(cXResponse).doesNotContain("root");
+ assertThat(cYResponse).doesNotContain("root");
+
+ // Change owners file name to "OWNERS.alpha" and "OWNERS.beta"
+ switchProject(pA);
+ setProjectConfig("ownersFileName", "OWNERS.alpha");
+ switchProject(pB);
+ setProjectConfig("ownersFileName", "OWNERS.beta");
+
+ assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS.alpha");
+ assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS.beta");
+ String ownerA = oneOwnerList("a@a");
+ String ownerB = oneOwnerList("b@b");
+ cAResponse = getOwnersDebugResponse(cA);
+ cBResponse = getOwnersDebugResponse(cB);
+ cXResponse = getOwnersDebugResponse(cX);
+ cYResponse = getOwnersDebugResponse(cY);
+ assertThat(cAResponse).contains("ownersFileName:OWNERS.alpha, ");
+ assertThat(cBResponse).contains("ownersFileName:OWNERS.beta, ");
+ assertThat(cXResponse).contains("ownersFileName:OWNERS.alpha, ");
+ assertThat(cYResponse).contains("ownersFileName:OWNERS.beta, ");
+ assertThat(cAResponse).contains(ownerA + ", files:[ tA.c ]");
+ assertThat(cBResponse).contains(ownerB + ", files:[ tB.c ]");
+ // pA and pB now use non-default OWNERS file name.
+ // cA and cB logs should contain "Found root ..."
+ assertThat(cAResponse).contains("FoundrootOWNERS.alpha");
+ assertThat(cBResponse).contains("FoundrootOWNERS.beta");
+ assertThat(cXResponse).doesNotContain("root");
+ assertThat(cYResponse).doesNotContain("root");
+
+ // Now change owners file name to "MAINTAINERS"
+ // logs should contain "Missing root ..."
+ switchProject(pA);
+ setProjectConfig("ownersFileName", "MAINTAINERS");
+ cAResponse = getOwnersDebugResponse(cA);
+ cXResponse = getOwnersDebugResponse(cX);
+ assertThat(cAResponse).contains("ownersFileName:MAINTAINERS, ");
+ assertThat(cXResponse).contains("ownersFileName:MAINTAINERS, ");
+ assertThat(cAResponse).contains("owners:[], ");
+ assertThat(cXResponse).contains("owners:[], ");
+ assertThat(cAResponse).contains("MissingrootMAINTAINERS");
+ // Gerrit server log file should contain: "Missing root MAINTAINERS for change "
+ // cX is not on the master branch, so we do not check for the root owners file.
+ assertThat(cXResponse).doesNotContain("root");
+
+ // Change back to OWNERS in Project_A
+ switchProject(pA);
+ setProjectConfig("ownersFileName", "OWNERS");
+ assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS");
+ cAResponse = getOwnersDebugResponse(cA);
+ cBResponse = getOwnersDebugResponse(cB);
+ assertThat(cAResponse).contains(ownerX + ", files:[ tA.c ]");
+ assertThat(cBResponse).contains(ownerB + ", files:[ tB.c ]");
+
+ // Change back to OWNERS.alpha in Project_B, but there is no OWNERS.alpha
+ switchProject(pB);
+ setProjectConfig("ownersFileName", "OWNERS.alpha");
+ assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS.alpha");
+ cAResponse = getOwnersDebugResponse(cA);
+ cBResponse = getOwnersDebugResponse(cB);
+ cYResponse = getOwnersDebugResponse(cY);
+ assertThat(cAResponse).contains("ownersFileName:OWNERS, ");
+ assertThat(cBResponse).contains("ownersFileName:OWNERS.alpha, ");
+ assertThat(cAResponse).contains(ownerX + ", files:[ tA.c ]");
+ assertThat(cBResponse).contains("owners:[], files:[ tB.c ]");
+ assertThat(cBResponse).contains("MissingrootOWNERS.alpha");
+ // Gerrit server log file should contain: "Missing root OWNERS.alpha for change "
+ assertThat(cYResponse).doesNotContain("root");
+
+ // Do not accept empty string or all-white-spaces for ownersFileName.
+ setProjectConfig("ownersFileName", " ");
+ assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS");
+ setProjectConfig("ownersFileName", " \t ");
+ assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS");
+ setProjectConfig("ownersFileName", "O");
+ assertThat(projectOwnersFileName(pB)).isEqualTo("O");
+ }
+
+ @Test
+ public void projectTest() throws Exception {
+ RestResponse response = adminRestSession.get("/projects/?d");
+ String content = response.getEntityContent();
+ // Predefined projects: "All-Projects", "All-Users", project
+ assertThat(content).contains("\"id\": \"All-Projects\",");
+ assertThat(content).contains("\"id\": \"All-Users\",");
+ assertThat(content).contains(idProject("projectTest", "project"));
+ assertThat(content).doesNotContain(idProject("projectTest", "ProjectA"));
+ assertThat(content).doesNotContain(idProject("ProjectA"));
+ newProject("ProjectA");
+ response = adminRestSession.get("/projects/?d");
+ content = response.getEntityContent();
+ assertThat(content).doesNotContain(idProject("projectTest", "ProjectA"));
+ assertThat(content).contains(idProject("ProjectA"));
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/FindOwners.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/FindOwners.java
new file mode 100644
index 0000000..da89ccf
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/FindOwners.java
@@ -0,0 +1,226 @@
+// 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.reviewdb.client.RefNames.REFS_CONFIG;
+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.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.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.account.Emails;
+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 ProjectOperations projectOperations;
+
+ protected static final String PLUGIN_NAME = "find-owners";
+ protected Config config;
+
+ @Before
+ public void setConfig() {
+ config = new Config(pluginConfig);
+ }
+
+ 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 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.getIdent(), // normal user cannot change refs/meta/config
+ testRepo,
+ "Update project config",
+ "project.config",
+ cfg.toText())
+ .to("refs/for/" + REFS_CONFIG);
+ commit.assertOkStatus();
+ approveSubmit(commit);
+ }
+
+ 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, projectCache.get(project), accountCache, emails,
+ repoManager, pluginConfig, r.getChange(), 1);
+ Checker c = new Checker(repoManager, pluginConfig, null, r.getChange(), 1);
+ return c.findApproval(accountCache, db);
+ }
+
+ // Remove '"' and space; replace '\n' with ' '; ignore "owner_revision" and "HostName:*".
+ protected static String filteredJson(String json) {
+ return json.replaceAll("[\" ]*", "").replace('\n', ' ').replaceAll("owner_revision:[^ ]* ", "")
+ .replaceAll("HostName:[^ ]*, ", "");
+ }
+
+ 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(new Branch.NameKey(project, branch));
+ }
+
+ protected PushOneCommit.Result createChangeInBranch(
+ String branch, String subject, String fileName, String content) throws Exception {
+ PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo, subject, fileName, content);
+ return push.to("refs/for/" + branch);
+ }
+
+ protected Project.NameKey newProject(String name) {
+ return newProject(name, new 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) {
+ return config.getOwnersFileName(projectCache.get(name), null);
+ }
+
+ protected Cache getCache() {
+ return Cache.getInstance(pluginConfig, repoManager);
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/FindOwnersIT.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/FindOwnersIT.java
deleted file mode 100644
index e7fef5b..0000000
--- a/src/test/java/com/googlesource/gerrit/plugins/findowners/FindOwnersIT.java
+++ /dev/null
@@ -1,1019 +0,0 @@
-// 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.common.truth.Truth8.assertThat;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_CONFIG;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.collect.Multimap;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
-import com.google.gerrit.acceptance.PushOneCommit;
-import com.google.gerrit.acceptance.RestResponse;
-import com.google.gerrit.acceptance.TestPlugin;
-import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
-import com.google.gerrit.extensions.api.accounts.EmailInput;
-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.extensions.restapi.Response;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.account.Emails;
-import com.google.gerrit.server.change.ChangeResource;
-import com.google.inject.Inject;
-import java.util.Collection;
-import java.util.Optional;
-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;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-
-/** Test find-owners plugin API. */
-@TestPlugin(name = "find-owners", sysModule = "com.googlesource.gerrit.plugins.findowners.Module")
-public class FindOwnersIT extends LightweightPluginDaemonTest {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- @Rule
- public TestWatcher watcher = new TestWatcher() {
- @Override
- public void starting(final Description method) {
- logger.atInfo().log("Test starting: " + method.getMethodName());
- }
-
- @Override
- public void finished(final Description method) {
- logger.atInfo().log("Test finished: " + method.getMethodName());
- }
- };
-
- @Inject private Emails emails;
- @Inject private ProjectOperations projectOperations;
- private Config config;
-
- @Before
- public void setConfig() {
- config = new Config(pluginConfig);
- }
-
- @Test
- public void getOwnersTest() throws Exception {
- ChangeInfo info1 = newChangeInfo("test1 GetOwners");
- ChangeInfo info2 = newChangeInfo("test2 GetOwners");
- assertThat(info2._number).isEqualTo(info1._number + 1);
- String expected =
- ")]}' { minOwnerVoteLevel:1, addDebugMsg:false, change:"
- + info1._number
- + ", patchset:1, file2owners:{}, reviewers:[], owners:[], files:[] }";
- Cache cache = getCache().init(0, 10); // reset, no Cache
- assertThat(cache.size()).isEqualTo(0L);
- // GetOwners GET API
- assertThat(getOwnersResponse(info1)).isEqualTo(expected);
- assertThat(cache.size()).isEqualTo(0L);
- // find-owners GET API
- assertThat(getFindOwnersResponse(info1)).isEqualTo(expected);
- cache.init(10, 5); // create the Cache
- assertThat(cache.size()).isEqualTo(0L);
- assertThat(getOwnersResponse(info1)).isEqualTo(expected);
- assertThat(getFindOwnersResponse(info1)).isEqualTo(expected);
- assertThat(cache.size()).isEqualTo(1L);
- }
-
- @Test
- public void includeNotFoundTest() throws Exception {
- // c2 and c1 are both submitted before existence of OWNERS.
- PushOneCommit.Result c2 = addFile("1", "t.c", "##");
- // Submitted c2 still finds no owners before c1 is submitted.
- assertThat(getOwnersResponse(c2)).contains("owners:[], files:[ t.c ]");
- PushOneCommit.Result c1 = addFile("2", "OWNERS",
- "x@x\na@a\ninclude P1/P2 : f1\ninclude ./d1/d2/../../f2\n");
- // Now c2 should find owners, but include directives find no repository or file.
- String ownersAX = "owners:[ " + ownerJson("a@a") + ", " + ownerJson("x@x") + " ]";
- String path2owners = "path2owners:{ ./:[ a@a, x@x ] }";
- String owner2paths = "owner2paths:{ a@a:[ ./ ], x@x:[ ./ ] }";
- String projectName = project.get();
- String expectedInLog = "project:" + projectName + ", "
- + "ownersFileName:OWNERS, "
- + "getBranchId:refs/heads/master(FOUND), "
- + "findOwnersFileFor:./t.c, "
- + "findOwnersFileIn:., "
- + "getFile:OWNERS:(...), "
- + "parseLine:include:P1/P2:f1, "
- + "getRepoFile:P1/P2:refs/heads/master:f1, "
- + "getRepoFileException:repositorynotfound:P1/P2, " // repository not found
- + "parseLine:include:(), " // missing file is treated as empty
- + "parseLine:include:" + projectName + ":./d1/d2/../../f2, "
- + "getRepoFile:" + projectName + ":refs/heads/master:f2, "
- + "getFile:f2(NOTFOUND), " // same repository but f2 is missing
- + "parseLine:include:(), " // missing file is treated as empty
- + "countNumOwners, "
- + "findOwners, "
- + "checkFile:./t.c, "
- + "checkDir:., "
- + "addOwnerWeightsIn:./ "
- + "] ";
- String c2Response = getOwnersDebugResponse(c2);
- assertThat(c2Response).contains(path2owners);
- assertThat(c2Response).contains(owner2paths);
- assertThat(c2Response).contains("file2owners:{ ./t.c:[ a@a, x@x ] }");
- assertThat(c2Response).contains(ownersAX);
- assertThat(c2Response).contains(expectedInLog);
- // A submitted change gets owners info from current repository.
- String c1Response = getOwnersDebugResponse(c1);
- assertThat(c1Response).contains(path2owners);
- assertThat(c1Response).contains(owner2paths);
- assertThat(c1Response).contains("file2owners:{ ./OWNERS:[ a@a, x@x ] }");
- assertThat(c1Response).contains(ownersAX);
- }
-
- @Test
- public void includeFoundTest() throws Exception {
- // Compared with includeNotFoundTest, this one has file "f2" to include.
- addFile("c0", "f2", "g1@g\ng2@g\n");
- // c2 and c1 are both submitted before existence of OWNERS.
- PushOneCommit.Result c2 = addFile("c2", "t.c", "##");
- PushOneCommit.Result c1 = addFile("c1", "OWNERS",
- "x@x\na@a\ninclude P1/P2 : f1\ninclude ./d1/d2/../../f2\n");
- String ownerA = ownerJson("a@a");
- String ownerX = ownerJson("x@x");
- String ownerG1 = ownerJson("g1@g");
- String ownerG2 = ownerJson("g2@g");
- String ownersAG1G2X =
- "owners:[ " + ownerA + ", " + ownerG1 + ", " + ownerG2 + ", " + ownerX + " ]";
- String path2owners = "path2owners:{ ./:[ a@a, g1@g, g2@g, x@x ] }";
- String owner2paths = "owner2paths:{ a@a:[ ./ ], g1@g:[ ./ ], g2@g:[ ./ ], x@x:[ ./ ] }";
- String projectName = project.get();
- String expectedInLog = "project:" + projectName + ", "
- + "ownersFileName:OWNERS, "
- + "getBranchId:refs/heads/master(FOUND), "
- + "findOwnersFileFor:./t.c, "
- + "findOwnersFileIn:., "
- + "getFile:OWNERS:(...), "
- + "parseLine:include:P1/P2:f1, "
- + "getRepoFile:P1/P2:refs/heads/master:f1, "
- + "getRepoFileException:repositorynotfound:P1/P2, "
- + "parseLine:include:(), " // P1/P2 is still not found
- + "parseLine:include:" + projectName + ":./d1/d2/../../f2, "
- + "getRepoFile:" + projectName + ":refs/heads/master:f2, "
- + "getFile:f2:(...), " // f2 is included
- + "countNumOwners, "
- + "findOwners, "
- + "checkFile:./t.c, "
- + "checkDir:., "
- + "addOwnerWeightsIn:./ "
- + "] ";
- String c2Response = getOwnersDebugResponse(c2);
- assertThat(c2Response).contains(path2owners);
- assertThat(c2Response).contains(owner2paths);
- assertThat(c2Response).contains("file2owners:{ ./t.c:[ a@a, g1@g, g2@g, x@x ] }");
- assertThat(c2Response).contains(ownersAG1G2X);
- assertThat(c2Response).contains(expectedInLog);
- // A submitted change gets owners info from current repository.
- String c1Response = getOwnersDebugResponse(c1);
- assertThat(c1Response).contains(path2owners);
- assertThat(c1Response).contains(owner2paths);
- assertThat(c1Response).contains("file2owners:{ ./OWNERS:[ a@a, g1@g, g2@g, x@x ] }");
- assertThat(c1Response).contains(ownersAG1G2X);
- }
-
- @Test
- public void includeIndirectFileTest() throws Exception {
- // Test indirectly included file and relative file path.
- addFile("1", "d1/f2", "d1f2@g\n");
- addFile("2", "d2/f2", "d2f2@g\n");
- addFile("3", "d3/f2", "d3f2@g\n");
- addFile("4", "d1/d2/owners", "d1d2@g\ninclude ../f2\n");
- addFile("5", "d2/d2/owners", "d2d2@g\ninclude ../f2\n");
- addFile("6", "d3/d2/owners", "d3d2@g\ninclude ../f2\n");
- addFile("7", "d3/OWNERS", "d3@g\ninclude ../d2/d2/owners\n");
- addFile("8", "OWNERS", "x@g\n");
- PushOneCommit.Result c1 = createChange("c1", "d3/t.c", "Hello!");
- // d3's owners are in d3/OWNERS, d2/d2/owners, d2/f2, OWNERS,
- // If the include directories are based on original directory d3,
- // then the included files will be d2/d2/owners and d3/f2.
- String ownerD3 = ownerJson("d3@g");
- String ownerD2 = ownerJson("d2d2@g");
- String ownerF2 = ownerJson("d2f2@g");
- String ownerX = ownerJson("x@g", 0, 1, 0);
- assertThat(getOwnersResponse(c1)).contains("owners:[ " + ownerD2 + ", "
- + ownerF2 + ", " + ownerD3 + ", " + ownerX + " ], files:[ d3/t.c ]");
- }
-
- @Test
- public void includeVsFileTest() throws Exception {
- // Test difference between include and file statements.
- // The file statement skips "set noparent" and "per-file" statements.
- addFile("d1", "d1/OWNERS", "d1@g\n");
- addFile("d1/d1", "d1/d1/OWNERS",
- "per-file *.c=d1d1p@g\nd1d1@g\nfile: d1/OWNERS\n");
- addFile("d1/d1/d1", "d1/d1/d1/OWNERS",
- "set noparent\nper-file *.c=d1d1d1p@g\nd1d1d1@g\n");
- addFile("d1/d2", "d1/d2/OWNERS",
- "per-file *.c=d1d2p@g\nd1d2@g\ninclude d1/OWNERS\n");
- addFile("d1/d2/d1", "d1/d2/d1/OWNERS",
- "set noparent\nper-file *.c=d1d2d1p@g\nd1d2d1@g\n");
-
- addFile("d2", "d2/OWNERS", "d2@g\n");
- addFile("d2/d1", "d2/d1/OWNERS",
- "per-file *.c=d2d1p@g\nd2d1@g\nfile: ./d1/OWNERS\n");
- addFile("d2/d1/d1", "d2/d1/d1/OWNERS",
- "set noparent\nper-file *.c=d2d1d1p@g\nd2d1d1@g\n");
- addFile("d2/d2", "d2/d2/OWNERS",
- "per-file *.c=d2d2p@g\nd2d2@g\ninclude ./d1/OWNERS\n");
- addFile("d2/d2/d1", "d2/d2/d1/OWNERS",
- "set noparent\nper-file *.c=d2d2d1p@g\nd2d2d1@g\n");
-
- addFile("d3", "d3/OWNERS", "d3@g\n");
- addFile("d3/d1/d1", "d3/d1/d1/OWNERS", "d3d1d1@g\nfile: ../../../d1/d1/OWNERS\n");
- addFile("d3/d1/d2", "d3/d1/d2/OWNERS", "d3d1d2@g\nfile: //d1/d2/OWNERS\n");
- addFile("d3/d2/d1", "d3/d2/d1/OWNERS", "d3d2d1@g\ninclude /d2/d1/OWNERS\n");
- addFile("d3/d2/d2", "d3/d2/d2/OWNERS", "d3d2d2@g\ninclude //d2/d2/OWNERS\n");
- PushOneCommit.Result c11 = createChange("c11", "d3/d1/d1/t.c", "test");
- PushOneCommit.Result c12 = createChange("c12", "d3/d1/d2/t.c", "test");
- PushOneCommit.Result c21 = createChange("c21", "d3/d2/d1/t.c", "test");
- PushOneCommit.Result c22 = createChange("c22", "d3/d2/d2/t.c", "test");
-
- // file and file
- String owners11 = "file2owners:{ ./d3/d1/d1/t.c:"
- + "[ d1d1@g, d1d1d1@g, d3@g, d3d1d1@g ] }";
- // file and include
- String owners12 = "file2owners:{ ./d3/d1/d2/t.c:"
- + "[ d1d2@g, d1d2d1@g, d3@g, d3d1d2@g ] }";
- // include and file
- String owners21 = "file2owners:{ ./d3/d2/d1/t.c:"
- + "[ d2d1@g, d2d1d1@g, d2d1p@g, d3@g, d3d2d1@g ] }";
- // include and include
- String owners22 = "file2owners:{ ./d3/d2/d2/t.c:"
- + "[ d2d2@g, d2d2d1@g, d2d2d1p@g, d2d2p@g, d3d2d2@g ] }";
-
- assertThat(getOwnersDebugResponse(c11)).contains(owners11);
- assertThat(getOwnersDebugResponse(c12)).contains(owners12);
- assertThat(getOwnersDebugResponse(c21)).contains(owners21);
- assertThat(getOwnersDebugResponse(c22)).contains(owners22);
- }
-
- @Test
- public void multipleIncludeTest() throws Exception {
- // Now "include" and "file:" statements can share parsed results.
- addFile("d1", "d1/OWNERS", "d1@g\n");
- addFile("d2/d1", "d2/d1/OWNERS", "include /d1/OWNERS\nfile://d1/OWNERS\n");
- addFile("d2/d2", "d2/d2/OWNERS", "file: //d1/OWNERS\ninclude /d1/OWNERS\n");
- PushOneCommit.Result c1 = createChange("c1", "d2/d1/t.c", "test");
- PushOneCommit.Result c2 = createChange("c2", "d2/d2/t.c", "test");
- String projectName = project.get();
- String log1 = "parseLine:useSaved:file:" + projectName + "://d1/OWNERS, ";
- String log2 = "parseLine:useSaved:include:" + projectName + ":/d1/OWNERS, ";
- String response1 = getOwnersDebugResponse(c1);
- String response2 = getOwnersDebugResponse(c2);
- assertThat(response1).contains(log1);
- assertThat(response1).doesNotContain(log2);
- assertThat(response2).doesNotContain(log1);
- assertThat(response2).contains(log2);
- }
-
- @Test
- public void includeCycleTest() throws Exception {
- // f1 includes f2, f2 includes f3, f3 includes f4, f4 includes f2, OWNERS includes f1.
- // All files are in the root directory, but could be referred with relative paths.
- addFile("1", "f1", "f1@g\ninclude ./f2\n");
- addFile("2", "f2", "f2@g\ninclude d1/../f3\n");
- addFile("3", "f3", "f3@g\ninclude /f4\n");
- addFile("4", "f4", "f4@g\ninclude d2/../f2\n");
- addFile("5", "OWNERS", "x@g\ninclude ./d1/../f1\n");
- PushOneCommit.Result c = createChange("6", "t.c", "#\n");
- String response = getOwnersDebugResponse(c);
- String projectName = project.get();
- String expectedInLog = "project:" + projectName + ", "
- + "ownersFileName:OWNERS, "
- + "getBranchId:refs/heads/master(FOUND), "
- + "findOwnersFileFor:./t.c, "
- + "findOwnersFileIn:., "
- + "getFile:OWNERS:(...), "
- + "parseLine:include:" + projectName + ":./d1/../f1, "
- + "getRepoFile:" + projectName + ":refs/heads/master:f1, "
- + "getFile:f1:(...), "
- + "parseLine:include:" + projectName + ":./f2, "
- + "getRepoFile:" + projectName + ":refs/heads/master:f2, "
- + "getFile:f2:(...), "
- + "parseLine:include:" + projectName + ":d1/../f3, "
- + "getRepoFile:" + projectName + ":refs/heads/master:f3, "
- + "getFile:f3:(...), "
- + "parseLine:include:" + projectName + ":/f4, "
- + "getRepoFile:" + projectName + ":refs/heads/master:f4, "
- + "getFile:f4:(...), "
- + "parseLine:errorRecursion:include:" + projectName + ":d2/../f2, "
- + "countNumOwners, "
- + "findOwners, "
- + "checkFile:./t.c, "
- + "checkDir:., "
- + "addOwnerWeightsIn:./ "
- + "] ";
- assertThat(response).contains("path2owners:{ ./:[ f1@g, f2@g, f3@g, f4@g, x@g ] }");
- assertThat(response).contains(
- "owner2paths:{ f1@g:[ ./ ], f2@g:[ ./ ], f3@g:[ ./ ], f4@g:[ ./ ], x@g:[ ./ ] }");
- assertThat(response).contains(expectedInLog);
- }
-
- @Test
- public void includeDuplicationTest() throws Exception {
- // f0 is included into f1, f2, f3,
- // f2 is included into f4 and f5; f4 is included into f5.
- // f0, f1, f2, f3, f5 are included into d6/OWNERS.
- addFile("0", "d0/f0", "f0@g\n");
- addFile("1", "d1/d2/f1", "f1@g\ninclude ../../d0/f0\n");
- addFile("2", "d2/f2", "f2@g\ninclude ../d0/f0\n");
- addFile("3", "d2/d3/f3", "f3@g\ninclude /d0/f0\n");
- addFile("4", "d4/f4", "f4@g\ninclude ../d2/f2\n");
- addFile("5", "d4/d5/f5", "f5@g\ninclude /d2/f2\ninclude ../f4\n");
- PushOneCommit.Result c = addFile("6", "d6/OWNERS",
- "f6@g\ninclude /d0/f0\ninclude ../d1/d2/f1\n"
- + "include ../d2/f2\ninclude /d2/d3/f3\ninclude /d2/../d4/d5/f5\ninclude /d4/f4\n");
- String result = getOwnersDebugResponse(c);
- assertThat(result).contains("{ ./d6/OWNERS:[ f0@g, f1@g, f2@g, f3@g, f4@g, f5@g, f6@g ] }");
- String projectName = project.get();
- String skipLog = "parseLine:useSaved:include:" + projectName + ":";
- for (String path : new String[]{"../../d0/f0", "../d0/f0", "../d2/f2", "/d2/f2", "/d4/f4"}) {
- assertThat(result).contains(skipLog + path);
- }
- String expectedInLog = "project:" + projectName + ", "
- + "ownersFileName:OWNERS, "
- + "getBranchId:refs/heads/master(FOUND), "
- + "findOwnersFileFor:./d6/OWNERS, "
- + "findOwnersFileIn:./d6, "
- + "getFile:d6/OWNERS:(...), "
- + "parseLine:include:" + projectName + ":/d0/f0, "
- + "getRepoFile:" + projectName + ":refs/heads/master:d0/f0, "
- + "getFile:d0/f0:(...), "
- + "parseLine:include:" + projectName + ":../d1/d2/f1, "
- + "getRepoFile:" + projectName + ":refs/heads/master:d1/d2/f1, "
- + "getFile:d1/d2/f1:(...), "
- + "parseLine:useSaved:include:" + projectName + ":../../d0/f0, "
- + "parseLine:include:" + projectName + ":../d2/f2, "
- + "getRepoFile:" + projectName + ":refs/heads/master:d2/f2, "
- + "getFile:d2/f2:(...), "
- + "parseLine:useSaved:include:" + projectName + ":../d0/f0, "
- + "parseLine:include:" + projectName + ":/d2/d3/f3, "
- + "getRepoFile:" + projectName + ":refs/heads/master:d2/d3/f3, "
- + "getFile:d2/d3/f3:(...), "
- + "parseLine:useSaved:include:" + projectName + ":/d0/f0, "
- + "parseLine:include:" + projectName + ":/d2/../d4/d5/f5, "
- + "getRepoFile:" + projectName + ":refs/heads/master:d4/d5/f5, "
- + "getFile:d4/d5/f5:(...), "
- + "parseLine:useSaved:include:" + projectName + ":/d2/f2, "
- + "parseLine:include:" + projectName + ":../f4, "
- + "getRepoFile:" + projectName + ":refs/heads/master:d4/f4, "
- + "getFile:d4/f4:(...), "
- + "parseLine:useSaved:include:" + projectName + ":../d2/f2, "
- + "parseLine:useSaved:include:" + projectName + ":/d4/f4, "
- + "findOwnersFileIn:., "
- + "getFile:OWNERS(NOTFOUND), "
- + "countNumOwners, "
- + "findOwners, "
- + "checkFile:./d6/OWNERS, "
- + "checkDir:./d6, "
- + "checkDir:., "
- + "addOwnerWeightsIn:./d6/ "
- + "] ";
- assertThat(result).contains(expectedInLog);
- }
-
- @Test
- public void ownersPerFileTest() throws Exception {
- addFile("1", "OWNERS", "per-file *.c=x@x\na@a\nc@c\nb@b\n");
- // Add "t.c" file, which has per-file owner x@x, and a@a, b@b, c@c.
- PushOneCommit.Result c2 = createChange("2", "t.c", "Hello!");
- String ownerA = ownerJson("a@a");
- String ownerB = ownerJson("b@b");
- String ownerC = ownerJson("c@c");
- String ownerABC = "owners:[ " +ownerA + ", " + ownerB + ", " + ownerC;
- String ownerX = ownerJson("x@x");
- assertThat(getOwnersResponse(c2)).contains(ownerABC + ", " + ownerX + " ], files:[ t.c ]");
- // Add "t.txt" file, which has only global default owners.
- PushOneCommit.Result c3 = createChange("3", "t.txt", "Test!");
- assertThat(getOwnersResponse(c3)).contains(ownerABC + " ], files:[ t.txt ]");
- }
-
- @Test
- public void perFileIncludeTest() throws Exception {
- // A per-file with file: directive to include more owners.
- addFile("1", "OWNERS", "per-file *.c=x@x\na@a\nper-file t.c=file: t_owner\n");
- addFile("2", "t_owner", "t1@g\n*\nper-file *.c=y@y\ninclude more_owner\n");
- addFile("3", "more_owner", "m@g\nm2@g\nper-file *.c=z@z\n");
- PushOneCommit.Result c1 = createChange("c1", "x.c", "test");
- PushOneCommit.Result c2 = createChange("c2", "t.c", "test");
- String c1Response = getOwnersDebugResponse(c1);
- String c2Response = getOwnersDebugResponse(c2);
- assertThat(c1Response).contains("file2owners:{ ./x.c:[ a@a, x@x ] }");
- assertThat(c2Response).contains("file2owners:{ ./t.c:[ *, a@a, m2@g, m@g, t1@g, x@x ] }");
- }
-
- @Test
- public void includePerFileTest() throws Exception {
- // Test included file with per-file, which affects the including file.
- PushOneCommit.Result c1 = addFile("1", "d1/d1/OWNERS", "d1d1@g\nper-file OWNERS=d1d1o@g\n");
- PushOneCommit.Result c2 = addFile("2", "d1/OWNERS", "d1@g\nper-file OWNERS=d1o@g\n");
- PushOneCommit.Result c3 = addFile("3", "d2/d1/OWNERS", "d2d1@g\ninclude ../../d1/d1/OWNERS\n");
- PushOneCommit.Result c4 = addFile("4", "d2/OWNERS", "d2@g\nper-file OWNERS=d2o@g");
- // Files that match per-file globs now inherit global default owners.
- assertThat(getOwnersResponse(c1)).contains(
- "{ ./d1/d1/OWNERS:[ d1@g, d1d1@g, d1d1o@g, d1o@g ] }");
- assertThat(getOwnersResponse(c2)).contains("{ ./d1/OWNERS:[ d1@g, d1o@g ] }");
- assertThat(getOwnersResponse(c3)).contains(
- "{ ./d2/d1/OWNERS:[ d1d1@g, d1d1o@g, d2@g, d2d1@g, d2o@g ] }");
- assertThat(getOwnersResponse(c4)).contains("{ ./d2/OWNERS:[ d2@g, d2o@g ] }");
- }
-
- @Test
- public void includePerFileNoParentTest() throws Exception {
- // Test included file with per-file and set noparent, which affects the including file.
- PushOneCommit.Result c1 = addFile("1", "d1/d1/OWNERS",
- "d1d1@g\nper-file OW* = set noparent\nper-file OWNERS=d1d1o@g\n");
- PushOneCommit.Result c2 = addFile("2", "d1/OWNERS",
- "d1@g\nper-file OWNERS=d1o@g\nper-file * = set noparent\n");
- PushOneCommit.Result c3 = addFile( "3", "d2/d1/OWNERS",
- "per-file O*S=d2d1o@g\nd2d1@g\ninclude ../../d1/d1/OWNERS\n");
- PushOneCommit.Result c4 = addFile("4",
- "d2/OWNERS", "d2@g\nper-file OWNERS=d2o@g\nper-file *S=set noparent \n");
- // Files that match per-file globs with set noparent do not inherit global default owners.
- // But include directive can include more per-file owners as in c3.
- assertThat(getOwnersResponse(c1)).contains("{ ./d1/d1/OWNERS:[ d1d1o@g ] }");
- assertThat(getOwnersResponse(c2)).contains("{ ./d1/OWNERS:[ d1o@g ] }");
- assertThat(getOwnersResponse(c3)).contains("{ ./d2/d1/OWNERS:[ d1d1o@g, d2d1o@g ] }");
- assertThat(getOwnersResponse(c4)).contains("{ ./d2/OWNERS:[ d2o@g ] }");
- }
-
- @Test
- public void includeNoParentTest() throws Exception {
- // Test included file with noparent, which affects the inheritance of including file.
- PushOneCommit.Result c1 = addFile("1", "d1/d1/OWNERS", "d1d1@g\nset noparent\n");
- PushOneCommit.Result c2 = addFile("2", "d1/d2/OWNERS", "d1d2@g\n");
- PushOneCommit.Result c3 = addFile("3", "d1/OWNERS", "d1@g\n");
- PushOneCommit.Result c4 = addFile("4", "d2/d1/OWNERS", "d2d1@g\ninclude ../../d1/d1/OWNERS\n");
- PushOneCommit.Result c5 = addFile("5", "d2/d2/OWNERS", "d2d2@g\ninclude ../../d1/d2/OWNERS");
- PushOneCommit.Result c6 = addFile("6", "d2/OWNERS", "d2@g\n");
- // d1/d1/OWNERS sets noparent, does not inherit d1/OWNERS
- assertThat(getOwnersResponse(c1)).contains("{ ./d1/d1/OWNERS:[ d1d1@g ] }");
- // d1/d2/OWNERS inherits d1/OWNERS
- assertThat(getOwnersResponse(c2)).contains("{ ./d1/d2/OWNERS:[ d1@g, d1d2@g ] }");
- assertThat(getOwnersResponse(c3)).contains("{ ./d1/OWNERS:[ d1@g ] }");
- // d2/d1/OWNERS includes d1/d1/OWNERS, does not inherit d1/OWNERS or d2/OWNERS
- assertThat(getOwnersResponse(c4)).contains("{ ./d2/d1/OWNERS:[ d1d1@g, d2d1@g ] }");
- // d2/d2/OWNERS includes d1/d1/OWNERS, inherit d2/OWNERS but not d1/OWNERS
- assertThat(getOwnersResponse(c5)).contains("{ ./d2/d2/OWNERS:[ d1d2@g, d2@g, d2d2@g ] }");
- assertThat(getOwnersResponse(c6)).contains("{ ./d2/OWNERS:[ d2@g ] }");
- }
-
- @Test
- public void includeProjectOwnerTest() throws Exception {
- // Test include directive with other project name.
- Project.NameKey pA = newProject("PA");
- Project.NameKey pB = newProject("PB");
- String nameA = pA.get();
- String nameB = pB.get();
- switchProject(pA);
- addFile("1", "f1", "pAf1@g\ninclude ./d1/f1\n");
- addFile("2", "d1/f1", "pAd1f1@g\ninclude " + nameB + ":" + "/d2/f2\n");
- addFile("3", "d2/OWNERS", "pAd2@g\n include " + nameA + " : " + "../f1\n");
- addFile("4", "OWNERS", "pA@g\n");
- switchProject(pB);
- addFile("5", "f1", "pBf1@g\ninclude ./d1/f1\n");
- addFile("6", "f2", "pBf2@g\n");
- addFile("7", "d1/f1", "pBd1f1@g\n");
- addFile("8", "d2/f2", "pBd2f2@g\ninclude ../f1\n");
- switchProject(pA);
- PushOneCommit.Result c1 = createChange("c1", "d2/t.c", "Hello!");
- // included: pA:d2/OWNERS, pA:d2/../f1, pA:d1/f1, pB:d2/f2, pB:d2/../f1, pB:./d1/f1
- // inherited: pA:OWNERS
- String owners = "owners:[ " + ownerJson("pAd1f1@g") + ", " + ownerJson("pAd2@g") + ", "
- + ownerJson("pAf1@g") + ", " + ownerJson("pBd1f1@g") + ", " + ownerJson("pBd2f2@g")
- + ", " + ownerJson("pBf1@g") + ", " + ownerJson("pA@g", 0, 1, 0) + " ]";
- assertThat(getOwnersResponse(c1)).contains(owners);
- }
-
- @Test
- public void subOwnersFileTest() throws Exception {
- // Add OWNERS file in root and subdirectories.
- addFile("1", "OWNERS", "x@x\n");
- addFile("2", "d1/OWNERS", "a@a\n");
- addFile("3", "d2/OWNERS", "y@y\n");
- addFile("4", "d3/OWNERS", "b@b\nset noparent\n");
- addFile("5", "d4/OWNERS", "z@z\ninclude ../d2/OWNERS");
- // Add "t.c" file, which is not owned by subdirectory owners.
- PushOneCommit.Result c2 = createChange("c2", "t.c", "Hello!");
- String ownerA = ownerJson("a@a");
- String ownerX = ownerJson("x@x");
- assertThat(getOwnersResponse(c2)).contains("owners:[ " + ownerX + " ], files:[ t.c ]");
- // Add "d1/t.c" file, which is owned by ./d1 and root owners.
- PushOneCommit.Result c3 = createChange("c3", "d1/t.c", "Hello!");
- String ownerX010 = ownerJson("x@x", 0, 1, 0);
- assertThat(getOwnersResponse(c3))
- .contains("owners:[ " + ownerA + ", " + ownerX010 + " ], files:[ d1/t.c ]");
- // Add "d2/t.c" file, which is owned by ./d2 and root owners.
- PushOneCommit.Result c4 = createChange("c4", "d2/t.c", "Hello!");
- String ownerY = ownerJson("y@y");
- assertThat(getOwnersResponse(c4))
- .contains("owners:[ " + ownerY + ", " + ownerX010 + " ], files:[ d2/t.c ]");
- // Add "d2/d1/t.c" file, which is owned by ./d2 and root owners.
- PushOneCommit.Result c5 = createChange("c5", "d2/d1/t.c", "Hello!");
- assertThat(getOwnersResponse(c5)).contains(
- "owners:[ " + ownerY + ", " + ownerX010 + " ], files:[ d2/d1/t.c ]");
- // Add "d3/t.c" file, which is owned only by ./d3 owners due to "set noparent".
- PushOneCommit.Result c6 = createChange("c6", "d3/t.c", "Hello!");
- String ownerB = ownerJson("b@b");
- assertThat(getOwnersResponse(c6)).contains("owners:[ " + ownerB + " ], files:[ d3/t.c ]");
- // Add "d3/d1/t.c" file, which is owned only by ./d3 owners due to "set noparent".
- PushOneCommit.Result c7 = createChange("c7", "d3/d1/t.c", "Hello!");
- assertThat(getOwnersResponse(c7)).contains(
- "owners:[ " + ownerB + " ], files:[ d3/d1/t.c ]");
- // Add "d4/t.c" file, which is owned by ./d4 and ./d2 owners, but not root owners.
- PushOneCommit.Result c8 = createChange("c8", "d4/t.c", "Hello!");
- String ownerZ = ownerJson("z@z");
- assertThat(getOwnersResponse(c8)).contains(
- "owners:[ " + ownerY + ", " + ownerZ + ", " + ownerX010 + " ], files:[ d4/t.c ]");
- }
-
- @Test
- public void requestErrorTest() throws Exception {
- PushOneCommit.Result c1 = createChange("1", "t.c", "##");
- assertThat(getOwnersResponse(c1)).contains("owners:[], files:[ t.c ]");
- int id = c1.getChange().getId().get();
- // Correct change id.
- String result = userRestSession.get("/changes/" + id + "/owners").getEntityContent();
- assertThat(filteredJson(result)).contains("owners:[], files:[ t.c ]");
- // Wrong change number, 404 not found.
- RestResponse response = userRestSession.get("/changes/" + (id + 1) + "/owners");
- assertThat(response.getStatusCode()).isEqualTo(404);
- assertThat(response.getEntityContent()).isEqualTo("Not found: " + (id + 1));
- // Wrong request parameter, 400 not a valid option
- response = userRestSession.get("/changes/" + id + "/owners?xyz=3");
- assertThat(response.getStatusCode()).isEqualTo(400);
- assertThat(response.getEntityContent()).isEqualTo("\"--xyz\" is not a valid option");
- // Wrong patchset parameter, no content
- response = userRestSession.get("/changes/" + id + "/owners?patchset=2");
- assertThat(response.getStatusCode()).isEqualTo(204);
- assertThat(response.hasContent()).isFalse();
- }
-
- @Test
- public void accountTest() throws Exception {
- String[] users = {"user1", "user2", "user3"};
- String[] emails1 = {"abc@g.com", "abc+xyz@g.com", "xyz-team+review@g.com"};
- String[] emails2 = {"abc@goog.com", "abc+xyz2@g.com", "xyz-team@goog.com"};
- // Create accounts with given user name, first and second email addresses.
- for (int i = 0; i < users.length; i++) {
- accountCreator.create(users[i], emails1[i], "FullName " + users[i]).getId();
- EmailInput input = new EmailInput();
- input.email = emails2[i];
- input.noConfirmation = true;
- gApi.accounts().id(users[i]).addEmail(input);
- }
- // Find accounts with given first and second email addresses.
- // OwnersDb uses either emails.getAccountFor or getAccountsFor to get preferred email addresses.
- Multimap<String, Account.Id> map1 = emails.getAccountsFor(emails1);
- Multimap<String, Account.Id> map2 = emails.getAccountsFor(emails2);
- for (int i = 0; i < users.length; i++) {
- Collection<Account.Id> ids1 = emails.getAccountFor(emails1[i]);
- Collection<Account.Id> ids2 = emails.getAccountFor(emails2[i]);
- Collection<Account.Id> ids3 = map1.get(emails1[i]);
- Collection<Account.Id> ids4 = map2.get(emails2[i]);
- assertThat(ids1).hasSize(1);
- assertThat(ids2).hasSize(1);
- assertThat(ids3).hasSize(1);
- assertThat(ids4).hasSize(1);
- Account.Id id1 = ids1.iterator().next();
- Account.Id id2 = ids2.iterator().next();
- Account.Id id3 = ids3.iterator().next();
- Account.Id id4 = ids4.iterator().next();
- assertThat(id1).isEqualTo(id2); // Both emails should find the same account.
- assertThat(id1).isEqualTo(id3);
- assertThat(id1).isEqualTo(id4);
- // Action.getReviewers and Checker.getVotes use accountCache to get email address.
- Optional<Account> account = accountCache.get(id1).map(AccountState::getAccount);
- assertThat(account).named("account %s", id1).isPresent();
- assertThat(account.get().getPreferredEmail()).isEqualTo(emails1[i]);
- }
- // Wrong or non-existing email address.
- String[] wrongEmails = {"nobody", "@g.com", "nobody@g.com", "*"};
- Multimap<String, Account.Id> email2ids = emails.getAccountsFor(wrongEmails);
- for (String email : wrongEmails) {
- assertThat(emails.getAccountFor(email)).isEmpty();
- assertThat(email2ids).doesNotContainKey(email);
- }
- }
-
- @Test
- public void projectTest() throws Exception {
- RestResponse response = adminRestSession.get("/projects/?d");
- String content = response.getEntityContent();
- // Predefined projects: "All-Projects", "All-Users", project
- assertThat(content).contains("\"id\": \"All-Projects\",");
- assertThat(content).contains("\"id\": \"All-Users\",");
- assertThat(content).contains(idProject("projectTest", "project"));
- assertThat(content).doesNotContain(idProject("projectTest", "ProjectA"));
- newProject("ProjectA");
- response = adminRestSession.get("/projects/?d");
- assertThat(response.getEntityContent()).contains(idProject("projectTest", "ProjectA"));
- }
-
- @Test
- public void projectInheritanceTest() throws Exception {
- Project.NameKey pA = newProject("Project_A");
- Project.NameKey pB = newProject("Project_B", pA);
- Project.NameKey pC = newProject("Project_C", pB);
- assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS");
- assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS");
- assertThat(projectOwnersFileName(pC)).isEqualTo("OWNERS");
- switchProject(pA);
- setProjectConfig("ownersFileName", "OWNERS_A");
- assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS_A");
- assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS_A");
- assertThat(projectOwnersFileName(pC)).isEqualTo("OWNERS_A");
- switchProject(pC);
- setProjectConfig("ownersFileName", "OWNERS_C");
- assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS_A");
- assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS_A");
- assertThat(projectOwnersFileName(pC)).isEqualTo("OWNERS_C");
- switchProject(pB);
- setProjectConfig("ownersFileName", "OWNERS_B");
- assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS_A");
- assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS_B");
- assertThat(projectOwnersFileName(pC)).isEqualTo("OWNERS_C");
- switchProject(pC);
- }
-
- @Test
- public void ownersFileNameTest() throws Exception {
- // Default project is something like ....FindOwnersIT..._project
- Project.NameKey pA = newProject("Project_A");
- Project.NameKey pB = newProject("Project_B");
- // Add OWNERS and OWNERS.alpha file to Project_A.
- switchProject(pA);
- createBranch("BranchX");
- addFile("1", "OWNERS", "per-file *.c=x@x\n"); // default owner x@x
- addFile("2", "OWNERS.alpha", "per-file *.c=a@a\n"); // alpha owner a@a
- PushOneCommit.Result cA = createChange("cA", "tA.c", "Hello A!");
- PushOneCommit.Result cX = createChangeInBranch("BranchX", "cX", "tX.c", "Hello X!");
- // Add OWNERS and OWNERS.beta file to Project_B.
- switchProject(pB);
- createBranch("BranchY");
- addFile("3", "OWNERS", "per-file *.c=y@y\n"); // default owner y@y
- addFile("4", "OWNERS.beta", "per-file *.c=b@b\n"); // beta owner b@b
- PushOneCommit.Result cB = createChange("cB", "tB.c", "Hello B!");
- PushOneCommit.Result cY = createChangeInBranch("BranchY", "cY", "tY.c", "Hello Y!");
-
- // Default owners file name is "OWNERS".
- assertThat(Config.OWNERS).isEqualTo("OWNERS");
- assertThat(config.getDefaultOwnersFileName()).isEqualTo("OWNERS");
- assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS");
- assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS");
-
- String ownerX = oneOwnerList("x@x");
- String ownerY = oneOwnerList("y@y");
- String cAResponse = getOwnersDebugResponse(cA);
- String cXResponse = getOwnersDebugResponse(cX);
- String cBResponse = getOwnersDebugResponse(cB);
- String cYResponse = getOwnersDebugResponse(cY);
- assertThat(cAResponse).contains(ownerX + ", files:[ tA.c ]");
- assertThat(cBResponse).contains(ownerY + ", files:[ tB.c ]");
- assertThat(cXResponse).contains(", files:[ tX.c ]");
- assertThat(cYResponse).contains(", files:[ tY.c ]");
- assertThat(cXResponse).doesNotContain(ownerX);
- assertThat(cYResponse).doesNotContain(ownerY);
- assertThat(cAResponse).contains("branch:refs/heads/master");
- assertThat(cBResponse).contains("branch:refs/heads/master");
- assertThat(cXResponse).contains("branch:refs/heads/BranchX");
- assertThat(cYResponse).contains("branch:refs/heads/BranchY");
- assertThat(cAResponse).contains("ownersFileName:OWNERS, ");
- assertThat(cBResponse).contains("ownersFileName:OWNERS, ");
- assertThat(cXResponse).contains("ownersFileName:OWNERS, ");
- assertThat(cYResponse).contains("ownersFileName:OWNERS, ");
-
- // pA and pB use default OWNERS file name.
- // cA and cB logs should not contain anything about Missing/Found root.
- assertThat(cAResponse).doesNotContain("root");
- assertThat(cBResponse).doesNotContain("root");
- // cX and cY are not for the master branch.
- // They should not contain anything about Missing/Found root.
- assertThat(cXResponse).doesNotContain("root");
- assertThat(cYResponse).doesNotContain("root");
-
- // Change owners file name to "OWNERS.alpha" and "OWNERS.beta"
- switchProject(pA);
- setProjectConfig("ownersFileName", "OWNERS.alpha");
- switchProject(pB);
- setProjectConfig("ownersFileName", "OWNERS.beta");
-
- assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS.alpha");
- assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS.beta");
- String ownerA = oneOwnerList("a@a");
- String ownerB = oneOwnerList("b@b");
- cAResponse = getOwnersDebugResponse(cA);
- cBResponse = getOwnersDebugResponse(cB);
- cXResponse = getOwnersDebugResponse(cX);
- cYResponse = getOwnersDebugResponse(cY);
- assertThat(cAResponse).contains("ownersFileName:OWNERS.alpha, ");
- assertThat(cBResponse).contains("ownersFileName:OWNERS.beta, ");
- assertThat(cXResponse).contains("ownersFileName:OWNERS.alpha, ");
- assertThat(cYResponse).contains("ownersFileName:OWNERS.beta, ");
- assertThat(cAResponse).contains(ownerA + ", files:[ tA.c ]");
- assertThat(cBResponse).contains(ownerB + ", files:[ tB.c ]");
- // pA and pB now use non-default OWNERS file name.
- // cA and cB logs should contain "Found root ..."
- assertThat(cAResponse).contains("FoundrootOWNERS.alpha");
- assertThat(cBResponse).contains("FoundrootOWNERS.beta");
- assertThat(cXResponse).doesNotContain("root");
- assertThat(cYResponse).doesNotContain("root");
-
- // Now change owners file name to "MAINTAINERS"
- // logs should contain "Missing root ..."
- switchProject(pA);
- setProjectConfig("ownersFileName", "MAINTAINERS");
- cAResponse = getOwnersDebugResponse(cA);
- cXResponse = getOwnersDebugResponse(cX);
- assertThat(cAResponse).contains("ownersFileName:MAINTAINERS, ");
- assertThat(cXResponse).contains("ownersFileName:MAINTAINERS, ");
- assertThat(cAResponse).contains("owners:[], ");
- assertThat(cXResponse).contains("owners:[], ");
- assertThat(cAResponse).contains("MissingrootMAINTAINERS");
- // Gerrit server log file should contain: "Missing root MAINTAINERS for change "
- // cX is not on the master branch, so we do not check for the root owners file.
- assertThat(cXResponse).doesNotContain("root");
-
- // Change back to OWNERS in Project_A
- switchProject(pA);
- setProjectConfig("ownersFileName", "OWNERS");
- assertThat(projectOwnersFileName(pA)).isEqualTo("OWNERS");
- cAResponse = getOwnersDebugResponse(cA);
- cBResponse = getOwnersDebugResponse(cB);
- assertThat(cAResponse).contains(ownerX + ", files:[ tA.c ]");
- assertThat(cBResponse).contains(ownerB + ", files:[ tB.c ]");
-
- // Change back to OWNERS.alpha in Project_B, but there is no OWNERS.alpha
- switchProject(pB);
- setProjectConfig("ownersFileName", "OWNERS.alpha");
- assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS.alpha");
- cAResponse = getOwnersDebugResponse(cA);
- cBResponse = getOwnersDebugResponse(cB);
- cYResponse = getOwnersDebugResponse(cY);
- assertThat(cAResponse).contains("ownersFileName:OWNERS, ");
- assertThat(cBResponse).contains("ownersFileName:OWNERS.alpha, ");
- assertThat(cAResponse).contains(ownerX + ", files:[ tA.c ]");
- assertThat(cBResponse).contains("owners:[], files:[ tB.c ]");
- assertThat(cBResponse).contains("MissingrootOWNERS.alpha");
- // Gerrit server log file should contain: "Missing root OWNERS.alpha for change "
- assertThat(cYResponse).doesNotContain("root");
-
- // Do not accept empty string or all-white-spaces for ownersFileName.
- setProjectConfig("ownersFileName", " ");
- assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS");
- setProjectConfig("ownersFileName", " \t ");
- assertThat(projectOwnersFileName(pB)).isEqualTo("OWNERS");
- setProjectConfig("ownersFileName", "O");
- assertThat(projectOwnersFileName(pB)).isEqualTo("O");
- }
-
- @Test
- public void authorDefaultVoteTest() throws Exception {
- // CL author has default +1 owner vote.
- addFile("1", "d1/OWNERS", user.email + "\n"); // d1 owned by user
- addFile("2", "d2/OWNERS", admin.email + "\n"); // d2 owned by admin
- // admin is the author of CLs created by createChange.
- PushOneCommit.Result r1 = createChange("r1", "d1/t.c", "Hello1");
- PushOneCommit.Result r2 = createChange("r2", "d2/t.c", "Hello2");
- PushOneCommit.Result r3 = createChange("r3", "d3/t.c", "Hello3");
- assertThat(checkApproval(r1)).isEqualTo(-1); // owner is not change author
- assertThat(checkApproval(r2)).isEqualTo(1); // owner is change author, default +1
- assertThat(checkApproval(r3)).isEqualTo(0); // no owner is found in d3
- }
-
- @Test
- public void actionApplyTest() throws Exception {
- Cache cache = getCache().init(0, 10);
- assertThat(cache.size()).isEqualTo(0);
- // TODO: create ChangeInput in a new project.
- ChangeInfo changeInfo = newChangeInfo("test Action.apply");
- ChangeResource cr = parseChangeResource(changeInfo.changeId);
- Action.Parameters param = new Action.Parameters();
- Action action =
- new Action(
- pluginConfig,
- null,
- changeDataFactory,
- accountCache,
- emails,
- repoManager,
- projectCache);
- Response<RestResult> response = action.apply(cr, param);
- RestResult result = response.value();
- verifyRestResult(result, 1, 1, changeInfo._number, false);
- param.debug = true;
- response = action.apply(cr, param);
- result = response.value();
- verifyRestResult(result, 1, 1, changeInfo._number, true);
- assertThat(result.dbgmsgs.user).isEqualTo("?");
- assertThat(result.dbgmsgs.project).isEqualTo(changeInfo.project);
- // changeInfo.branch is "master" but result.dbgmsgs.branch is "refs/heads/master".
- assertThat(result.dbgmsgs.branch).contains(changeInfo.branch);
- assertThat(result.dbgmsgs.path2owners).isEmpty();
- assertThat(result.dbgmsgs.owner2paths).isEmpty();
- assertThat(result.file2owners).isEmpty();
- assertThat(result.reviewers).isEmpty();
- assertThat(result.owners).isEmpty();
- assertThat(result.files).isEmpty();
- // TODO: find expected value of ownerRevision.
- assertThat(cache.size()).isEqualTo(0);
- }
-
- private String oneOwnerList(String email) {
- return "owners:[ " + ownerJson(email) + " ]";
- }
-
- private String ownerJson(String email) {
- return "{ email:" + email + ", weights:[ 1, 0, 0 ] }";
- }
-
- private String ownerJson(String email, int w1, int w2, int w3) {
- return "{ email:" + email + ", weights:[ " + w1 + ", " + w2 + ", " + w3 + " ] }";
- }
-
- private ChangeInfo newChangeInfo(String subject) throws Exception {
- // should be called with different subject
- ChangeInput in = new ChangeInput();
- in.project = project.get();
- in.branch = "master";
- in.subject = subject;
- in.topic = "test empty change";
- in.status = ChangeStatus.NEW;
- return gApi.changes().create(in).get();
- }
-
- private String getFindOwnersResponse(ChangeInfo info) throws Exception {
- return filteredJson(
- userRestSession.get("/changes/" + info._number + "/revisions/1/find-owners"));
- }
-
- private String getOwnersResponse(ChangeInfo info) throws Exception {
- return filteredJson(userRestSession.get("/changes/" + info._number + "/owners"));
- }
-
- private String getOwnersResponse(PushOneCommit.Result change) throws Exception {
- return filteredJson(userRestSession.get("/changes/" + change.getChangeId() + "/owners"));
- }
-
- private String getOwnersDebugResponse(PushOneCommit.Result change) throws Exception {
- return filteredJson(
- userRestSession.get("/changes/" + change.getChangeId() + "/owners?debug=1"));
- }
-
- private void approveSubmit(PushOneCommit.Result change) throws Exception {
- approve(change.getChangeId());
- gApi.changes().id(change.getChangeId()).current().submit(new SubmitInput());
- }
-
- private PushOneCommit.Result addFile(
- String subject, String file, String content) throws Exception {
- PushOneCommit.Result c = createChange(subject, file, content);
- approveSubmit(c);
- return c;
- }
-
- private void switchProject(Project.NameKey p) throws Exception {
- project = p;
- testRepo = cloneProject(project);
- }
-
- private 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;
- }
-
- private void setProjectConfig(String var, String value) throws Exception {
- org.eclipse.jgit.lib.Config cfg = readProjectConfig();
- cfg.setString("plugin", "find-owners", var, value);
- assertThat(cfg.getString("plugin", "find-owners", var)).isEqualTo(value);
- PushOneCommit.Result commit =
- pushFactory
- .create(
- admin.getIdent(), // normal user cannot change refs/meta/config
- testRepo,
- "Update project config",
- "project.config",
- cfg.toText())
- .to("refs/for/" + REFS_CONFIG);
- commit.assertOkStatus();
- approveSubmit(commit);
- }
-
- private int checkApproval(PushOneCommit.Result r) throws Exception {
- Project.NameKey project = r.getChange().project();
- Cache cache = getCache().init(0, 0);
- OwnersDb db = cache.get(true, projectCache.get(project), accountCache, emails,
- repoManager, pluginConfig, r.getChange(), 1);
- Checker c = new Checker(repoManager, pluginConfig, null, r.getChange(), 1);
- return c.findApproval(accountCache, db);
- }
-
- // Remove '"' and space; replace '\n' with ' '; ignore "owner_revision" and "HostName:*".
- private static String filteredJson(String json) {
- return json.replaceAll("[\" ]*", "").replace('\n', ' ').replaceAll("owner_revision:[^ ]* ", "")
- .replaceAll("HostName:[^ ]*, ", "");
- }
-
- private static String filteredJson(RestResponse response) throws Exception {
- return filteredJson(response.getEntityContent());
- }
-
- private String myProjectName(String test, String project) {
- return this.getClass().getName() + "_" + test + "_" + project;
- }
-
- private String idProject(String test, String project) {
- // Expected string of "id": "project_name",
- return "\"id\": \"" + myProjectName(test, project) + "\",";
- }
-
- private 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();
- }
- }
-
- private BranchApi createBranch(String branch) throws Exception {
- return createBranch(new Branch.NameKey(project, branch));
- }
-
- private PushOneCommit.Result createChangeInBranch(
- String branch, String subject, String fileName, String content) throws Exception {
- PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo, subject, fileName, content);
- return push.to("refs/for/" + branch);
- }
-
- private Project.NameKey newProject(String name) {
- return newProject(name, project);
- }
-
- private Project.NameKey newProject(String myName, Project.NameKey parent) {
- return projectOperations.newProject().parent(parent).name(name(myName)).create();
- }
-
- private String projectOwnersFileName(Project.NameKey name) {
- return config.getOwnersFileName(projectCache.get(name), null);
- }
-
- private Cache getCache() {
- return Cache.getInstance(pluginConfig, repoManager);
- }
-}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/IncludeIT.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/IncludeIT.java
new file mode 100644
index 0000000..4c376e1
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/IncludeIT.java
@@ -0,0 +1,356 @@
+// Copyright (C) 2019 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 com.google.common.flogger.FluentLogger;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.reviewdb.client.Project;
+import org.junit.Rule;
+import org.junit.Test;
+
+/** Test find-owners plugin features related to include and file statements. */
+@TestPlugin(name = "find-owners", sysModule = "com.googlesource.gerrit.plugins.findowners.Module")
+public class IncludeIT extends FindOwners {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ @Rule public Watcher watcher = new Watcher(logger);
+
+ @Test
+ public void includeNotFoundTest() throws Exception {
+ // c2 and c1 are both submitted before existence of OWNERS.
+ PushOneCommit.Result c2 = addFile("1", "t.c", "##");
+ // Submitted c2 still finds no owners before c1 is submitted.
+ assertThat(getOwnersResponse(c2)).contains("owners:[], files:[ t.c ]");
+ PushOneCommit.Result c1 = addFile("2", "OWNERS",
+ "x@x\na@a\ninclude P1/P2 : f1\ninclude ./d1/d2/../../f2\n");
+ // Now c2 should find owners, but include directives find no repository or file.
+ String ownersAX = "owners:[ " + ownerJson("a@a") + ", " + ownerJson("x@x") + " ]";
+ String path2owners = "path2owners:{ ./:[ a@a, x@x ] }";
+ String owner2paths = "owner2paths:{ a@a:[ ./ ], x@x:[ ./ ] }";
+ String projectName = project.get();
+ String expectedInLog = "project:" + projectName + ", "
+ + "ownersFileName:OWNERS, "
+ + "getBranchId:refs/heads/master(FOUND), "
+ + "findOwnersFileFor:./t.c, "
+ + "findOwnersFileIn:., "
+ + "getFile:OWNERS:(...), "
+ + "parseLine:include:P1/P2:f1, "
+ + "getRepoFile:P1/P2:refs/heads/master:f1, "
+ + "getRepoFileException:repositorynotfound:P1/P2, " // repository not found
+ + "parseLine:include:(), " // missing file is treated as empty
+ + "parseLine:include:" + projectName + ":./d1/d2/../../f2, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:f2, "
+ + "getFile:f2(NOTFOUND), " // same repository but f2 is missing
+ + "parseLine:include:(), " // missing file is treated as empty
+ + "countNumOwners, "
+ + "findOwners, "
+ + "checkFile:./t.c, "
+ + "checkDir:., "
+ + "addOwnerWeightsIn:./ "
+ + "] ";
+ String c2Response = getOwnersDebugResponse(c2);
+ assertThat(c2Response).contains(path2owners);
+ assertThat(c2Response).contains(owner2paths);
+ assertThat(c2Response).contains("file2owners:{ ./t.c:[ a@a, x@x ] }");
+ assertThat(c2Response).contains(ownersAX);
+ assertThat(c2Response).contains(expectedInLog);
+ // A submitted change gets owners info from current repository.
+ String c1Response = getOwnersDebugResponse(c1);
+ assertThat(c1Response).contains(path2owners);
+ assertThat(c1Response).contains(owner2paths);
+ assertThat(c1Response).contains("file2owners:{ ./OWNERS:[ a@a, x@x ] }");
+ assertThat(c1Response).contains(ownersAX);
+ }
+
+ @Test
+ public void includeFoundTest() throws Exception {
+ // Compared with includeNotFoundTest, this one has file "f2" to include.
+ addFile("c0", "f2", "g1@g\ng2@g\n");
+ // c2 and c1 are both submitted before existence of OWNERS.
+ PushOneCommit.Result c2 = addFile("c2", "t.c", "##");
+ PushOneCommit.Result c1 = addFile("c1", "OWNERS",
+ "x@x\na@a\ninclude P1/P2 : f1\ninclude ./d1/d2/../../f2\n");
+ String ownerA = ownerJson("a@a");
+ String ownerX = ownerJson("x@x");
+ String ownerG1 = ownerJson("g1@g");
+ String ownerG2 = ownerJson("g2@g");
+ String ownersAG1G2X =
+ "owners:[ " + ownerA + ", " + ownerG1 + ", " + ownerG2 + ", " + ownerX + " ]";
+ String path2owners = "path2owners:{ ./:[ a@a, g1@g, g2@g, x@x ] }";
+ String owner2paths = "owner2paths:{ a@a:[ ./ ], g1@g:[ ./ ], g2@g:[ ./ ], x@x:[ ./ ] }";
+ String projectName = project.get();
+ String expectedInLog = "project:" + projectName + ", "
+ + "ownersFileName:OWNERS, "
+ + "getBranchId:refs/heads/master(FOUND), "
+ + "findOwnersFileFor:./t.c, "
+ + "findOwnersFileIn:., "
+ + "getFile:OWNERS:(...), "
+ + "parseLine:include:P1/P2:f1, "
+ + "getRepoFile:P1/P2:refs/heads/master:f1, "
+ + "getRepoFileException:repositorynotfound:P1/P2, "
+ + "parseLine:include:(), " // P1/P2 is still not found
+ + "parseLine:include:" + projectName + ":./d1/d2/../../f2, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:f2, "
+ + "getFile:f2:(...), " // f2 is included
+ + "countNumOwners, "
+ + "findOwners, "
+ + "checkFile:./t.c, "
+ + "checkDir:., "
+ + "addOwnerWeightsIn:./ "
+ + "] ";
+ String c2Response = getOwnersDebugResponse(c2);
+ assertThat(c2Response).contains(path2owners);
+ assertThat(c2Response).contains(owner2paths);
+ assertThat(c2Response).contains("file2owners:{ ./t.c:[ a@a, g1@g, g2@g, x@x ] }");
+ assertThat(c2Response).contains(ownersAG1G2X);
+ assertThat(c2Response).contains(expectedInLog);
+ // A submitted change gets owners info from current repository.
+ String c1Response = getOwnersDebugResponse(c1);
+ assertThat(c1Response).contains(path2owners);
+ assertThat(c1Response).contains(owner2paths);
+ assertThat(c1Response).contains("file2owners:{ ./OWNERS:[ a@a, g1@g, g2@g, x@x ] }");
+ assertThat(c1Response).contains(ownersAG1G2X);
+ }
+
+ @Test
+ public void includeIndirectFileTest() throws Exception {
+ // Test indirectly included file and relative file path.
+ addFile("1", "d1/f2", "d1f2@g\n");
+ addFile("2", "d2/f2", "d2f2@g\n");
+ addFile("3", "d3/f2", "d3f2@g\n");
+ addFile("4", "d1/d2/owners", "d1d2@g\ninclude ../f2\n");
+ addFile("5", "d2/d2/owners", "d2d2@g\ninclude ../f2\n");
+ addFile("6", "d3/d2/owners", "d3d2@g\ninclude ../f2\n");
+ addFile("7", "d3/OWNERS", "d3@g\ninclude ../d2/d2/owners\n");
+ addFile("8", "OWNERS", "x@g\n");
+ PushOneCommit.Result c1 = createChange("c1", "d3/t.c", "Hello!");
+ // d3's owners are in d3/OWNERS, d2/d2/owners, d2/f2, OWNERS,
+ // If the include directories are based on original directory d3,
+ // then the included files will be d2/d2/owners and d3/f2.
+ String ownerD3 = ownerJson("d3@g");
+ String ownerD2 = ownerJson("d2d2@g");
+ String ownerF2 = ownerJson("d2f2@g");
+ String ownerX = ownerJson("x@g", 0, 1, 0);
+ assertThat(getOwnersResponse(c1)).contains("owners:[ " + ownerD2 + ", "
+ + ownerF2 + ", " + ownerD3 + ", " + ownerX + " ], files:[ d3/t.c ]");
+ }
+
+ @Test
+ public void includeVsFileTest() throws Exception {
+ // Test difference between include and file statements.
+ // The file statement skips "set noparent" and "per-file" statements.
+ addFile("d1", "d1/OWNERS", "d1@g\n");
+ addFile("d1/d1", "d1/d1/OWNERS",
+ "per-file *.c=d1d1p@g\nd1d1@g\nfile: d1/OWNERS\n");
+ addFile("d1/d1/d1", "d1/d1/d1/OWNERS",
+ "set noparent\nper-file *.c=d1d1d1p@g\nd1d1d1@g\n");
+ addFile("d1/d2", "d1/d2/OWNERS",
+ "per-file *.c=d1d2p@g\nd1d2@g\ninclude d1/OWNERS\n");
+ addFile("d1/d2/d1", "d1/d2/d1/OWNERS",
+ "set noparent\nper-file *.c=d1d2d1p@g\nd1d2d1@g\n");
+
+ addFile("d2", "d2/OWNERS", "d2@g\n");
+ addFile("d2/d1", "d2/d1/OWNERS",
+ "per-file *.c=d2d1p@g\nd2d1@g\nfile: ./d1/OWNERS\n");
+ addFile("d2/d1/d1", "d2/d1/d1/OWNERS",
+ "set noparent\nper-file *.c=d2d1d1p@g\nd2d1d1@g\n");
+ addFile("d2/d2", "d2/d2/OWNERS",
+ "per-file *.c=d2d2p@g\nd2d2@g\ninclude ./d1/OWNERS\n");
+ addFile("d2/d2/d1", "d2/d2/d1/OWNERS",
+ "set noparent\nper-file *.c=d2d2d1p@g\nd2d2d1@g\n");
+
+ addFile("d3", "d3/OWNERS", "d3@g\n");
+ addFile("d3/d1/d1", "d3/d1/d1/OWNERS", "d3d1d1@g\nfile: ../../../d1/d1/OWNERS\n");
+ addFile("d3/d1/d2", "d3/d1/d2/OWNERS", "d3d1d2@g\nfile: //d1/d2/OWNERS\n");
+ addFile("d3/d2/d1", "d3/d2/d1/OWNERS", "d3d2d1@g\ninclude /d2/d1/OWNERS\n");
+ addFile("d3/d2/d2", "d3/d2/d2/OWNERS", "d3d2d2@g\ninclude //d2/d2/OWNERS\n");
+ PushOneCommit.Result c11 = createChange("c11", "d3/d1/d1/t.c", "test");
+ PushOneCommit.Result c12 = createChange("c12", "d3/d1/d2/t.c", "test");
+ PushOneCommit.Result c21 = createChange("c21", "d3/d2/d1/t.c", "test");
+ PushOneCommit.Result c22 = createChange("c22", "d3/d2/d2/t.c", "test");
+
+ // file and file
+ String owners11 = "file2owners:{ ./d3/d1/d1/t.c:"
+ + "[ d1d1@g, d1d1d1@g, d3@g, d3d1d1@g ] }";
+ // file and include
+ String owners12 = "file2owners:{ ./d3/d1/d2/t.c:"
+ + "[ d1d2@g, d1d2d1@g, d3@g, d3d1d2@g ] }";
+ // include and file
+ String owners21 = "file2owners:{ ./d3/d2/d1/t.c:"
+ + "[ d2d1@g, d2d1d1@g, d2d1p@g, d3@g, d3d2d1@g ] }";
+ // include and include
+ String owners22 = "file2owners:{ ./d3/d2/d2/t.c:"
+ + "[ d2d2@g, d2d2d1@g, d2d2d1p@g, d2d2p@g, d3d2d2@g ] }";
+
+ assertThat(getOwnersDebugResponse(c11)).contains(owners11);
+ assertThat(getOwnersDebugResponse(c12)).contains(owners12);
+ assertThat(getOwnersDebugResponse(c21)).contains(owners21);
+ assertThat(getOwnersDebugResponse(c22)).contains(owners22);
+ }
+
+ @Test
+ public void multipleIncludeTest() throws Exception {
+ // Now "include" and "file:" statements can share parsed results.
+ addFile("d1", "d1/OWNERS", "d1@g\n");
+ addFile("d2/d1", "d2/d1/OWNERS", "include /d1/OWNERS\nfile://d1/OWNERS\n");
+ addFile("d2/d2", "d2/d2/OWNERS", "file: //d1/OWNERS\ninclude /d1/OWNERS\n");
+ PushOneCommit.Result c1 = createChange("c1", "d2/d1/t.c", "test");
+ PushOneCommit.Result c2 = createChange("c2", "d2/d2/t.c", "test");
+ String projectName = project.get();
+ String log1 = "parseLine:useSaved:file:" + projectName + "://d1/OWNERS, ";
+ String log2 = "parseLine:useSaved:include:" + projectName + ":/d1/OWNERS, ";
+ String response1 = getOwnersDebugResponse(c1);
+ String response2 = getOwnersDebugResponse(c2);
+ assertThat(response1).contains(log1);
+ assertThat(response1).doesNotContain(log2);
+ assertThat(response2).doesNotContain(log1);
+ assertThat(response2).contains(log2);
+ }
+
+ @Test
+ public void includeCycleTest() throws Exception {
+ // f1 includes f2, f2 includes f3, f3 includes f4, f4 includes f2, OWNERS includes f1.
+ // All files are in the root directory, but could be referred with relative paths.
+ addFile("1", "f1", "f1@g\ninclude ./f2\n");
+ addFile("2", "f2", "f2@g\ninclude d1/../f3\n");
+ addFile("3", "f3", "f3@g\ninclude /f4\n");
+ addFile("4", "f4", "f4@g\ninclude d2/../f2\n");
+ addFile("5", "OWNERS", "x@g\ninclude ./d1/../f1\n");
+ PushOneCommit.Result c = createChange("6", "t.c", "#\n");
+ String response = getOwnersDebugResponse(c);
+ String projectName = project.get();
+ String expectedInLog = "project:" + projectName + ", "
+ + "ownersFileName:OWNERS, "
+ + "getBranchId:refs/heads/master(FOUND), "
+ + "findOwnersFileFor:./t.c, "
+ + "findOwnersFileIn:., "
+ + "getFile:OWNERS:(...), "
+ + "parseLine:include:" + projectName + ":./d1/../f1, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:f1, "
+ + "getFile:f1:(...), "
+ + "parseLine:include:" + projectName + ":./f2, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:f2, "
+ + "getFile:f2:(...), "
+ + "parseLine:include:" + projectName + ":d1/../f3, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:f3, "
+ + "getFile:f3:(...), "
+ + "parseLine:include:" + projectName + ":/f4, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:f4, "
+ + "getFile:f4:(...), "
+ + "parseLine:errorRecursion:include:" + projectName + ":d2/../f2, "
+ + "countNumOwners, "
+ + "findOwners, "
+ + "checkFile:./t.c, "
+ + "checkDir:., "
+ + "addOwnerWeightsIn:./ "
+ + "] ";
+ assertThat(response).contains("path2owners:{ ./:[ f1@g, f2@g, f3@g, f4@g, x@g ] }");
+ assertThat(response).contains(
+ "owner2paths:{ f1@g:[ ./ ], f2@g:[ ./ ], f3@g:[ ./ ], f4@g:[ ./ ], x@g:[ ./ ] }");
+ assertThat(response).contains(expectedInLog);
+ }
+
+ @Test
+ public void includeDuplicationTest() throws Exception {
+ // f0 is included into f1, f2, f3,
+ // f2 is included into f4 and f5; f4 is included into f5.
+ // f0, f1, f2, f3, f5 are included into d6/OWNERS.
+ addFile("0", "d0/f0", "f0@g\n");
+ addFile("1", "d1/d2/f1", "f1@g\ninclude ../../d0/f0\n");
+ addFile("2", "d2/f2", "f2@g\ninclude ../d0/f0\n");
+ addFile("3", "d2/d3/f3", "f3@g\ninclude /d0/f0\n");
+ addFile("4", "d4/f4", "f4@g\ninclude ../d2/f2\n");
+ addFile("5", "d4/d5/f5", "f5@g\ninclude /d2/f2\ninclude ../f4\n");
+ PushOneCommit.Result c = addFile("6", "d6/OWNERS",
+ "f6@g\ninclude /d0/f0\ninclude ../d1/d2/f1\n"
+ + "include ../d2/f2\ninclude /d2/d3/f3\ninclude /d2/../d4/d5/f5\ninclude /d4/f4\n");
+ String result = getOwnersDebugResponse(c);
+ assertThat(result).contains("{ ./d6/OWNERS:[ f0@g, f1@g, f2@g, f3@g, f4@g, f5@g, f6@g ] }");
+ String projectName = project.get();
+ String skipLog = "parseLine:useSaved:include:" + projectName + ":";
+ for (String path : new String[]{"../../d0/f0", "../d0/f0", "../d2/f2", "/d2/f2", "/d4/f4"}) {
+ assertThat(result).contains(skipLog + path);
+ }
+ String expectedInLog = "project:" + projectName + ", "
+ + "ownersFileName:OWNERS, "
+ + "getBranchId:refs/heads/master(FOUND), "
+ + "findOwnersFileFor:./d6/OWNERS, "
+ + "findOwnersFileIn:./d6, "
+ + "getFile:d6/OWNERS:(...), "
+ + "parseLine:include:" + projectName + ":/d0/f0, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:d0/f0, "
+ + "getFile:d0/f0:(...), "
+ + "parseLine:include:" + projectName + ":../d1/d2/f1, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:d1/d2/f1, "
+ + "getFile:d1/d2/f1:(...), "
+ + "parseLine:useSaved:include:" + projectName + ":../../d0/f0, "
+ + "parseLine:include:" + projectName + ":../d2/f2, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:d2/f2, "
+ + "getFile:d2/f2:(...), "
+ + "parseLine:useSaved:include:" + projectName + ":../d0/f0, "
+ + "parseLine:include:" + projectName + ":/d2/d3/f3, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:d2/d3/f3, "
+ + "getFile:d2/d3/f3:(...), "
+ + "parseLine:useSaved:include:" + projectName + ":/d0/f0, "
+ + "parseLine:include:" + projectName + ":/d2/../d4/d5/f5, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:d4/d5/f5, "
+ + "getFile:d4/d5/f5:(...), "
+ + "parseLine:useSaved:include:" + projectName + ":/d2/f2, "
+ + "parseLine:include:" + projectName + ":../f4, "
+ + "getRepoFile:" + projectName + ":refs/heads/master:d4/f4, "
+ + "getFile:d4/f4:(...), "
+ + "parseLine:useSaved:include:" + projectName + ":../d2/f2, "
+ + "parseLine:useSaved:include:" + projectName + ":/d4/f4, "
+ + "findOwnersFileIn:., "
+ + "getFile:OWNERS(NOTFOUND), "
+ + "countNumOwners, "
+ + "findOwners, "
+ + "checkFile:./d6/OWNERS, "
+ + "checkDir:./d6, "
+ + "checkDir:., "
+ + "addOwnerWeightsIn:./d6/ "
+ + "] ";
+ assertThat(result).contains(expectedInLog);
+ }
+
+ @Test
+ public void includeProjectOwnerTest() throws Exception {
+ // Test include directive with other project name.
+ Project.NameKey pA = newProject("PA");
+ Project.NameKey pB = newProject("PB");
+ String nameA = pA.get();
+ String nameB = pB.get();
+ switchProject(pA);
+ addFile("1", "f1", "pAf1@g\ninclude ./d1/f1\n");
+ addFile("2", "d1/f1", "pAd1f1@g\ninclude " + nameB + ":" + "/d2/f2\n");
+ addFile("3", "d2/OWNERS", "pAd2@g\n include " + nameA + " : " + "../f1\n");
+ addFile("4", "OWNERS", "pA@g\n");
+ switchProject(pB);
+ addFile("5", "f1", "pBf1@g\ninclude ./d1/f1\n");
+ addFile("6", "f2", "pBf2@g\n");
+ addFile("7", "d1/f1", "pBd1f1@g\n");
+ addFile("8", "d2/f2", "pBd2f2@g\ninclude ../f1\n");
+ switchProject(pA);
+ PushOneCommit.Result c1 = createChange("c1", "d2/t.c", "Hello!");
+ // included: pA:d2/OWNERS, pA:d2/../f1, pA:d1/f1, pB:d2/f2, pB:d2/../f1, pB:./d1/f1
+ // inherited: pA:OWNERS
+ String owners = "owners:[ " + ownerJson("pAd1f1@g") + ", " + ownerJson("pAd2@g") + ", "
+ + ownerJson("pAf1@g") + ", " + ownerJson("pBd1f1@g") + ", " + ownerJson("pBd2f2@g")
+ + ", " + ownerJson("pBf1@g") + ", " + ownerJson("pA@g", 0, 1, 0) + " ]";
+ assertThat(getOwnersResponse(c1)).contains(owners);
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/InheritanceIT.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/InheritanceIT.java
new file mode 100644
index 0000000..e3bc749
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/InheritanceIT.java
@@ -0,0 +1,112 @@
+// Copyright (C) 2019 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 com.google.common.flogger.FluentLogger;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestPlugin;
+import org.junit.Rule;
+import org.junit.Test;
+
+/** Test find-owners plugin features related to inheritance and set noparent statements. */
+@TestPlugin(name = "find-owners", sysModule = "com.googlesource.gerrit.plugins.findowners.Module")
+public class InheritanceIT extends FindOwners {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ @Rule public Watcher watcher = new Watcher(logger);
+
+ @Test
+ public void includePerFileNoParentTest() throws Exception {
+ // Test included file with per-file and set noparent, which affects the including file.
+ PushOneCommit.Result c1 = addFile("1", "d1/d1/OWNERS",
+ "d1d1@g\nper-file OW* = set noparent\nper-file OWNERS=d1d1o@g\n");
+ PushOneCommit.Result c2 = addFile("2", "d1/OWNERS",
+ "d1@g\nper-file OWNERS=d1o@g\nper-file * = set noparent\n");
+ PushOneCommit.Result c3 = addFile( "3", "d2/d1/OWNERS",
+ "per-file O*S=d2d1o@g\nd2d1@g\ninclude ../../d1/d1/OWNERS\n");
+ PushOneCommit.Result c4 = addFile("4",
+ "d2/OWNERS", "d2@g\nper-file OWNERS=d2o@g\nper-file *S=set noparent \n");
+ // Files that match per-file globs with set noparent do not inherit global default owners.
+ // But include directive can include more per-file owners as in c3.
+ assertThat(getOwnersResponse(c1)).contains("{ ./d1/d1/OWNERS:[ d1d1o@g ] }");
+ assertThat(getOwnersResponse(c2)).contains("{ ./d1/OWNERS:[ d1o@g ] }");
+ assertThat(getOwnersResponse(c3)).contains("{ ./d2/d1/OWNERS:[ d1d1o@g, d2d1o@g ] }");
+ assertThat(getOwnersResponse(c4)).contains("{ ./d2/OWNERS:[ d2o@g ] }");
+ }
+
+ @Test
+ public void includeNoParentTest() throws Exception {
+ // Test included file with noparent, which affects the inheritance of including file.
+ PushOneCommit.Result c1 = addFile("1", "d1/d1/OWNERS", "d1d1@g\nset noparent\n");
+ PushOneCommit.Result c2 = addFile("2", "d1/d2/OWNERS", "d1d2@g\n");
+ PushOneCommit.Result c3 = addFile("3", "d1/OWNERS", "d1@g\n");
+ PushOneCommit.Result c4 = addFile("4", "d2/d1/OWNERS", "d2d1@g\ninclude ../../d1/d1/OWNERS\n");
+ PushOneCommit.Result c5 = addFile("5", "d2/d2/OWNERS", "d2d2@g\ninclude ../../d1/d2/OWNERS");
+ PushOneCommit.Result c6 = addFile("6", "d2/OWNERS", "d2@g\n");
+ // d1/d1/OWNERS sets noparent, does not inherit d1/OWNERS
+ assertThat(getOwnersResponse(c1)).contains("{ ./d1/d1/OWNERS:[ d1d1@g ] }");
+ // d1/d2/OWNERS inherits d1/OWNERS
+ assertThat(getOwnersResponse(c2)).contains("{ ./d1/d2/OWNERS:[ d1@g, d1d2@g ] }");
+ assertThat(getOwnersResponse(c3)).contains("{ ./d1/OWNERS:[ d1@g ] }");
+ // d2/d1/OWNERS includes d1/d1/OWNERS, does not inherit d1/OWNERS or d2/OWNERS
+ assertThat(getOwnersResponse(c4)).contains("{ ./d2/d1/OWNERS:[ d1d1@g, d2d1@g ] }");
+ // d2/d2/OWNERS includes d1/d1/OWNERS, inherit d2/OWNERS but not d1/OWNERS
+ assertThat(getOwnersResponse(c5)).contains("{ ./d2/d2/OWNERS:[ d1d2@g, d2@g, d2d2@g ] }");
+ assertThat(getOwnersResponse(c6)).contains("{ ./d2/OWNERS:[ d2@g ] }");
+ }
+
+ @Test
+ public void subOwnersFileTest() throws Exception {
+ // Add OWNERS file in root and subdirectories.
+ addFile("1", "OWNERS", "x@x\n");
+ addFile("2", "d1/OWNERS", "a@a\n");
+ addFile("3", "d2/OWNERS", "y@y\n");
+ addFile("4", "d3/OWNERS", "b@b\nset noparent\n");
+ addFile("5", "d4/OWNERS", "z@z\ninclude ../d2/OWNERS");
+ // Add "t.c" file, which is not owned by subdirectory owners.
+ PushOneCommit.Result c2 = createChange("c2", "t.c", "Hello!");
+ String ownerA = ownerJson("a@a");
+ String ownerX = ownerJson("x@x");
+ assertThat(getOwnersResponse(c2)).contains("owners:[ " + ownerX + " ], files:[ t.c ]");
+ // Add "d1/t.c" file, which is owned by ./d1 and root owners.
+ PushOneCommit.Result c3 = createChange("c3", "d1/t.c", "Hello!");
+ String ownerX010 = ownerJson("x@x", 0, 1, 0);
+ assertThat(getOwnersResponse(c3))
+ .contains("owners:[ " + ownerA + ", " + ownerX010 + " ], files:[ d1/t.c ]");
+ // Add "d2/t.c" file, which is owned by ./d2 and root owners.
+ PushOneCommit.Result c4 = createChange("c4", "d2/t.c", "Hello!");
+ String ownerY = ownerJson("y@y");
+ assertThat(getOwnersResponse(c4))
+ .contains("owners:[ " + ownerY + ", " + ownerX010 + " ], files:[ d2/t.c ]");
+ // Add "d2/d1/t.c" file, which is owned by ./d2 and root owners.
+ PushOneCommit.Result c5 = createChange("c5", "d2/d1/t.c", "Hello!");
+ assertThat(getOwnersResponse(c5)).contains(
+ "owners:[ " + ownerY + ", " + ownerX010 + " ], files:[ d2/d1/t.c ]");
+ // Add "d3/t.c" file, which is owned only by ./d3 owners due to "set noparent".
+ PushOneCommit.Result c6 = createChange("c6", "d3/t.c", "Hello!");
+ String ownerB = ownerJson("b@b");
+ assertThat(getOwnersResponse(c6)).contains("owners:[ " + ownerB + " ], files:[ d3/t.c ]");
+ // Add "d3/d1/t.c" file, which is owned only by ./d3 owners due to "set noparent".
+ PushOneCommit.Result c7 = createChange("c7", "d3/d1/t.c", "Hello!");
+ assertThat(getOwnersResponse(c7)).contains(
+ "owners:[ " + ownerB + " ], files:[ d3/d1/t.c ]");
+ // Add "d4/t.c" file, which is owned by ./d4 and ./d2 owners, but not root owners.
+ PushOneCommit.Result c8 = createChange("c8", "d4/t.c", "Hello!");
+ String ownerZ = ownerJson("z@z");
+ assertThat(getOwnersResponse(c8)).contains(
+ "owners:[ " + ownerY + ", " + ownerZ + ", " + ownerX010 + " ], files:[ d4/t.c ]");
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/OwnerWeightsTest.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/OwnerWeightsTest.java
index 65abbe7..2031292 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/findowners/OwnerWeightsTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/OwnerWeightsTest.java
@@ -23,28 +23,14 @@
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TestWatcher;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.junit.runner.Description;
/** Test OwnerWeights class */
@RunWith(JUnit4.class)
public class OwnerWeightsTest {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- @Rule
- public TestWatcher watcher = new TestWatcher() {
- @Override
- public void starting(final Description method) {
- logger.atInfo().log("Test starting: " + method.getMethodName());
- }
-
- @Override
- public void finished(final Description method) {
- logger.atInfo().log("Test finished: " + method.getMethodName());
- }
- };
+ @Rule public Watcher watcher = new Watcher(logger);
private static OwnerWeights createOwnerWeights(int[] counts) {
OwnerWeights obj = new OwnerWeights();
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/OwnersValidatorTest.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/OwnersValidatorTest.java
index a9292b5..c294f30 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/findowners/OwnersValidatorTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/OwnersValidatorTest.java
@@ -51,28 +51,14 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TestWatcher;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.junit.runner.Description;
/** Test OwnersValidator, which checks syntax of changed OWNERS files. */
@RunWith(JUnit4.class)
public class OwnersValidatorTest {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- @Rule
- public TestWatcher watcher = new TestWatcher() {
- @Override
- public void starting(final Description method) {
- logger.atInfo().log("Test starting: " + method.getMethodName());
- }
-
- @Override
- public void finished(final Description method) {
- logger.atInfo().log("Test finished: " + method.getMethodName());
- }
- };
+ @Rule public Watcher watcher = new Watcher(logger);
private static class MockedEmails extends Emails {
Set<String> registered;
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/ParserTest.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/ParserTest.java
index c2de856..6998277 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/findowners/ParserTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/ParserTest.java
@@ -20,28 +20,14 @@
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TestWatcher;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.junit.runner.Description;
/** Test Parser class */
@RunWith(JUnit4.class)
public class ParserTest {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- @Rule
- public TestWatcher watcher = new TestWatcher() {
- @Override
- public void starting(final Description method) {
- logger.atInfo().log("Test starting: " + method.getMethodName());
- }
-
- @Override
- public void finished(final Description method) {
- logger.atInfo().log("Test finished: " + method.getMethodName());
- }
- };
+ @Rule public Watcher watcher = new Watcher(logger);
private static String mockedTestDir() {
return "./d1/d2/";
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/PerFileIT.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/PerFileIT.java
new file mode 100644
index 0000000..3b5e076
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/PerFileIT.java
@@ -0,0 +1,76 @@
+// Copyright (C) 2019 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 com.google.common.flogger.FluentLogger;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestPlugin;
+import org.junit.Rule;
+import org.junit.Test;
+
+/** Test find-owners plugin features related to per-file statements. */
+@TestPlugin(name = "find-owners", sysModule = "com.googlesource.gerrit.plugins.findowners.Module")
+public class PerFileIT extends FindOwners {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ @Rule public Watcher watcher = new Watcher(logger);
+
+ @Test
+ public void ownersPerFileTest() throws Exception {
+ addFile("1", "OWNERS", "per-file *.c=x@x\na@a\nc@c\nb@b\n");
+ // Add "t.c" file, which has per-file owner x@x, and a@a, b@b, c@c.
+ PushOneCommit.Result c2 = createChange("2", "t.c", "Hello!");
+ String ownerA = ownerJson("a@a");
+ String ownerB = ownerJson("b@b");
+ String ownerC = ownerJson("c@c");
+ String ownerABC = "owners:[ " +ownerA + ", " + ownerB + ", " + ownerC;
+ String ownerX = ownerJson("x@x");
+ assertThat(getOwnersResponse(c2)).contains(ownerABC + ", " + ownerX + " ], files:[ t.c ]");
+ // Add "t.txt" file, which has only global default owners.
+ PushOneCommit.Result c3 = createChange("3", "t.txt", "Test!");
+ assertThat(getOwnersResponse(c3)).contains(ownerABC + " ], files:[ t.txt ]");
+ }
+
+ @Test
+ public void perFileIncludeTest() throws Exception {
+ // A per-file with file: directive to include more owners.
+ addFile("1", "OWNERS", "per-file *.c=x@x\na@a\nper-file t.c=file: t_owner\n");
+ addFile("2", "t_owner", "t1@g\n*\nper-file *.c=y@y\ninclude more_owner\n");
+ addFile("3", "more_owner", "m@g\nm2@g\nper-file *.c=z@z\n");
+ PushOneCommit.Result c1 = createChange("c1", "x.c", "test");
+ PushOneCommit.Result c2 = createChange("c2", "t.c", "test");
+ String c1Response = getOwnersDebugResponse(c1);
+ String c2Response = getOwnersDebugResponse(c2);
+ assertThat(c1Response).contains("file2owners:{ ./x.c:[ a@a, x@x ] }");
+ assertThat(c2Response).contains("file2owners:{ ./t.c:[ *, a@a, m2@g, m@g, t1@g, x@x ] }");
+ }
+
+ @Test
+ public void includePerFileTest() throws Exception {
+ // Test included file with per-file, which affects the including file.
+ PushOneCommit.Result c1 = addFile("1", "d1/d1/OWNERS", "d1d1@g\nper-file OWNERS=d1d1o@g\n");
+ PushOneCommit.Result c2 = addFile("2", "d1/OWNERS", "d1@g\nper-file OWNERS=d1o@g\n");
+ PushOneCommit.Result c3 = addFile("3", "d2/d1/OWNERS", "d2d1@g\ninclude ../../d1/d1/OWNERS\n");
+ PushOneCommit.Result c4 = addFile("4", "d2/OWNERS", "d2@g\nper-file OWNERS=d2o@g");
+ // Files that match per-file globs now inherit global default owners.
+ assertThat(getOwnersResponse(c1)).contains(
+ "{ ./d1/d1/OWNERS:[ d1@g, d1d1@g, d1d1o@g, d1o@g ] }");
+ assertThat(getOwnersResponse(c2)).contains("{ ./d1/OWNERS:[ d1@g, d1o@g ] }");
+ assertThat(getOwnersResponse(c3)).contains(
+ "{ ./d2/d1/OWNERS:[ d1d1@g, d1d1o@g, d2@g, d2d1@g, d2o@g ] }");
+ assertThat(getOwnersResponse(c4)).contains("{ ./d2/OWNERS:[ d2@g, d2o@g ] }");
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/PrologIT.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/PrologIT.java
index 258e708..c169321 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/findowners/PrologIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/PrologIT.java
@@ -34,29 +34,12 @@
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
/** Test existence of compiled find-owners plugin Prolog predicates. */
@TestPlugin(name = "find-owners", sysModule = "com.googlesource.gerrit.plugins.findowners.Module")
public class PrologIT extends LightweightPluginDaemonTest {
- // This class now only tests existence of compiled Prolog predicates.
- // Later we can test the functionality of the predicates.
-
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- @Rule
- public TestWatcher watcher = new TestWatcher() {
- @Override
- public void starting(final Description method) {
- logger.atInfo().log("Test starting: " + method.getMethodName());
- }
-
- @Override
- public void finished(final Description method) {
- logger.atInfo().log("Test finished: " + method.getMethodName());
- }
- };
+ @Rule public Watcher watcher = new Watcher(logger);
@Test
public void predefinedPredicateTest() throws Exception {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/UtilTest.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/UtilTest.java
index 6411e3a..1f846fe 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/findowners/UtilTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/UtilTest.java
@@ -24,28 +24,14 @@
import java.util.Set;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TestWatcher;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.junit.runner.Description;
/** Test Util class */
@RunWith(JUnit4.class)
public class UtilTest {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- @Rule
- public TestWatcher watcher = new TestWatcher() {
- @Override
- public void starting(final Description method) {
- logger.atInfo().log("Test starting: " + method.getMethodName());
- }
-
- @Override
- public void finished(final Description method) {
- logger.atInfo().log("Test finished: " + method.getMethodName());
- }
- };
+ @Rule public Watcher watcher = new Watcher(logger);
@Test
public void getOwner2WeightsTest() {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/findowners/Watcher.java b/src/test/java/com/googlesource/gerrit/plugins/findowners/Watcher.java
new file mode 100644
index 0000000..ae743a7
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/findowners/Watcher.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2019 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 com.google.common.flogger.FluentLogger;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+/** Watcher for JUnit tests. */
+class Watcher extends TestWatcher {
+ private final FluentLogger logger;
+
+ Watcher(FluentLogger logger) { this.logger = logger; }
+
+ @Override
+ public void starting(final Description method) {
+ logger.atInfo().log("Test starting: " + method);
+ }
+
+ @Override
+ public void finished(final Description method) {
+ logger.atInfo().log("Test finished: " + method);
+ }
+}