Merge branch 'stable-3.3' into stable-3.4

* stable-3.3:
  Allow to auto-assign to CC instead of reviewer
  Trigger owners-autoassign when setting WIP to false
  Only add owners with permissions to attention-set
  Reformat with GJF
  Update build instructions to include owners-api
  Fix linking to owners-api in standalone build
  Allow to skip auto-assign on WIP changes
  Upgrade bazlets to build with 3.3.4 API

Change-Id: Ibe8cd1d82cf65718c1072e611f37f29d55d1f97c
diff --git a/README.md b/README.md
index 4c9519f..4893600 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,7 @@
 ```
   bazel-bin/owners/owners.jar
   bazel-bin/owners-autoassign/owners-autoassign.jar
+  bazel-bin/owners-api/owners-api.jar
 
 ```
 
@@ -91,6 +92,7 @@
    $ cd gerrit/plugins
    $ ln -s ../../owners/owners .
    $ ln -s ../../owners/owners-autoassign .
+   $ ln -s ../../owners/owners-api .
    $ ln -sf ../../owners/external_plugin_deps.bzl .
    $ cd ..
    $ ln -s ../owners/owners-common .
diff --git a/WORKSPACE b/WORKSPACE
index 67f7e9e..273e2eb 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,7 +3,7 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "4a27255dff75eadc98b86391806be13e030e6ff3",
+    commit = "10e78cc706760ff24cbc67ba527f9a8e4134d66f",
     #local_path = "/home/<user>/projects/bazlets",
 )
 
diff --git a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/AutoassignConfig.java b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/AutoassignConfig.java
new file mode 100644
index 0000000..546e0aa
--- /dev/null
+++ b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/AutoassignConfig.java
@@ -0,0 +1,55 @@
+// Copyright (C) 2021 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.owners.common;
+
+import static com.google.gerrit.extensions.client.InheritableBoolean.TRUE;
+import static com.googlesource.gerrit.owners.common.AutoassignConfigModule.PROJECT_CONFIG_AUTOASSIGN_FIELD;
+import static com.googlesource.gerrit.owners.common.AutoassignConfigModule.PROJECT_CONFIG_AUTOASSIGN_WIP_CHANGES;
+
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.client.ReviewerState;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+@Singleton
+public class AutoassignConfig {
+
+  private final PluginConfigFactory cfgFactory;
+  private final String pluginName;
+
+  @Inject
+  AutoassignConfig(@PluginName String pluginName, PluginConfigFactory cfgFactory) {
+    this.pluginName = pluginName;
+    this.cfgFactory = cfgFactory;
+  }
+
+  public boolean autoAssignWip(Project.NameKey projectKey) throws NoSuchProjectException {
+    return cfg(projectKey).getEnum(PROJECT_CONFIG_AUTOASSIGN_WIP_CHANGES, TRUE).equals(TRUE);
+  }
+
+  public ReviewerState autoassignedReviewerState(Project.NameKey projectKey)
+      throws NoSuchProjectException {
+    return cfg(projectKey).getEnum(PROJECT_CONFIG_AUTOASSIGN_FIELD, ReviewerState.REVIEWER);
+  }
+
+  private PluginConfig cfg(Project.NameKey projectKey) throws NoSuchProjectException {
+    return cfgFactory.getFromProjectConfigWithInheritance(projectKey, pluginName);
+  }
+}
diff --git a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/AutoassignConfigModule.java b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/AutoassignConfigModule.java
new file mode 100644
index 0000000..5536ebc
--- /dev/null
+++ b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/AutoassignConfigModule.java
@@ -0,0 +1,48 @@
+// Copyright (C) 2021 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.owners.common;
+
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
+import com.google.gerrit.extensions.client.InheritableBoolean;
+import com.google.gerrit.extensions.client.ReviewerState;
+import com.google.gerrit.server.config.ProjectConfigEntry;
+import com.google.inject.AbstractModule;
+import java.util.Arrays;
+
+public class AutoassignConfigModule extends AbstractModule {
+  public static final String PROJECT_CONFIG_AUTOASSIGN_WIP_CHANGES = "autoAssignWip";
+  public static final String PROJECT_CONFIG_AUTOASSIGN_FIELD = "autoAssignField";
+
+  @Override
+  protected void configure() {
+    bind(ProjectConfigEntry.class)
+        .annotatedWith(Exports.named(PROJECT_CONFIG_AUTOASSIGN_WIP_CHANGES))
+        .toInstance(
+            new ProjectConfigEntry(
+                "Auto-assign WIP changes", InheritableBoolean.TRUE, InheritableBoolean.class));
+    bind(ProjectConfigEntry.class)
+        .annotatedWith(Exports.named(PROJECT_CONFIG_AUTOASSIGN_FIELD))
+        .toInstance(
+            new ProjectConfigEntry(
+                "Auto-assign field",
+                ReviewerState.REVIEWER.name(),
+                ProjectConfigEntryType.STRING,
+                Arrays.asList(ReviewerState.CC.name(), ReviewerState.REVIEWER.name()),
+                true,
+                "Change field to use for the assigned accounts"));
+  }
+}
diff --git a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/AutoassignModule.java b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/AutoassignModule.java
index d6bdaef..4ed43b2 100644
--- a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/AutoassignModule.java
+++ b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/AutoassignModule.java
@@ -24,5 +24,6 @@
   @Override
   protected void configure() {
     DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(GitRefListener.class);
+    install(new AutoassignConfigModule());
   }
 }
diff --git a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/GitRefListener.java b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/GitRefListener.java
index 7792583..756332c 100644
--- a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/GitRefListener.java
+++ b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/GitRefListener.java
@@ -38,18 +38,25 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.notedb.ChangeNotesCommit;
+import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
 import com.google.gerrit.server.patch.PatchList;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.patch.PatchListKey;
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
+import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
+import java.util.List;
 import java.util.Set;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.FooterKey;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -58,6 +65,8 @@
 public class GitRefListener implements GitReferenceUpdatedListener {
   private static final Logger logger = LoggerFactory.getLogger(GitRefListener.class);
 
+  private static final FooterKey FOOTER_WORK_IN_PROGRESS = new FooterKey("Work-in-progress");
+
   private final GerritApi api;
 
   private final PatchListCache patchListCache;
@@ -71,6 +80,8 @@
 
   private ChangeNotes.Factory notesFactory;
 
+  private final AutoassignConfig cfg;
+
   @Inject
   public GitRefListener(
       GerritApi api,
@@ -80,7 +91,8 @@
       ReviewerManager reviewerManager,
       OneOffRequestContext oneOffReqCtx,
       Provider<CurrentUser> currentUserProvider,
-      ChangeNotes.Factory notesFactory) {
+      ChangeNotes.Factory notesFactory,
+      AutoassignConfig cfg) {
     this.api = api;
     this.patchListCache = patchListCache;
     this.repositoryManager = repositoryManager;
@@ -89,6 +101,7 @@
     this.oneOffReqCtx = oneOffReqCtx;
     this.currentUserProvider = currentUserProvider;
     this.notesFactory = notesFactory;
+    this.cfg = cfg;
   }
 
   @Override
@@ -98,46 +111,51 @@
       return;
     }
 
-    AccountInfo updaterAccountInfo = event.getUpdater();
-    CurrentUser currentUser = currentUserProvider.get();
-    if (currentUser.isIdentifiedUser()) {
-      handleGitReferenceUpdated(event);
-    } else if (updaterAccountInfo != null) {
-      handleGitReferenceUpdatedAsUser(event, Account.id(updaterAccountInfo._accountId));
-    } else {
-      handleGitReferenceUpdatedAsServer(event);
+    try {
+      AccountInfo updaterAccountInfo = event.getUpdater();
+      CurrentUser currentUser = currentUserProvider.get();
+      if (currentUser.isIdentifiedUser()) {
+        handleGitReferenceUpdated(event);
+      } else if (updaterAccountInfo != null) {
+        handleGitReferenceUpdatedAsUser(event, Account.id(updaterAccountInfo._accountId));
+      } else {
+        handleGitReferenceUpdatedAsServer(event);
+      }
+    } catch (StorageException | NoSuchProjectException e) {
+      logger.warn("Unable to process event {} on project {}", event, event.getProjectName(), e);
     }
   }
 
-  private void handleGitReferenceUpdatedAsUser(Event event, Account.Id updaterAccountId) {
+  private void handleGitReferenceUpdatedAsUser(Event event, Account.Id updaterAccountId)
+      throws NoSuchProjectException {
     try (ManualRequestContext ctx = oneOffReqCtx.openAs(updaterAccountId)) {
       handleGitReferenceUpdated(event);
-    } catch (StorageException e) {
-      logger.warn("Unable to process event {} on project {}", event, event.getProjectName(), e);
     }
   }
 
-  private void handleGitReferenceUpdatedAsServer(Event event) {
+  private void handleGitReferenceUpdatedAsServer(Event event) throws NoSuchProjectException {
     try (ManualRequestContext ctx = oneOffReqCtx.open()) {
       handleGitReferenceUpdated(event);
-    } catch (StorageException e) {
-      logger.warn("Unable to process event {} on project {}", event, event.getProjectName(), e);
     }
   }
 
-  private void handleGitReferenceUpdated(Event event) {
+  private void handleGitReferenceUpdated(Event event) throws NoSuchProjectException {
     String projectName = event.getProjectName();
     Repository repository;
     try {
       NameKey projectNameKey = Project.NameKey.parse(projectName);
+      boolean autoAssignWip = cfg.autoAssignWip(projectNameKey);
       repository = repositoryManager.openRepository(projectNameKey);
       try {
         String refName = event.getRefName();
         Change.Id changeId = Change.Id.fromRef(refName);
-        if (changeId != null
-            && (!RefNames.isNoteDbMetaRef(refName)
-                || isChangeSetReadyForReview(projectNameKey, changeId, event.getNewObjectId()))) {
-          processEvent(repository, event, changeId);
+        if (changeId != null) {
+          ChangeNotes changeNotes = notesFactory.createChecked(projectNameKey, changeId);
+          if ((!RefNames.isNoteDbMetaRef(refName)
+                  && isChangeToBeProcessed(changeNotes.getChange(), autoAssignWip))
+              || isChangeSetReadyForReview(repository, changeNotes, event.getNewObjectId())) {
+            processEvent(projectNameKey, repository, event, changeId);
+          }
         }
       } finally {
         repository.close();
@@ -147,18 +165,41 @@
     }
   }
 
-  private boolean isChangeSetReadyForReview(
-      NameKey project, Change.Id changeId, String metaObjectId) {
-    ChangeNotes changeNotes = notesFactory.createChecked(project, changeId);
-    return !changeNotes.getChange().isWorkInProgress()
-        && changeNotes.getChangeMessages().stream()
-            .filter(message -> message.getKey().uuid().equals(metaObjectId))
-            .map(message -> message.getTag())
-            .filter(Predicates.notNull())
-            .anyMatch(tag -> tag.contains(ChangeMessagesUtil.TAG_SET_READY));
+  private boolean isChangeToBeProcessed(Change change, boolean autoAssignWip) {
+    return !change.isWorkInProgress() || autoAssignWip;
   }
 
-  public void processEvent(Repository repository, Event event, Change.Id cId) {
+  private boolean isChangeSetReadyForReview(
+      Repository repository, ChangeNotes changeNotes, String metaObjectId)
+      throws MissingObjectException, IncorrectObjectTypeException, IOException {
+    if (changeNotes.getChange().isWorkInProgress()) {
+      return false;
+    }
+
+    if (changeNotes.getChangeMessages().stream()
+        .filter(message -> message.getKey().uuid().equals(metaObjectId))
+        .map(message -> message.getTag())
+        .filter(Predicates.notNull())
+        .anyMatch(tag -> tag.contains(ChangeMessagesUtil.TAG_SET_READY))) {
+      return true;
+    }
+
+    try (ChangeNotesRevWalk revWalk = ChangeNotesCommit.newRevWalk(repository)) {
+      ChangeNotesCommit metaCommit = revWalk.parseCommit(ObjectId.fromString(metaObjectId));
+      if (metaCommit.getParentCount() == 0) {
+        // The first commit cannot be a 'Set ready' operation
+        return false;
+      }
+      List<String> wipFooterLines = metaCommit.getFooterLines(FOOTER_WORK_IN_PROGRESS);
+      return wipFooterLines != null
+          && !wipFooterLines.isEmpty()
+          && Boolean.FALSE.toString().equalsIgnoreCase(wipFooterLines.get(0));
+    }
+  }
+
+  public void processEvent(
+      Project.NameKey projectNameKey, Repository repository, Event event, Change.Id cId)
+      throws NoSuchProjectException {
     Changes changes = api.changes();
     // The provider injected by Gerrit is shared with other workers on the
     // same local thread and thus cannot be closed in this event listener.
@@ -176,7 +217,7 @@
           allReviewers.addAll(matcher.getReviewers());
         }
         logger.debug("Autoassigned reviewers are: {}", allReviewers.toString());
-        reviewerManager.addReviewers(cApi, allReviewers);
+        reviewerManager.addReviewers(projectNameKey, cApi, allReviewers);
       }
     } catch (RestApiException e) {
       logger.warn("Could not open change: {}", cId, e);
diff --git a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/ReviewerManager.java b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/ReviewerManager.java
index eef9321..fe62307 100644
--- a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/ReviewerManager.java
+++ b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/ReviewerManager.java
@@ -19,17 +19,20 @@
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.Project.NameKey;
 import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.AttentionSetInput;
 import com.google.gerrit.extensions.api.changes.ChangeApi;
 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.registration.DynamicItem;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
@@ -38,6 +41,7 @@
 import com.googlesource.gerrit.owners.api.OwnersAttentionSet;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
@@ -64,41 +68,47 @@
   @Inject(optional = true)
   private DynamicItem<OwnersAttentionSet> ownersForAttentionSet;
 
+  private final AutoassignConfig cfg;
+
   @Inject
   public ReviewerManager(
       OneOffRequestContext requestContext,
       GerritApi gApi,
       IdentifiedUser.GenericFactory userFactory,
       ChangeData.Factory changeDataFactory,
-      PermissionBackend permissionBackend) {
+      PermissionBackend permissionBackend,
+      AutoassignConfig cfg) {
     this.requestContext = requestContext;
     this.gApi = gApi;
     this.userFactory = userFactory;
     this.changeDataFactory = changeDataFactory;
     this.permissionBackend = permissionBackend;
+    this.cfg = cfg;
   }
 
-  public void addReviewers(ChangeApi cApi, Collection<Account.Id> reviewers)
-      throws ReviewerManagerException {
+  public void addReviewers(
+      NameKey projectNameKey, ChangeApi cApi, Collection<Account.Id> accountsIds)
+      throws ReviewerManagerException, NoSuchProjectException {
     try {
       ChangeInfo changeInfo = cApi.get();
+      ReviewerState reviewerState = cfg.autoassignedReviewerState(projectNameKey);
       try (ManualRequestContext ctx =
           requestContext.openAs(Account.id(changeInfo.owner._accountId))) {
         // TODO(davido): Switch back to using changes API again,
         // when it supports batch mode for adding reviewers
         ReviewInput in = new ReviewInput();
-        Collection<Account.Id> reviewersAccounts =
-            Optional.ofNullable(ownersForAttentionSet)
-                .map(DynamicItem::get)
-                .filter(Objects::nonNull)
-                .map(owners -> owners.addToAttentionSet(changeInfo, reviewers))
-                .orElse(reviewers);
-        in.reviewers = new ArrayList<>(reviewers.size());
-        for (Account.Id account : reviewers) {
+        in.reviewers = new ArrayList<>(accountsIds.size());
+        Collection<Account.Id> validOwnersForAttentionSet = new ArrayList<>(accountsIds.size());
+        for (Account.Id account : accountsIds) {
           if (isVisibleTo(changeInfo, account)) {
             AddReviewerInput addReviewerInput = new AddReviewerInput();
             addReviewerInput.reviewer = account.toString();
+            addReviewerInput.state = reviewerState;
             in.reviewers.add(addReviewerInput);
+
+            if (reviewerState == ReviewerState.REVIEWER) {
+              validOwnersForAttentionSet.add(account);
+            }
           } else {
             log.warn(
                 "Not adding account {} as reviewer to change {} because the associated ref is not visible",
@@ -107,6 +117,18 @@
           }
         }
 
+        Collection<Account.Id> reviewersAccounts;
+        if (validOwnersForAttentionSet.isEmpty()) {
+          reviewersAccounts = Collections.emptyList();
+        } else {
+          reviewersAccounts =
+              Optional.ofNullable(ownersForAttentionSet)
+                  .map(DynamicItem::get)
+                  .filter(Objects::nonNull)
+                  .map(owners -> owners.addToAttentionSet(changeInfo, validOwnersForAttentionSet))
+                  .orElse(validOwnersForAttentionSet);
+        }
+
         in.ignoreAutomaticAttentionSetRules = true;
         in.addToAttentionSet =
             reviewersAccounts.stream()
diff --git a/owners-autoassign/src/main/resources/Documentation/config.md b/owners-autoassign/src/main/resources/Documentation/config.md
index 6db4b1d..afb534c 100644
--- a/owners-autoassign/src/main/resources/Documentation/config.md
+++ b/owners-autoassign/src/main/resources/Documentation/config.md
@@ -1,4 +1,15 @@
-## Configuration
+## Project configuration
+
+The project configuration `autoAssignWip` controls the automatic
+assignment of reviewers based on the OWNERS file on WIP changes.
+
+The setting can be inherited from the parent project by setting the value
+to `INHERIT`.
+
+By default, all changes are subject to auto-assignment, unless the project
+or one of its parent projects has the `autoAssignWip` set to `FALSE`.
+
+## OWNERS configuration
 
 Owner approval is determined based on OWNERS files located in the same
 repository on the target branch of the changes uploaded for review.
diff --git a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/AbstractAutoassignIT.java b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/AbstractAutoassignIT.java
index e8fa11b..cabe670 100644
--- a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/AbstractAutoassignIT.java
+++ b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/AbstractAutoassignIT.java
@@ -16,13 +16,17 @@
 package com.googlesource.gerrit.owners.common;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.googlesource.gerrit.owners.common.AutoassignConfigModule.PROJECT_CONFIG_AUTOASSIGN_FIELD;
 
 import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
 import com.google.gerrit.extensions.api.changes.ChangeApi;
 import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import com.google.gerrit.server.project.ProjectConfig;
 import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
 import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -31,12 +35,20 @@
 
 @Ignore
 public abstract class AbstractAutoassignIT extends LightweightPluginDaemonTest {
+  private static final String PLUGIN_NAME = "owners-api";
+
   private final String section;
   private final boolean INHERITED = true;
   private final boolean NOT_INHERITED = false;
+  private final ReviewerState assignedUserState;
 
-  AbstractAutoassignIT(String section) {
+  @SuppressWarnings("hiding")
+  @Inject
+  private ProjectConfig.Factory projectConfigFactory;
+
+  AbstractAutoassignIT(String section, ReviewerState assignedUserState) {
     this.section = section;
+    this.assignedUserState = assignedUserState;
   }
 
   public static class TestModule extends AbstractModule {
@@ -46,6 +58,21 @@
     }
   }
 
+  @Override
+  public void setUpTestPlugin() throws Exception {
+    super.setUpTestPlugin();
+
+    try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
+      ProjectConfig projectConfig = projectConfigFactory.create(project);
+      projectConfig.load(md);
+      projectConfig.updatePluginConfig(
+          PLUGIN_NAME,
+          cfg -> cfg.setString(PROJECT_CONFIG_AUTOASSIGN_FIELD, assignedUserState.name()));
+      projectConfig.commit(md);
+      projectCache.evict(project);
+    }
+  }
+
   @Test
   public void shouldAutoassignOneOwner() throws Exception {
     String ownerEmail = user.email();
@@ -70,7 +97,7 @@
 
     addOwnersToRepo("", ownerEmail, NOT_INHERITED);
 
-    Collection<AccountInfo> reviewers = getChangeReviewers(change(createChange()));
+    Collection<AccountInfo> reviewers = getAutoassignedAccounts(change(createChange()));
 
     assertThat(reviewers).isNotNull();
     assertThat(reviewers).hasSize(1);
@@ -87,7 +114,7 @@
     addOwnersToRepo(childpath, childOwnersEmail, INHERITED);
 
     Collection<AccountInfo> reviewers =
-        getChangeReviewers(change(createChange("test change", childpath + "foo.txt", "foo")));
+        getAutoassignedAccounts(change(createChange("test change", childpath + "foo.txt", "foo")));
 
     assertThat(reviewers).isNotNull();
     assertThat(reviewersEmail(reviewers)).containsExactly(parentOwnersEmail, childOwnersEmail);
@@ -103,7 +130,7 @@
     addOwnersToRepo(childpath, childOwnersEmail, NOT_INHERITED);
 
     Collection<AccountInfo> reviewers =
-        getChangeReviewers(change(createChange("test change", childpath + "foo.txt", "foo")));
+        getAutoassignedAccounts(change(createChange("test change", childpath + "foo.txt", "foo")));
 
     assertThat(reviewers).isNotNull();
     assertThat(reviewersEmail(reviewers)).containsExactly(childOwnersEmail);
@@ -116,7 +143,7 @@
     addOwnersToRepo("", "suffix", ".java", ownerEmail, NOT_INHERITED);
 
     Collection<AccountInfo> reviewers =
-        getChangeReviewers(change(createChange("test change", "foo.java", "foo")));
+        getAutoassignedAccounts(change(createChange("test change", "foo.java", "foo")));
 
     assertThat(reviewers).isNotNull();
     assertThat(reviewersEmail(reviewers)).containsExactly(ownerEmail);
@@ -129,7 +156,7 @@
     addOwnersToRepo("", "suffix", ".java", ownerEmail, NOT_INHERITED);
 
     ChangeApi changeApi = change(createChange("test change", "foo.bar", "foo"));
-    Collection<AccountInfo> reviewers = getChangeReviewers(changeApi);
+    Collection<AccountInfo> reviewers = getAutoassignedAccounts(changeApi);
 
     assertThat(reviewers).isNull();
   }
@@ -144,7 +171,7 @@
     addOwnersToRepo(childpath, "suffix", ".java", childOwnersEmail, INHERITED);
 
     ChangeApi changeApi = change(createChange("test change", childpath + "foo.java", "foo"));
-    Collection<AccountInfo> reviewers = getChangeReviewers(changeApi);
+    Collection<AccountInfo> reviewers = getAutoassignedAccounts(changeApi);
 
     assertThat(reviewers).isNotNull();
     assertThat(reviewersEmail(reviewers)).containsExactly(parentOwnersEmail, childOwnersEmail);
@@ -160,14 +187,15 @@
     addOwnersToRepo(childpath, "suffix", ".java", childOwnersEmail, NOT_INHERITED);
 
     ChangeApi changeApi = change(createChange("test change", childpath + "foo.java", "foo"));
-    Collection<AccountInfo> reviewers = getChangeReviewers(changeApi);
+    Collection<AccountInfo> reviewers = getAutoassignedAccounts(changeApi);
 
     assertThat(reviewers).isNotNull();
     assertThat(reviewersEmail(reviewers)).containsExactly(childOwnersEmail);
   }
 
-  private Collection<AccountInfo> getChangeReviewers(ChangeApi changeApi) throws RestApiException {
-    Collection<AccountInfo> reviewers = changeApi.get().reviewers.get(ReviewerState.REVIEWER);
+  private Collection<AccountInfo> getAutoassignedAccounts(ChangeApi changeApi)
+      throws RestApiException {
+    Collection<AccountInfo> reviewers = changeApi.get().reviewers.get(assignedUserState);
     return reviewers;
   }
 
diff --git a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignAsCcIT.java
similarity index 80%
rename from owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java
rename to owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignAsCcIT.java
index dbe6d41..ff71c87 100644
--- a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java
+++ b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignAsCcIT.java
@@ -16,13 +16,14 @@
 package com.googlesource.gerrit.owners.common;
 
 import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.extensions.client.ReviewerState;
 
 @TestPlugin(
     name = "owners-api",
     sysModule = "com.googlesource.gerrit.owners.common.AbstractAutoassignIT$TestModule")
-public class OwnersAutoassignIT extends AbstractAutoassignIT {
+public class OwnersAutoassignAsCcIT extends AbstractAutoassignIT {
 
-  public OwnersAutoassignIT() {
-    super("owners");
+  public OwnersAutoassignAsCcIT() {
+    super("owners", ReviewerState.CC);
   }
 }
diff --git a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignAsReviewerIT.java
similarity index 79%
copy from owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java
copy to owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignAsReviewerIT.java
index dbe6d41..06db78d 100644
--- a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java
+++ b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignAsReviewerIT.java
@@ -16,13 +16,14 @@
 package com.googlesource.gerrit.owners.common;
 
 import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.extensions.client.ReviewerState;
 
 @TestPlugin(
     name = "owners-api",
     sysModule = "com.googlesource.gerrit.owners.common.AbstractAutoassignIT$TestModule")
-public class OwnersAutoassignIT extends AbstractAutoassignIT {
+public class OwnersAutoassignAsReviewerIT extends AbstractAutoassignIT {
 
-  public OwnersAutoassignIT() {
-    super("owners");
+  public OwnersAutoassignAsReviewerIT() {
+    super("owners", ReviewerState.REVIEWER);
   }
 }
diff --git a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignWithAttentionSetIT.java b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignWithAttentionSetIT.java
index b26b2f9..36795a6 100644
--- a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignWithAttentionSetIT.java
+++ b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignWithAttentionSetIT.java
@@ -16,27 +16,40 @@
 package com.googlesource.gerrit.owners.common;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
 import static java.util.stream.Collectors.toList;
 
 import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
+import com.google.gerrit.acceptance.TestAccount;
 import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.entities.AccessSection;
 import com.google.gerrit.entities.Account.Id;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Permission;
+import com.google.gerrit.extensions.api.groups.GroupInput;
 import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.Scopes;
 import com.googlesource.gerrit.owners.api.OwnersApiModule;
 import com.googlesource.gerrit.owners.api.OwnersAttentionSet;
 import java.util.Collection;
+import java.util.Collections;
 import org.junit.Test;
 
 @TestPlugin(
     name = "owners-autoassign",
-    sysModule = "com.googlesource.gerrit.owners.common.OwnersAutoassignWithAttentionSetIT$TestModule")
+    sysModule =
+        "com.googlesource.gerrit.owners.common.OwnersAutoassignWithAttentionSetIT$TestModule")
 public class OwnersAutoassignWithAttentionSetIT extends LightweightPluginDaemonTest {
 
+  @Inject private ProjectOperations projectOperations;
+
   @Override
   public Module createModule() {
     return new OwnersApiModule();
@@ -66,6 +79,35 @@
     String ownerEmail1 = user.email();
     String ownerEmail2 = accountCreator.user2().email();
 
+    setOwners(ownerEmail1, ownerEmail2);
+
+    ChangeInfo change = change(createChange()).get();
+    assertThat(change.reviewers.get(ReviewerState.REVIEWER)).hasSize(2);
+    assertThat(change.attentionSet).hasSize(1);
+  }
+
+  @Test
+  public void shouldAddToAttentionSetOneUserIfAnotherUserHasNoPermission() throws Exception {
+    TestAccount userWithAccessToProject = accountCreator.user();
+    TestAccount userWithNoAccessToProject = accountCreator.user2();
+
+    AccountGroup.UUID groupWithNoAccessToProject =
+        createGroup("groupWithNoAccessToProject", userWithNoAccessToProject);
+
+    setOwners(userWithAccessToProject.email(), userWithNoAccessToProject.email());
+
+    projectOperations
+        .project(project)
+        .forUpdate()
+        .add(block(Permission.READ).ref(AccessSection.ALL).group(groupWithNoAccessToProject))
+        .update();
+
+    ChangeInfo change = change(createChange()).get();
+    assertThat(change.attentionSet).isNotNull();
+    assertThat(change.attentionSet.keySet()).containsExactly(userWithAccessToProject.id().get());
+  }
+
+  private void setOwners(String ownerEmail1, String ownerEmail2) throws Exception {
     pushFactory
         .create(
             admin.newIdent(),
@@ -82,9 +124,12 @@
                 + "\n")
         .to("refs/heads/master")
         .assertOkStatus();
+  }
 
-    ChangeInfo change = change(createChange()).get();
-    assertThat(change.reviewers.get(ReviewerState.REVIEWER)).hasSize(2);
-    assertThat(change.attentionSet).hasSize(1);
+  private AccountGroup.UUID createGroup(String name, TestAccount member) throws RestApiException {
+    GroupInput groupInput = new GroupInput();
+    groupInput.name = name(name);
+    groupInput.members = Collections.singletonList(String.valueOf(member.id().get()));
+    return AccountGroup.uuid(gApi.groups().create(groupInput).get().id);
   }
 }
diff --git a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/ReviewersAutoassignAsCcIT.java
similarity index 79%
copy from owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java
copy to owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/ReviewersAutoassignAsCcIT.java
index dbe6d41..ce9cc02 100644
--- a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java
+++ b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/ReviewersAutoassignAsCcIT.java
@@ -16,13 +16,14 @@
 package com.googlesource.gerrit.owners.common;
 
 import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.extensions.client.ReviewerState;
 
 @TestPlugin(
     name = "owners-api",
     sysModule = "com.googlesource.gerrit.owners.common.AbstractAutoassignIT$TestModule")
-public class OwnersAutoassignIT extends AbstractAutoassignIT {
+public class ReviewersAutoassignAsCcIT extends AbstractAutoassignIT {
 
-  public OwnersAutoassignIT() {
-    super("owners");
+  public ReviewersAutoassignAsCcIT() {
+    super("reviewers", ReviewerState.CC);
   }
 }
diff --git a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/ReviewersAutoassignAsReviewerIT.java
similarity index 78%
copy from owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java
copy to owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/ReviewersAutoassignAsReviewerIT.java
index dbe6d41..8e2e45b 100644
--- a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/OwnersAutoassignIT.java
+++ b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/ReviewersAutoassignAsReviewerIT.java
@@ -16,13 +16,14 @@
 package com.googlesource.gerrit.owners.common;
 
 import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.extensions.client.ReviewerState;
 
 @TestPlugin(
     name = "owners-api",
     sysModule = "com.googlesource.gerrit.owners.common.AbstractAutoassignIT$TestModule")
-public class OwnersAutoassignIT extends AbstractAutoassignIT {
+public class ReviewersAutoassignAsReviewerIT extends AbstractAutoassignIT {
 
-  public OwnersAutoassignIT() {
-    super("owners");
+  public ReviewersAutoassignAsReviewerIT() {
+    super("reviewers", ReviewerState.REVIEWER);
   }
 }
diff --git a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/ReviewersAutoassignIT.java b/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/ReviewersAutoassignIT.java
deleted file mode 100644
index c88edfb..0000000
--- a/owners-autoassign/src/test/java/com/googlesource/gerrit/owners/common/ReviewersAutoassignIT.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2021 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.owners.common;
-
-import com.google.gerrit.acceptance.TestPlugin;
-
-@TestPlugin(
-    name = "owners-api",
-    sysModule = "com.googlesource.gerrit.owners.common.AbstractAutoassignIT$TestModule")
-public class ReviewersAutoassignIT extends AbstractAutoassignIT {
-
-  public ReviewersAutoassignIT() {
-    super("reviewers");
-  }
-}
diff --git a/owners-autoassign/src/test/java/com/vmware/gerrit/owners/common/GitRefListenerIT.java b/owners-autoassign/src/test/java/com/vmware/gerrit/owners/common/GitRefListenerIT.java
index 1e0f8ec..2cbceda 100644
--- a/owners-autoassign/src/test/java/com/vmware/gerrit/owners/common/GitRefListenerIT.java
+++ b/owners-autoassign/src/test/java/com/vmware/gerrit/owners/common/GitRefListenerIT.java
@@ -15,19 +15,29 @@
 
 package com.vmware.gerrit.owners.common;
 
+import static com.googlesource.gerrit.owners.common.AutoassignConfigModule.PROJECT_CONFIG_AUTOASSIGN_WIP_CHANGES;
 import static org.junit.Assert.assertEquals;
 
 import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
+import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.acceptance.TestPlugin;
-import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.api.projects.ConfigInput;
+import com.google.gerrit.extensions.api.projects.ConfigValue;
+import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.extensions.registration.Extension;
+import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestContext;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
+import com.googlesource.gerrit.owners.common.AutoassignConfigModule;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.stream.StreamSupport;
 import org.eclipse.jgit.transport.ReceiveCommand.Type;
 import org.junit.Test;
@@ -36,6 +46,7 @@
     name = "owners-autoassign",
     sysModule = "com.vmware.gerrit.owners.common.GitRefListenerIT$TestModule")
 public class GitRefListenerIT extends LightweightPluginDaemonTest {
+  private static final String PLUGIN_NAME = "owners-autoassign";
 
   @Inject DynamicSet<GitReferenceUpdatedListener> allRefUpdateListeners;
   @Inject ThreadLocalRequestContext requestContext;
@@ -47,24 +58,16 @@
     @Override
     protected void configure() {
       DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(GitRefListenerTest.class);
+      install(new AutoassignConfigModule());
     }
   }
 
   @Test
   public void shouldNotProcessNoteDbOnlyRefs() throws Exception {
-    String changeRefPrefix = createChange().getChange().getId().toRefPrefix();
+    int changeNum = createChange().getChange().getId().get();
     int baselineProcessedEvents = gitRefListener().getProcessedEvents();
 
-    ReferenceUpdatedEventTest refUpdatedEvent =
-        new ReferenceUpdatedEventTest(
-            project,
-            changeRefPrefix + RefNames.META_SUFFIX.substring(1),
-            anOldObjectId,
-            aNewObjectId,
-            Type.CREATE,
-            admin.id());
-
-    gitRefListener().onGitReferenceUpdated(refUpdatedEvent);
+    gApi.changes().id(changeNum).current().review(new ReviewInput().message("Foo comment"));
     assertEquals(baselineProcessedEvents, gitRefListener().getProcessedEvents());
   }
 
@@ -80,6 +83,69 @@
   }
 
   @Test
+  public void shouldProcessSendAndStartReviewOnNoteDb() throws Exception {
+    int wipChangeNum = createChange().getChange().getId().get();
+    gApi.changes().id(wipChangeNum).setWorkInProgress();
+
+    int baselineProcessedEvents = gitRefListener().getProcessedEvents();
+
+    ReviewInput input = new ReviewInput().message("Let's start the review");
+    input.setWorkInProgress(false);
+
+    RestResponse resp =
+        adminRestSession.post("/changes/" + wipChangeNum + "/revisions/1/review", input);
+    resp.assertOK();
+
+    assertEquals(1, gitRefListener().getProcessedEvents() - baselineProcessedEvents);
+  }
+
+  @Test
+  public void shouldProcessWipChangesByDefault() throws Exception {
+    int baselineProcessedEvents = gitRefListener().getProcessedEvents();
+
+    createChange("refs/for/master%wip");
+
+    assertEquals(1, gitRefListener().getProcessedEvents() - baselineProcessedEvents);
+  }
+
+  @Test
+  public void shouldNotProcessWipChanges() throws Exception {
+    setProjectPluginConfig(
+        project, PROJECT_CONFIG_AUTOASSIGN_WIP_CHANGES, InheritableBoolean.FALSE.name());
+    int baselineProcessedEvents = gitRefListener().getProcessedEvents();
+
+    createChange("refs/for/master%wip");
+
+    assertEquals(baselineProcessedEvents, gitRefListener().getProcessedEvents());
+  }
+
+  @Test
+  public void shouldNotProcessWipChangesWhenAutoAssignWipChangesIsInherited() throws Exception {
+    setProjectPluginConfig(
+        allProjects, PROJECT_CONFIG_AUTOASSIGN_WIP_CHANGES, InheritableBoolean.FALSE.name());
+    setProjectPluginConfig(
+        project, PROJECT_CONFIG_AUTOASSIGN_WIP_CHANGES, InheritableBoolean.INHERIT.name());
+    int baselineProcessedEvents = gitRefListener().getProcessedEvents();
+
+    createChange("refs/for/master%wip");
+
+    assertEquals(baselineProcessedEvents, gitRefListener().getProcessedEvents());
+  }
+
+  @Test
+  public void shouldProcessWipChangesWhenAutoAssignWipChangesIsOverridden() throws Exception {
+    setProjectPluginConfig(
+        allProjects, PROJECT_CONFIG_AUTOASSIGN_WIP_CHANGES, InheritableBoolean.FALSE.name());
+    setProjectPluginConfig(
+        project, PROJECT_CONFIG_AUTOASSIGN_WIP_CHANGES, InheritableBoolean.TRUE.name());
+    int baselineProcessedEvents = gitRefListener().getProcessedEvents();
+
+    createChange("refs/for/master%wip");
+
+    assertEquals(1, gitRefListener().getProcessedEvents() - baselineProcessedEvents);
+  }
+
+  @Test
   public void shoulNotProcessEmptyMetaRefUpdatesAfterSetReadyForReviewOnNoteDb() throws Exception {
     int wipChangeNum = createChange().getChange().getId().get();
     gApi.changes().id(wipChangeNum).setWorkInProgress();
@@ -116,6 +182,19 @@
     }
   }
 
+  private void setProjectPluginConfig(Project.NameKey projectName, String configKey, String value)
+      throws RestApiException {
+    ConfigInput projectConfig = new ConfigInput();
+    Map<String, ConfigValue> ownerAutoassignConfig = new HashMap<>();
+    ConfigValue configValue = new ConfigValue();
+    configValue.value = value;
+    ownerAutoassignConfig.put(configKey, configValue);
+    projectConfig.pluginConfigValues = new HashMap<>();
+    projectConfig.pluginConfigValues.put(PLUGIN_NAME, ownerAutoassignConfig);
+
+    gApi.projects().name(projectName.get()).config(projectConfig);
+  }
+
   private GitRefListenerTest gitRefListener() {
     return (GitRefListenerTest)
         StreamSupport.stream(allRefUpdateListeners.entries().spliterator(), false)
diff --git a/owners-autoassign/src/test/java/com/vmware/gerrit/owners/common/GitRefListenerTest.java b/owners-autoassign/src/test/java/com/vmware/gerrit/owners/common/GitRefListenerTest.java
index 75a9189..e460136 100644
--- a/owners-autoassign/src/test/java/com/vmware/gerrit/owners/common/GitRefListenerTest.java
+++ b/owners-autoassign/src/test/java/com/vmware/gerrit/owners/common/GitRefListenerTest.java
@@ -16,8 +16,8 @@
 package com.vmware.gerrit.owners.common;
 
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.events.GitReferenceUpdatedListener.Event;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotes;
@@ -27,6 +27,7 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.owners.common.Accounts;
+import com.googlesource.gerrit.owners.common.AutoassignConfig;
 import com.googlesource.gerrit.owners.common.GitRefListener;
 import com.googlesource.gerrit.owners.common.ReviewerManager;
 import org.eclipse.jgit.lib.Repository;
@@ -46,7 +47,8 @@
       ReviewerManager reviewerManager,
       OneOffRequestContext oneOffReqCtx,
       Provider<CurrentUser> currentUserProvider,
-      ChangeNotes.Factory notesFactory) {
+      ChangeNotes.Factory notesFactory,
+      AutoassignConfig cfg) {
     super(
         api,
         patchListCache,
@@ -55,11 +57,13 @@
         reviewerManager,
         oneOffReqCtx,
         currentUserProvider,
-        notesFactory);
+        notesFactory,
+        cfg);
   }
 
   @Override
-  public void processEvent(Repository repository, Event event, Change.Id cId) {
+  public void processEvent(
+      Project.NameKey projectKey, Repository repository, Event event, Change.Id cId) {
     processedEvents++;
   }
 
diff --git a/owners-common/src/main/java/com/googlesource/gerrit/owners/common/JgitWrapper.java b/owners-common/src/main/java/com/googlesource/gerrit/owners/common/JgitWrapper.java
index c1a7368..0f0db10 100644
--- a/owners-common/src/main/java/com/googlesource/gerrit/owners/common/JgitWrapper.java
+++ b/owners-common/src/main/java/com/googlesource/gerrit/owners/common/JgitWrapper.java
@@ -40,8 +40,7 @@
     }
 
     try (final TreeWalk w =
-        TreeWalk.forPath(
-            repository, path, parseCommit(repository, objectId).getTree())) {
+        TreeWalk.forPath(repository, path, parseCommit(repository, objectId).getTree())) {
 
       return Optional.ofNullable(w)
           .filter(walk -> (walk.getRawMode(0) & TYPE_MASK) == TYPE_FILE)
diff --git a/plugins/owners-api b/plugins/owners-api
new file mode 120000
index 0000000..d90303f
--- /dev/null
+++ b/plugins/owners-api
@@ -0,0 +1 @@
+../owners-api
\ No newline at end of file