Fix transitive per-file imports
Transitive per-file imports were imported regardless of whether the path
expressions of the per-file rules matched.
Signed-off-by: Edwin Kempin <ekempin@google.com>
Change-Id: Idd0982e5c95408e34e3c17754a6df1cf3b912278
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwners.java b/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwners.java
index 4f67254..036c298 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwners.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwners.java
@@ -21,6 +21,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Project;
@@ -344,10 +345,11 @@
.forEach(resolvedCodeOwnerConfigBuilder::addCodeOwnerSet);
}
+ ImmutableSet<CodeOwnerSet> matchingPerFileCodeOwnerSets =
+ getMatchingPerFileCodeOwnerSets(importedCodeOwnerConfig).collect(toImmutableSet());
if (importMode.importPerFileCodeOwnerSets()) {
logger.atFine().log("import per-file code owners");
- getMatchingPerFileCodeOwnerSets(importedCodeOwnerConfig)
- .forEach(resolvedCodeOwnerConfigBuilder::addCodeOwnerSet);
+ matchingPerFileCodeOwnerSets.forEach(resolvedCodeOwnerConfigBuilder::addCodeOwnerSet);
}
if (importMode.resolveImportsOfImport()
@@ -356,7 +358,7 @@
Set<CodeOwnerConfigReference> transitiveImports = new HashSet<>();
transitiveImports.addAll(importedCodeOwnerConfig.imports());
transitiveImports.addAll(
- importedCodeOwnerConfig.codeOwnerSets().stream()
+ matchingPerFileCodeOwnerSets.stream()
.flatMap(codeOwnerSet -> codeOwnerSet.imports().stream())
.collect(toImmutableSet()));
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersTest.java
index 066fcbb..341b3a3 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersTest.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersTest.java
@@ -1661,6 +1661,96 @@
}
@Test
+ public void onlyMatchingTransitivePerFileImportsAreImported() throws Exception {
+ TestAccount user2 = accountCreator.user2();
+
+ // create importing config with global import
+ CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/")
+ .addImport(
+ CodeOwnerConfigReference.create(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/bar/OWNERS"))
+ .create();
+
+ // create imported config with 2 per file imports, one for *.md files and one for *.txt
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression("*.md")
+ .addImport(
+ CodeOwnerConfigReference.create(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/md/OWNERS"))
+ .build())
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression("*.txt")
+ .addImport(
+ CodeOwnerConfigReference.create(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/txt/OWNERS"))
+ .build())
+ .create();
+
+ // create config with global code owner that is imported by the imported config for *.md files
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/md/")
+ .addCodeOwnerEmail(user.email())
+ .create();
+
+ // create config with global code owner that is imported by the imported config for *.txt files
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/txt/")
+ .addCodeOwnerEmail(user2.email())
+ .create();
+
+ // Expectation for foo.xyz file: code owners is empty since foo.xyz neither matches *.md nor
+ // *.txt
+ Optional<PathCodeOwners> pathCodeOwners =
+ pathCodeOwnersFactory.create(
+ rootCodeOwnerConfigKey,
+ projectOperations.project(project).getHead("master"),
+ Paths.get("/foo.xyz"));
+ assertThat(pathCodeOwners).isPresent();
+ assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().getPathCodeOwners()).isEmpty();
+
+ // Expectation for foo.md file: code owners contains only user since foo.md only matches *.md
+ pathCodeOwners =
+ pathCodeOwnersFactory.create(
+ rootCodeOwnerConfigKey,
+ projectOperations.project(project).getHead("master"),
+ Paths.get("/foo.md"));
+ assertThat(pathCodeOwners).isPresent();
+ assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().getPathCodeOwners())
+ .comparingElementsUsing(hasEmail())
+ .containsExactly(user.email());
+
+ // Expectation for foo.txt file: code owners contains only user2 since foo.txt only matches
+ // *.txt
+ pathCodeOwners =
+ pathCodeOwnersFactory.create(
+ rootCodeOwnerConfigKey,
+ projectOperations.project(project).getHead("master"),
+ Paths.get("/foo.txt"));
+ assertThat(pathCodeOwners).isPresent();
+ assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().getPathCodeOwners())
+ .comparingElementsUsing(hasEmail())
+ .containsExactly(user2.email());
+ }
+
+ @Test
public void cannotMatchAgainstNullCodeOwnerSet() throws Exception {
NullPointerException npe =
assertThrows(