CheckCodeOwner: Return whether parent/folder owners are ignored

To make debugging code ownerships easier return for the inspected code
owner files whether parent / folder code owners are ignored.

Note, internally we call folder code owners also "global code owners"
(per-file code owners vs. global code owners that are defined in a code
owner config file), but there is also the possibility to configure code
owners globally in the code-owners.config file (code owners that apply
globally for all repositories and branches). Hence to avoid confusion
between globally configured code owners and global code owners in a code
owner config file, we call the first "global code owners" and the latter
"folder code owners" in the API and in the documentation.

Change-Id: I833e9c2b266a8d13e1acd15a2b13d72065e7f1e7
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/java/com/google/gerrit/plugins/codeowners/api/CheckedCodeOwnerConfigFileInfo.java b/java/com/google/gerrit/plugins/codeowners/api/CheckedCodeOwnerConfigFileInfo.java
index c5f8fa2..e651865 100644
--- a/java/com/google/gerrit/plugins/codeowners/api/CheckedCodeOwnerConfigFileInfo.java
+++ b/java/com/google/gerrit/plugins/codeowners/api/CheckedCodeOwnerConfigFileInfo.java
@@ -32,4 +32,13 @@
    * not a code owner.
    */
   public boolean assignsCodeOwnershipToUser;
+
+  /** Whether code owners from parent directory are ignored. */
+  public boolean areParentCodeOwnersIgnored;
+
+  /**
+   * Whether folder code owners are ignored (i.e. whether there is a matching per-file rule that
+   * ignores folder code owners, e.g. by using "set noparent").
+   */
+  public boolean areFolderCodeOwnersIgnored;
 }
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResult.java b/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResult.java
index a124e3e..6967763 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResult.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResult.java
@@ -41,7 +41,7 @@
   public abstract boolean ignoreParentCodeOwners();
 
   /** Gets whether global code owners (aka folder code owners) should be ignored for the path. */
-  abstract boolean ignoreGlobalCodeOwners();
+  public abstract boolean ignoreGlobalCodeOwners();
 
   /** Gets code owner sets that contain global code owners (aka folder code owners). */
   abstract ImmutableSet<CodeOwnerSet> globalCodeOwnerSets();
diff --git a/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwner.java b/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwner.java
index eedbff4..b276c00 100644
--- a/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwner.java
+++ b/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwner.java
@@ -270,7 +270,10 @@
                   pathCodeOwnersResult.unresolvedImports());
           checkedCodeOwnerConfigFileInfosBuilder.add(
               CheckedCodeOwnerConfigFileJson.format(
-                  codeOwnerConfigFileInfo, assignsCodeOwnershipToUser));
+                  codeOwnerConfigFileInfo,
+                  assignsCodeOwnershipToUser,
+                  pathCodeOwnersResult.ignoreParentCodeOwners(),
+                  pathCodeOwnersResult.ignoreGlobalCodeOwners()));
 
           return !pathCodeOwnersResult.ignoreParentCodeOwners();
         });
diff --git a/java/com/google/gerrit/plugins/codeowners/restapi/CheckedCodeOwnerConfigFileJson.java b/java/com/google/gerrit/plugins/codeowners/restapi/CheckedCodeOwnerConfigFileJson.java
index fbc326a..8c5d516 100644
--- a/java/com/google/gerrit/plugins/codeowners/restapi/CheckedCodeOwnerConfigFileJson.java
+++ b/java/com/google/gerrit/plugins/codeowners/restapi/CheckedCodeOwnerConfigFileJson.java
@@ -20,10 +20,15 @@
 /** Collection of routines to populate {@link CheckedCodeOwnerConfigFileInfo}. */
 public class CheckedCodeOwnerConfigFileJson {
   public static CheckedCodeOwnerConfigFileInfo format(
-      CodeOwnerConfigFileInfo codeOwnerConfigFileInfo, boolean assignsCodeOwnershipToUser) {
+      CodeOwnerConfigFileInfo codeOwnerConfigFileInfo,
+      boolean assignsCodeOwnershipToUser,
+      boolean areParentCodeOwnersIgnored,
+      boolean areFolderCodeOwnersIgnored) {
     CheckedCodeOwnerConfigFileInfo info = new CheckedCodeOwnerConfigFileInfo();
     info.codeOwnerConfigFileInfo = codeOwnerConfigFileInfo;
     info.assignsCodeOwnershipToUser = assignsCodeOwnershipToUser;
+    info.areParentCodeOwnersIgnored = areParentCodeOwnersIgnored;
+    info.areFolderCodeOwnersIgnored = areFolderCodeOwnersIgnored;
     return info;
   }
 }
diff --git a/java/com/google/gerrit/plugins/codeowners/testing/CheckedCodeOwnerConfigFileInfoSubject.java b/java/com/google/gerrit/plugins/codeowners/testing/CheckedCodeOwnerConfigFileInfoSubject.java
index effef0c..1821534 100644
--- a/java/com/google/gerrit/plugins/codeowners/testing/CheckedCodeOwnerConfigFileInfoSubject.java
+++ b/java/com/google/gerrit/plugins/codeowners/testing/CheckedCodeOwnerConfigFileInfoSubject.java
@@ -69,6 +69,36 @@
     return this;
   }
 
+  public BooleanSubject hasAreParentCodeOwnersIgnoredThat() {
+    return check("areParentCodeOwnersIgnored()")
+        .that(checkedCodeOwnerConfigFileInfo().areParentCodeOwnersIgnored);
+  }
+
+  public CheckedCodeOwnerConfigFileInfoSubject parentCodeOwnersAreIgnored() {
+    hasAreParentCodeOwnersIgnoredThat().isTrue();
+    return this;
+  }
+
+  public CheckedCodeOwnerConfigFileInfoSubject parentCodeOwnersAreNotIgnored() {
+    hasAreParentCodeOwnersIgnoredThat().isFalse();
+    return this;
+  }
+
+  public BooleanSubject hasAreFolderCodeOwnersIgnoredThat() {
+    return check("areFolderCodeOwnersIgnored()")
+        .that(checkedCodeOwnerConfigFileInfo().areFolderCodeOwnersIgnored);
+  }
+
+  public CheckedCodeOwnerConfigFileInfoSubject folderCodeOwnersAreIgnored() {
+    hasAreFolderCodeOwnersIgnoredThat().isTrue();
+    return this;
+  }
+
+  public CheckedCodeOwnerConfigFileInfoSubject folderCodeOwnersAreNotIgnored() {
+    hasAreFolderCodeOwnersIgnoredThat().isFalse();
+    return this;
+  }
+
   private CheckedCodeOwnerConfigFileInfo checkedCodeOwnerConfigFileInfo() {
     isNotNull();
     return checkedCodeOwnerConfigFileInfo;
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java
index 473c3fa..05a29bf 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java
@@ -174,6 +174,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -217,6 +219,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .element(0)
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, fooBarCodeOwnerConfigKey)
         .assertNoWebLinks()
@@ -227,6 +231,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .element(1)
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, fooCodeOwnerConfigKey)
         .assertNoWebLinks()
@@ -237,6 +243,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .element(2)
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, rootCodeOwnerConfigKey)
         .assertNoWebLinks()
@@ -290,6 +298,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .element(0)
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, fooBarCodeOwnerConfigKey)
         .assertNoWebLinks()
@@ -300,6 +310,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .element(1)
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, fooCodeOwnerConfigKey)
         .assertNoWebLinks()
@@ -343,6 +355,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -376,6 +390,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -411,6 +427,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -460,6 +478,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -496,6 +516,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -535,6 +557,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -573,6 +597,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -612,6 +638,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -653,6 +681,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -702,6 +732,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -739,6 +771,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -777,6 +811,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -865,6 +901,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -911,6 +949,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -951,6 +991,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -1256,6 +1298,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -1340,6 +1384,8 @@
         .hasCheckedCodeOwnerConfigsThat()
         .onlyElement()
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreIgnored()
+        .folderCodeOwnersAreIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, codeOwnerConfigKey)
         .assertNoWebLinks()
@@ -1405,6 +1451,8 @@
         assertThat(checkCodeOwnerInfo).hasCheckedCodeOwnerConfigsThat().onlyElement();
     codeOwnerConfigFileInfoSubject
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, fooCodeOwnerConfigKey)
         .assertNoWebLinks()
@@ -1490,6 +1538,8 @@
         assertThat(checkCodeOwnerInfo).hasCheckedCodeOwnerConfigsThat().onlyElement();
     codeOwnerConfigFileInfoSubject
         .assignsCodeOwnershipToUser()
+        .parentCodeOwnersAreNotIgnored()
+        .folderCodeOwnersAreNotIgnored()
         .hasCodeOwnerConfigFileThat()
         .assertKey(backend, fooCodeOwnerConfigKey)
         .assertNoWebLinks()
diff --git a/resources/Documentation/rest-api.md b/resources/Documentation/rest-api.md
index cbb4d21..015cb9e 100644
--- a/resources/Documentation/rest-api.md
+++ b/resources/Documentation/rest-api.md
@@ -958,6 +958,8 @@
 | ----------- | ----------- |
 | `code_owner_config` | The code owner config file as a [CodeOwnerConfigFileInfo](#code-owner-config-file-info) entity.
 | `assigns_code_ownership_to_user` | Whether this code owner config file assigns code ownership to the specified email and path. Note that if code ownership is assigned to the email via a code owner config file, but the email is not resolvable (see the `is_resolvable` field in [CodeOwnerCheckInfo](#code-owner-check-info)), the user is not a code owner.
+| `are_parent_code_owners_ignored` | Whether code owners from parent directory are ignored.
+| `are_folder_code_owners_ignored` | Whether folder code owners are ignored (i.e. whether there is a matching per-file rule that ignores folder code owners, e.g. by using "set noparent").
 
 ---