Backend: Support cc

Support for cc in:
* Config
* REST
* Adding ccs on patchset upload

Maintain backwards compatibility by defaulting the newly introduced
ConfigReviewersInput#type to "REVIEWERS".

TODO:
Frontend support for cc.

Feature: Issue 11676
Change-Id: I2c2aba662f1a45ed4f2540b7eb32ee56cc8faaba
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewers.java
index 30568dc..90f5841 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/AddReviewers.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.util.ManualRequestContext;
@@ -36,9 +37,13 @@
   private final OneOffRequestContext requestContext;
   private final ChangeInfo changeInfo;
   private final Set<Account.Id> reviewers;
+  private final Set<Account.Id> ccs;
 
   interface Factory {
-    AddReviewers create(ChangeInfo changeInfo, Set<Account.Id> reviewers);
+    AddReviewers create(
+        ChangeInfo changeInfo,
+        @Assisted("reviewers") Set<Account.Id> reviewers,
+        @Assisted("ccs") Set<Account.Id> ccs);
   }
 
   @Inject
@@ -46,11 +51,13 @@
       GerritApi gApi,
       OneOffRequestContext requestContext,
       @Assisted ChangeInfo changeInfo,
-      @Assisted Set<Account.Id> reviewers) {
+      @Assisted("reviewers") Set<Account.Id> reviewers,
+      @Assisted("ccs") Set<Account.Id> ccs) {
     this.gApi = gApi;
     this.requestContext = requestContext;
     this.changeInfo = changeInfo;
     this.reviewers = reviewers;
+    this.ccs = ccs;
   }
 
   @Override
@@ -66,12 +73,18 @@
       // TODO(davido): Switch back to using changes API again,
       // when it supports batch mode for adding reviewers
       ReviewInput in = new ReviewInput();
-      in.reviewers = new ArrayList<>(reviewers.size());
+      in.reviewers = new ArrayList<>(reviewers.size() + ccs.size());
       for (Account.Id account : reviewers) {
         AddReviewerInput addReviewerInput = new AddReviewerInput();
         addReviewerInput.reviewer = account.toString();
         in.reviewers.add(addReviewerInput);
       }
+      for (Account.Id account : ccs) {
+        AddReviewerInput input = new AddReviewerInput();
+        input.state = ReviewerState.CC;
+        input.reviewer = account.toString();
+        in.reviewers.add(input);
+      }
       gApi.changes().id(changeInfo._number).current().review(in);
     } catch (RestApiException e) {
       logger.atSevere().withCause(e).log("Couldn't add reviewers to the change");
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java
index 9efc8bd..d815633 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/PutReviewers.java
@@ -54,6 +54,7 @@
 
   protected static class Input {
     public Action action;
+    public ReviewerType type;
     public String filter;
     public String reviewer;
   }
@@ -105,23 +106,29 @@
       if (input.action == Action.ADD) {
         validateReviewer(input.reviewer);
       }
+      if (input.type == null) {
+        input.type = ReviewerType.REVIEWER;
+      }
       try {
         StringBuilder message = new StringBuilder(pluginName).append(" plugin: ");
         forProject.load(md);
         if (input.action == Action.ADD) {
           message
-              .append("Add reviewer ")
+              .append("Add ")
+              .append(input.type.name)
+              .append(" ")
               .append(input.reviewer)
               .append(" to filter ")
               .append(input.filter);
-          forProject.addReviewer(input.filter, input.reviewer);
+          forProject.addReviewer(input.filter, input.reviewer, input.type);
         } else {
           message
-              .append("Remove reviewer ")
-              .append(input.reviewer)
+              .append("Remove ")
+              .append(input.type.name)
+              .append(" ")
               .append(" from filter ")
               .append(input.filter);
-          forProject.removeReviewer(input.filter, input.reviewer);
+          forProject.removeReviewer(input.filter, input.reviewer, input.type);
         }
         message.append("\n");
         md.setMessage(message.toString());
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilter.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilter.java
index 99a0c59..b991760 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerFilter.java
@@ -29,6 +29,7 @@
 public abstract class ReviewerFilter {
   protected String filter;
   protected Set<String> reviewers;
+  protected Set<String> ccs;
 
   String getFilter() {
     return filter;
@@ -38,17 +39,23 @@
     return reviewers;
   }
 
+  Set<String> getCcs() {
+    return ccs;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (o instanceof ReviewerFilter) {
       ReviewerFilter other = ((ReviewerFilter) o);
-      return Objects.equals(filter, other.filter) && Objects.equals(reviewers, other.reviewers);
+      return Objects.equals(filter, other.filter)
+          && Objects.equals(reviewers, other.reviewers)
+          && Objects.equals(ccs, other.ccs);
     }
     return false;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(filter, reviewers);
+    return Objects.hash(filter, reviewers, ccs);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerType.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerType.java
new file mode 100644
index 0000000..2d8f943
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ReviewerType.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 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.reviewers;
+
+public enum ReviewerType {
+  REVIEWER("reviewer"),
+  CC("cc");
+
+  String name;
+
+  ReviewerType(String name) {
+    this.name = name;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Reviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Reviewers.java
index 75a976d..42548a7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Reviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Reviewers.java
@@ -20,6 +20,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
@@ -152,13 +153,18 @@
     AccountInfo uploader = event.getWho();
     int changeNumber = c._number;
     try {
-      Set<String> reviewers = findReviewers(changeNumber, filters);
-      if (reviewers.isEmpty()) {
+      List<ReviewerFilter> matching = findMatchingFilters(changeNumber, filters);
+      Set<String> reviewers = getReviewersFrom(matching);
+      Set<String> ccs = getCcsFrom(matching);
+      ccs.removeAll(reviewers);
+      if (reviewers.isEmpty() && ccs.isEmpty()) {
         return;
       }
       final AddReviewers addReviewers =
           addReviewersFactory.create(
-              c, resolver.resolve(reviewers, projectName, changeNumber, uploader));
+              c,
+              resolver.resolve(reviewers, projectName, changeNumber, uploader),
+              resolver.resolve(ccs, projectName, changeNumber, uploader));
       workQueue.submit(addReviewers);
     } catch (QueryParseException e) {
       logger.atWarning().log(
@@ -171,15 +177,26 @@
 
   private Set<String> findReviewers(int change, List<ReviewerFilter> filters)
       throws StorageException, QueryParseException {
+    return getReviewersFrom(findMatchingFilters(change, filters));
+  }
+
+  private Set<String> getReviewersFrom(List<ReviewerFilter> filters) throws StorageException {
     ImmutableSet.Builder<String> reviewers = ImmutableSet.builder();
-    List<ReviewerFilter> found = findReviewerFilters(change, filters);
-    for (ReviewerFilter s : found) {
-      reviewers.addAll(s.getReviewers());
+    for (ReviewerFilter f : filters) {
+      reviewers.addAll(f.getReviewers());
     }
     return reviewers.build();
   }
 
-  private List<ReviewerFilter> findReviewerFilters(int change, List<ReviewerFilter> filters)
+  private Set<String> getCcsFrom(List<ReviewerFilter> filters) throws StorageException {
+    Set<String> ccs = Sets.newHashSet();
+    for (ReviewerFilter f : filters) {
+      ccs.addAll(f.getCcs());
+    }
+    return ccs;
+  }
+
+  private List<ReviewerFilter> findMatchingFilters(int change, List<ReviewerFilter> filters)
       throws StorageException, QueryParseException {
     ImmutableList.Builder<ReviewerFilter> found = ImmutableList.builder();
     for (ReviewerFilter s : filters) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewerFilterCollection.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewerFilterCollection.java
index dab0ca0..cd41373 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewerFilterCollection.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewerFilterCollection.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.reviewers.config;
 
+import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.KEY_CC;
 import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.KEY_REVIEWER;
 import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.SECTION_FILTER;
 
@@ -50,6 +51,7 @@
     private ReviewerFilterSection(String filter) {
       this.filter = filter;
       this.reviewers = Sets.newHashSet(cfg.getStringList(SECTION_FILTER, filter, KEY_REVIEWER));
+      this.ccs = Sets.newHashSet(cfg.getStringList(SECTION_FILTER, filter, KEY_CC));
     }
 
     public void removeReviewer(String reviewer) {
@@ -62,11 +64,22 @@
       save();
     }
 
+    public void removeCc(String cc) {
+      ccs.remove(cc);
+      save();
+    }
+
+    public void addCc(String cc) {
+      ccs.add(cc);
+      save();
+    }
+
     private void save() {
-      if (this.reviewers.isEmpty()) {
+      if (this.reviewers.isEmpty() && this.ccs.isEmpty()) {
         cfg.unsetSection(SECTION_FILTER, filter);
       } else {
         cfg.setStringList(SECTION_FILTER, filter, KEY_REVIEWER, Lists.newArrayList(this.reviewers));
+        cfg.setStringList(SECTION_FILTER, filter, KEY_CC, Lists.newArrayList(this.ccs));
       }
     }
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfig.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfig.java
index e440dcd..c81cbd5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfig.java
@@ -26,6 +26,7 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.reviewers.ReviewerFilter;
+import com.googlesource.gerrit.plugins.reviewers.ReviewerType;
 import java.io.IOException;
 import java.util.List;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -39,6 +40,7 @@
 
   @VisibleForTesting public static final String FILENAME = "reviewers.config";
   @VisibleForTesting public static final String SECTION_FILTER = "filter";
+  @VisibleForTesting public static final String KEY_CC = "cc";
   @VisibleForTesting public static final String KEY_REVIEWER = "reviewer";
   private static final String KEY_ENABLE_REST = "enableREST";
   private static final String KEY_SUGGEST_ONLY = "suggestOnly";
@@ -88,12 +90,24 @@
     private Config cfg;
     private ReviewerFilterCollection filters;
 
-    public void addReviewer(String filter, String reviewer) {
-      filters.get(filter).addReviewer(reviewer);
+    public void addReviewer(String filter, String reviewer, ReviewerType type) {
+      switch (type) {
+        case REVIEWER:
+          filters.get(filter).addReviewer(reviewer);
+          break;
+        case CC:
+          filters.get(filter).addCc(reviewer);
+      }
     }
 
-    public void removeReviewer(String filter, String reviewer) {
-      filters.get(filter).removeReviewer(reviewer);
+    public void removeReviewer(String filter, String reviewer, ReviewerType type) {
+      switch (type) {
+        case REVIEWER:
+          filters.get(filter).removeReviewer(reviewer);
+          break;
+        case CC:
+          filters.get(filter).removeCc(reviewer);
+      }
     }
 
     @Override
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 1088535..1b51181 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -38,6 +38,7 @@
 ```
   [filter "*"]
     reviewer = john.doe@example.com
+    cc = DevGroup
 
   [filter "branch:main file:^lib/.*"]
     reviewer = jane.doe@example.com
@@ -52,9 +53,16 @@
 	account's email address or username, or the group name.  Multiple `reviewer`
 	occurrences are allowed.
 
+filter.\<filter\>.cc
+:	An account or a group name. Must be an exact match (case sensitive) with the
+	account's email address or username, or the group name.  Multiple `cc`
+	occurrences are allowed.
+
 ##Multiple filter matches
 
 The plugin supports multiple filter matches.
+If a reviewer, according to filter matches, should be added as both `reviewer` and `cc`,
+`reviewer` takes precedence.
 
 ###Example
 
diff --git a/src/main/resources/Documentation/rest-api.md b/src/main/resources/Documentation/rest-api.md
index 8d13cad..957318f 100644
--- a/src/main/resources/Documentation/rest-api.md
+++ b/src/main/resources/Documentation/rest-api.md
@@ -36,6 +36,9 @@
       "reviewers": [
         "UserA",
         "UserB"
+      ],
+      "cc": [
+        "DevGroup"
       ]
     },
     {
@@ -66,8 +69,9 @@
   Content-Type: application/json;charset=UTF-8
   {
     "action": "ADD",
-    "filter": "branch:master"
-    "reviewer": "UserA"
+    "type": "REVIEWER"
+    "filter": "branch:master",
+    "reviewer": "UserA",
   }
 ```
 
@@ -88,6 +92,9 @@
         "UserA",
         "UserB"
       ]
+      "ccs": [
+        "DevGroup"
+      ]
     },
     {
       "filter": "file:^lib/*",
@@ -111,6 +118,8 @@
 * _filter_: A filter that is used to assign default reviewers.
 * _reviewers_: List of usernames which are assigned as default reviewers
  under the filter.
+* _ccs_: List of usernames which are assigned as default ccs
+ under the filter.
 
 ### <a id="config-reviewers-input"></a>ConfigReviewersInput
 
@@ -118,6 +127,7 @@
 reviewers.
 
 * _action_: Indicates whether to add or remove the input reviewer
+* _type_: Which type to add/remove the user as, (REVIEWER|CC), defaults to REVIEWER.
 * _filter_: The filter associated with the input reviewer.
 * _reviewer_: The user to add or remove from the default reviewers.
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/reviewers/AbstractReviewersPluginTest.java b/src/test/java/com/googlesource/gerrit/plugins/reviewers/AbstractReviewersPluginTest.java
index 731602d..1407b48 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/reviewers/AbstractReviewersPluginTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/reviewers/AbstractReviewersPluginTest.java
@@ -16,6 +16,7 @@
 
 import static com.google.gerrit.acceptance.GitUtil.fetch;
 import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.FILENAME;
+import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.KEY_CC;
 import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.KEY_REVIEWER;
 import static com.googlesource.gerrit.plugins.reviewers.config.ReviewersConfig.SECTION_FILTER;
 
@@ -47,9 +48,11 @@
     Config cfg = new Config();
     Arrays.stream(filters)
         .forEach(
-            f ->
-                cfg.setStringList(
-                    SECTION_FILTER, f.filter, KEY_REVIEWER, Lists.newArrayList(f.reviewers)));
+            f -> {
+              cfg.setStringList(
+                  SECTION_FILTER, f.filter, KEY_REVIEWER, Lists.newArrayList(f.reviewers));
+              cfg.setStringList(SECTION_FILTER, f.filter, KEY_CC, Lists.newArrayList(f.ccs));
+            });
     pushFactory
         .create(admin.newIdent(), repo, "Add reviewers", FILENAME, cfg.toText())
         .to(RefNames.REFS_CONFIG)
@@ -63,8 +66,8 @@
     return repo;
   }
 
-  protected TestFilter filter(String filter, Set<String> reviewers) {
-    return new TestFilter(filter, reviewers);
+  private TestFilter filter(String filter, Set<String> reviewers, Set<String> ccs) {
+    return new TestFilter(filter, reviewers, ccs);
   }
 
   protected TestFilter filter(String filter) {
@@ -73,13 +76,14 @@
 
   protected static class TestFilter extends ReviewerFilter {
 
-    public TestFilter(String filter, Set<String> reviewers) {
+    public TestFilter(String filter, Set<String> reviewers, Set<String> ccs) {
       this.filter = filter;
       this.reviewers = reviewers;
+      this.ccs = ccs;
     }
 
     public TestFilter(String filter) {
-      this(filter, Sets.newHashSet());
+      this(filter, Sets.newHashSet(), Sets.newHashSet());
     }
 
     public TestFilter reviewer(String reviewerId) {
@@ -90,5 +94,14 @@
     public TestFilter reviewer(TestAccount reviewer) {
       return reviewer(reviewer.email());
     }
+
+    public TestFilter cc(String ccId) {
+      ccs.add(ccId);
+      return this;
+    }
+
+    public TestFilter cc(TestAccount cc) {
+      return cc(cc.email());
+    }
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersIT.java b/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersIT.java
index a2a7035..1cb511e 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/reviewers/ReviewersIT.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.plugins.reviewers;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.extensions.client.ReviewerState.CC;
 import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER;
 import static java.util.stream.Collectors.toSet;
 
@@ -27,6 +28,7 @@
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.client.ChangeStatus;
+import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import java.util.Set;
 import org.junit.Test;
@@ -38,10 +40,19 @@
   @Test
   public void addReviewers() throws Exception {
     TestAccount user2 = accountCreator.user2();
-    createFilters(filter("*").reviewer(user).reviewer(user2));
+    createFilters(filter("*").reviewer(user).cc(user2));
     String changeId = createChange().getChangeId();
-    assertThat(reviewersFor(changeId))
-        .containsExactlyElementsIn(ImmutableSet.of(user.id(), user2.id()));
+    assertThat(reviewersFor(changeId)).containsExactlyElementsIn(ImmutableSet.of(user.id()));
+    assertThat(ccsFor(changeId)).containsExactlyElementsIn(ImmutableSet.of(user2.id()));
+  }
+
+  @Test
+  public void addReviewerMatchingReviewerAndCc() throws Exception {
+    TestAccount user2 = accountCreator.user2();
+    createFilters(filter("*").cc(user).cc(user2), filter("^a.txt").reviewer(user2));
+    String changeId = createChange().getChangeId();
+    assertThat(reviewersFor(changeId)).containsExactlyElementsIn(ImmutableSet.of(user2.id()));
+    assertThat(ccsFor(changeId)).containsExactlyElementsIn(ImmutableSet.of(user.id()));
   }
 
   @Test
@@ -54,8 +65,9 @@
   }
 
   @Test
-  public void doNotAddReviewersFromNonMatchingFilters() throws Exception {
-    createFilters(filter("branch:master").reviewer(user));
+  public void doNotAddReviewersOrCcFromNonMatchingFilters() throws Exception {
+    TestAccount user2 = accountCreator.user2();
+    createFilters(filter("branch:master").reviewer(user).cc(user2));
     createBranch(BranchNameKey.create(project, "other-branch"));
     // Create a change that matches the filter section.
     createChange("refs/for/master");
@@ -113,13 +125,23 @@
     assertThat(reviewersFor(changeId)).containsExactlyElementsIn(ImmutableSet.of(user.id()));
   }
 
+  private Set<Account.Id> ccsFor(String changeId) throws Exception {
+    return reviewersFor(changeId, CC);
+  }
+
   private Set<Account.Id> reviewersFor(String changeId) throws Exception {
-    return gApi.changes().id(changeId).get().reviewers.get(REVIEWER).stream()
+    return reviewersFor(changeId, REVIEWER);
+  }
+
+  private Set<Account.Id> reviewersFor(String changeId, ReviewerState reviewerState)
+      throws Exception {
+    return gApi.changes().id(changeId).get().reviewers.get(reviewerState).stream()
         .map(a -> Account.id(a._accountId))
         .collect(toSet());
   }
 
   private void assertNoReviewersAddedFor(String changeId) throws Exception {
     assertThat(gApi.changes().id(changeId).get().reviewers.get(REVIEWER)).isNull();
+    assertThat(gApi.changes().id(changeId).get().reviewers.get(CC)).isNull();
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfigIT.java b/src/test/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfigIT.java
index 9e4c06f..cd593c8 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfigIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/reviewers/config/ReviewersConfigIT.java
@@ -47,27 +47,39 @@
   }
 
   @Test
-  public void reviewersConfigWithMergedInheritance() throws Exception {
+  public void reviewersConfigSingleWithCc() throws Exception {
+    createFilters(filter(NO_FILTER).reviewer(JOHN_DOE).cc(JANE_DOE));
+
+    assertProjectHasFilters(project, filter(NO_FILTER).reviewer(JOHN_DOE).cc(JANE_DOE));
+  }
+
+  @Test
+  public void reviewersConfigSingleWithCcSeparateFilters() throws Exception {
+    createFilters(filter(NO_FILTER).reviewer(JOHN_DOE), filter(BRANCH_MAIN).cc(JANE_DOE));
+
+    assertProjectHasFilters(
+        project, filter(NO_FILTER).reviewer(JOHN_DOE), filter(BRANCH_MAIN).cc(JANE_DOE));
+  }
+
+  @Test
+  public void reviewersConfigWithMergedInheritanceWithCc() throws Exception {
     Project.NameKey childProject = projectOperations.newProject().parent(project).create();
     TestRepository<?> childTestRepo = checkoutRefsMetaConfig(cloneProject(childProject));
 
     createFiltersFor(
-        childTestRepo,
-        filter(NO_FILTER).reviewer(JANE_DOE),
-        filter(BRANCH_MAIN).reviewer(JANE_DOE));
+        childTestRepo, filter(NO_FILTER).reviewer(JANE_DOE), filter(BRANCH_MAIN).cc(JANE_DOE));
 
-    createFilters(filter(NO_FILTER).reviewer(JOHN_DOE), filter(BRANCH_MAIN).reviewer(JOHN_DOE));
+    createFilters(filter(NO_FILTER).cc(JOHN_DOE), filter(BRANCH_MAIN).reviewer(JOHN_DOE));
 
     assertProjectHasFilters(
         childProject,
-        filter(NO_FILTER).reviewer(JOHN_DOE).reviewer(JANE_DOE),
-        filter(BRANCH_MAIN).reviewer(JOHN_DOE).reviewer(JANE_DOE));
+        filter(NO_FILTER).cc(JOHN_DOE).reviewer(JANE_DOE),
+        filter(BRANCH_MAIN).reviewer(JOHN_DOE).cc(JANE_DOE));
   }
 
   private void assertProjectHasFilters(Project.NameKey project, TestFilter... filters) {
     assertThat(reviewersConfig().filtersWithInheritance(project))
-        .containsExactlyElementsIn(ImmutableList.copyOf(filters))
-        .inOrder();
+        .containsExactlyElementsIn(ImmutableList.copyOf(filters));
   }
 
   private ReviewersConfig reviewersConfig() {