Add extension API and acceptance tests for submit type rules

Change-Id: I23ffb1f78ee3919994fba9797edcf7f132c92a65
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
new file mode 100644
index 0000000..d91eacd
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
@@ -0,0 +1,202 @@
+// Copyright (C) 2015 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.google.gerrit.acceptance.api.change;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.extensions.client.SubmitType.CHERRY_PICK;
+import static com.google.gerrit.extensions.client.SubmitType.FAST_FORWARD_ONLY;
+import static com.google.gerrit.extensions.client.SubmitType.MERGE_ALWAYS;
+import static com.google.gerrit.extensions.client.SubmitType.MERGE_IF_NECESSARY;
+import static com.google.gerrit.extensions.client.SubmitType.REBASE_IF_NECESSARY;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.api.projects.BranchInput;
+import com.google.gerrit.extensions.client.SubmitType;
+import com.google.gerrit.extensions.common.TestSubmitRuleInput;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.VersionedMetaData;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@NoHttpd
+public class SubmitTypeRuleIT extends AbstractDaemonTest {
+  private class RulesPl extends VersionedMetaData {
+    private static final String FILENAME = "rules.pl";
+
+    private String rule;
+
+    @Override
+    protected String getRefName() {
+      return RefNames.REFS_CONFIG;
+    }
+
+    @Override
+    protected void onLoad() throws IOException, ConfigInvalidException {
+      rule = readUTF8(FILENAME);
+    }
+
+    @Override
+    protected boolean onSave(CommitBuilder commit)
+        throws IOException, ConfigInvalidException {
+      TestSubmitRuleInput in = new TestSubmitRuleInput();
+      in.rule = rule;
+      try {
+        gApi.changes().id(testChangeId.get()).current().testSubmitType(in);
+      } catch (RestApiException e) {
+        throw new ConfigInvalidException("Invalid submit type rule", e);
+      }
+
+      saveUTF8(FILENAME, rule);
+      return true;
+    }
+  }
+
+  private AtomicInteger fileCounter;
+  private Change.Id testChangeId;
+
+  @Before
+  public void setUp() throws Exception {
+    fileCounter = new AtomicInteger();
+    gApi.projects().name(project.get()).branch("test")
+        .create(new BranchInput());
+    testChangeId = createChange("test", "test change").getChange().getId();
+  }
+
+  private void setRulesPl(String rule) throws Exception {
+    try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
+      RulesPl r = new RulesPl();
+      r.load(md);
+      r.rule = rule;
+      r.commit(md);
+    }
+  }
+
+  private static final String SUBMIT_TYPE_FROM_SUBJECT =
+      "submit_type(fast_forward_only) :-"
+      + "gerrit:commit_message(M),"
+      + "regex_matches('.*FAST_FORWARD_ONLY.*', M),"
+      + "!.\n"
+      + "submit_type(merge_if_necessary) :-"
+      + "gerrit:commit_message(M),"
+      + "regex_matches('.*MERGE_IF_NECESSARY.*', M),"
+      + "!.\n"
+      + "submit_type(rebase_if_necessary) :-"
+      + "gerrit:commit_message(M),"
+      + "regex_matches('.*REBASE_IF_NECESSARY.*', M),"
+      + "!.\n"
+      + "submit_type(merge_always) :-"
+      + "gerrit:commit_message(M),"
+      + "regex_matches('.*MERGE_ALWAYS.*', M),"
+      + "!.\n"
+      + "submit_type(cherry_pick) :-"
+      + "gerrit:commit_message(M),"
+      + "regex_matches('.*CHERRY_PICK.*', M),"
+      + "!.\n"
+      + "submit_type(T) :- gerrit:project_default_submit_type(T).";
+
+  private PushOneCommit.Result createChange(String dest, String subject)
+      throws Exception {
+    PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo,
+        subject, "file" + fileCounter.incrementAndGet(),
+        PushOneCommit.FILE_CONTENT);
+    PushOneCommit.Result r = push.to("refs/for/" + dest);
+    r.assertOkStatus();
+    return r;
+  }
+
+  @Test
+  public void unconditionalCherryPick() throws Exception {
+    PushOneCommit.Result r = createChange();
+    assertSubmitType(MERGE_IF_NECESSARY, r.getChangeId());
+    setRulesPl("submit_type(cherry_pick).");
+    assertSubmitType(CHERRY_PICK, r.getChangeId());
+  }
+
+  @Test
+  public void submitTypeFromSubject() throws Exception {
+    PushOneCommit.Result r1 = createChange("master", "Default 1");
+    PushOneCommit.Result r2 = createChange("master", "FAST_FORWARD_ONLY 2");
+    PushOneCommit.Result r3 = createChange("master", "MERGE_IF_NECESSARY 3");
+    PushOneCommit.Result r4 = createChange("master", "REBASE_IF_NECESSARY 4");
+    PushOneCommit.Result r5 = createChange("master", "MERGE_ALWAYS 5");
+    PushOneCommit.Result r6 = createChange("master", "CHERRY_PICK 6");
+
+    assertSubmitType(MERGE_IF_NECESSARY, r1.getChangeId());
+    assertSubmitType(MERGE_IF_NECESSARY, r2.getChangeId());
+    assertSubmitType(MERGE_IF_NECESSARY, r3.getChangeId());
+    assertSubmitType(MERGE_IF_NECESSARY, r4.getChangeId());
+    assertSubmitType(MERGE_IF_NECESSARY, r5.getChangeId());
+    assertSubmitType(MERGE_IF_NECESSARY, r6.getChangeId());
+
+    setRulesPl(SUBMIT_TYPE_FROM_SUBJECT);
+
+    assertSubmitType(MERGE_IF_NECESSARY, r1.getChangeId());
+    assertSubmitType(FAST_FORWARD_ONLY, r2.getChangeId());
+    assertSubmitType(MERGE_IF_NECESSARY, r3.getChangeId());
+    assertSubmitType(REBASE_IF_NECESSARY, r4.getChangeId());
+    assertSubmitType(MERGE_ALWAYS, r5.getChangeId());
+    assertSubmitType(CHERRY_PICK, r6.getChangeId());
+  }
+
+  @Test
+  public void submitTypeIsUsedForSubmit() throws Exception {
+    setRulesPl(SUBMIT_TYPE_FROM_SUBJECT);
+
+    PushOneCommit.Result r = createChange("master", "CHERRY_PICK 1");
+
+    gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+    gApi.changes().id(r.getChangeId()).current().submit();
+
+    List<RevCommit> log = log("master", 1);
+    assertThat(log.get(0).getShortMessage()).isEqualTo("CHERRY_PICK 1");
+    assertThat(log.get(0).name()).isNotEqualTo(r.getCommit().name());
+    assertThat(log.get(0).getFullMessage())
+        .contains("Change-Id: " + r.getChangeId());
+    assertThat(log.get(0).getFullMessage()).contains("Reviewed-on: ");
+  }
+
+  private List<RevCommit> log(String commitish, int n) throws Exception {
+    try (Repository repo = repoManager.openRepository(project);
+        Git git = new Git(repo)) {
+      ObjectId id = repo.resolve(commitish);
+      assertThat(id).isNotNull();
+      return ImmutableList.copyOf(git.log().add(id).setMaxCount(n).call());
+    }
+  }
+
+  private void assertSubmitType(SubmitType expected, String id)
+      throws Exception {
+    assertThat(gApi.changes().id(id).current().submitType())
+        .isEqualTo(expected);
+  }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
index 44c2ba4..6ff7889 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
@@ -14,10 +14,12 @@
 
 package com.google.gerrit.extensions.api.changes;
 
+import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.common.ActionInfo;
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.common.FileInfo;
 import com.google.gerrit.extensions.common.MergeableInfo;
+import com.google.gerrit.extensions.common.TestSubmitRuleInput;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.NotImplementedException;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -28,6 +30,7 @@
 
 public interface RevisionApi {
   void delete() throws RestApiException;
+
   void review(ReviewInput in) throws RestApiException;
 
   void submit() throws RestApiException;
@@ -65,6 +68,9 @@
 
   Map<String, ActionInfo> actions() throws RestApiException;
 
+  SubmitType submitType() throws RestApiException;
+  SubmitType testSubmitType(TestSubmitRuleInput in) throws RestApiException;
+
   /**
    * A default implementation which allows source compatibility
    * when adding new methods to the interface.
@@ -194,5 +200,16 @@
     public Map<String, ActionInfo> actions() throws RestApiException {
       throw new NotImplementedException();
     }
+
+    @Override
+    public SubmitType submitType() throws RestApiException {
+      throw new NotImplementedException();
+    }
+
+    @Override
+    public SubmitType testSubmitType(TestSubmitRuleInput in)
+        throws RestApiException {
+      throw new NotImplementedException();
+    }
   }
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/TestSubmitRuleInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/TestSubmitRuleInput.java
new file mode 100644
index 0000000..96a1626
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/TestSubmitRuleInput.java
@@ -0,0 +1,27 @@
+// Copyright (C) 2015 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.google.gerrit.extensions.common;
+
+import com.google.gerrit.extensions.restapi.DefaultInput;
+
+public class TestSubmitRuleInput {
+  public enum Filters {
+    RUN, SKIP
+  }
+
+  @DefaultInput
+  public String rule;
+  public Filters filters;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
index 0ca43f5..eb16ffb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
@@ -27,10 +27,12 @@
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.RevisionApi;
 import com.google.gerrit.extensions.api.changes.SubmitInput;
+import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.common.ActionInfo;
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.common.FileInfo;
 import com.google.gerrit.extensions.common.MergeableInfo;
+import com.google.gerrit.extensions.common.TestSubmitRuleInput;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -54,6 +56,7 @@
 import com.google.gerrit.server.change.Reviewed;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.change.Submit;
+import com.google.gerrit.server.change.TestSubmitType;
 import com.google.gerrit.server.git.UpdateException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -94,6 +97,8 @@
   private final Comments comments;
   private final CommentApiImpl.Factory commentFactory;
   private final GetRevisionActions revisionActions;
+  private final Provider<TestSubmitType> testSubmitType;
+  private final TestSubmitType.Get getSubmitType;
 
   @Inject
   RevisionApiImpl(Changes changes,
@@ -119,6 +124,8 @@
       Comments comments,
       CommentApiImpl.Factory commentFactory,
       GetRevisionActions revisionActions,
+      Provider<TestSubmitType> testSubmitType,
+      TestSubmitType.Get getSubmitType,
       @Assisted RevisionResource r) {
     this.changes = changes;
     this.cherryPick = cherryPick;
@@ -143,6 +150,8 @@
     this.comments = comments;
     this.commentFactory = commentFactory;
     this.revisionActions = revisionActions;
+    this.testSubmitType = testSubmitType;
+    this.getSubmitType = getSubmitType;
     this.revision = r;
   }
 
@@ -375,4 +384,23 @@
   public Map<String, ActionInfo> actions() throws RestApiException {
     return revisionActions.apply(revision).value();
   }
+
+  @Override
+  public SubmitType submitType() throws RestApiException {
+    try {
+      return getSubmitType.apply(revision);
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot get submit type", e);
+    }
+  }
+
+  @Override
+  public SubmitType testSubmitType(TestSubmitRuleInput in)
+      throws RestApiException {
+    try {
+      return testSubmitType.get().apply(revision, in);
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot test submit type", e);
+    }
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
index 95a701e..3eecea3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
@@ -19,13 +19,13 @@
 import com.google.common.collect.Maps;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.TestSubmitRuleInput;
+import com.google.gerrit.extensions.common.TestSubmitRuleInput.Filters;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.DefaultInput;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.rules.RulesCache;
 import com.google.gerrit.server.account.AccountLoader;
-import com.google.gerrit.server.change.TestSubmitRule.Input;
 import com.google.gerrit.server.project.SubmitRuleEvaluator;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
@@ -37,17 +37,8 @@
 import java.util.List;
 import java.util.Map;
 
-public class TestSubmitRule implements RestModifyView<RevisionResource, Input> {
-  public enum Filters {
-    RUN, SKIP
-  }
-
-  public static class Input {
-    @DefaultInput
-    public String rule;
-    public Filters filters;
-  }
-
+public class TestSubmitRule
+    implements RestModifyView<RevisionResource, TestSubmitRuleInput> {
   private final Provider<ReviewDb> db;
   private final ChangeData.Factory changeDataFactory;
   private final RulesCache rules;
@@ -68,10 +59,10 @@
   }
 
   @Override
-  public List<Record> apply(RevisionResource rsrc, Input input)
+  public List<Record> apply(RevisionResource rsrc, TestSubmitRuleInput input)
       throws AuthException, OrmException {
     if (input == null) {
-      input = new Input();
+      input = new TestSubmitRuleInput();
     }
     if (input.rule != null && !rules.isProjectRulesEnabled()) {
       throw new AuthException("project rules are disabled");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java
index f6016b5..4855012 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java
@@ -17,14 +17,14 @@
 import com.google.common.base.MoreObjects;
 import com.google.gerrit.common.data.SubmitTypeRecord;
 import com.google.gerrit.extensions.client.SubmitType;
+import com.google.gerrit.extensions.common.TestSubmitRuleInput;
+import com.google.gerrit.extensions.common.TestSubmitRuleInput.Filters;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.rules.RulesCache;
-import com.google.gerrit.server.change.TestSubmitRule.Filters;
-import com.google.gerrit.server.change.TestSubmitRule.Input;
 import com.google.gerrit.server.project.SubmitRuleEvaluator;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
@@ -33,7 +33,8 @@
 
 import org.kohsuke.args4j.Option;
 
-public class TestSubmitType implements RestModifyView<RevisionResource, Input> {
+public class TestSubmitType
+    implements RestModifyView<RevisionResource, TestSubmitRuleInput> {
   private final Provider<ReviewDb> db;
   private final ChangeData.Factory changeDataFactory;
   private final RulesCache rules;
@@ -51,10 +52,10 @@
   }
 
   @Override
-  public SubmitType apply(RevisionResource rsrc, Input input)
+  public SubmitType apply(RevisionResource rsrc, TestSubmitRuleInput input)
       throws AuthException, BadRequestException, OrmException {
     if (input == null) {
-      input = new Input();
+      input = new TestSubmitRuleInput();
     }
     if (input.rule != null && !rules.isProjectRulesEnabled()) {
       throw new AuthException("project rules are disabled");
@@ -71,13 +72,13 @@
     if (rec.status != SubmitTypeRecord.Status.OK) {
       throw new BadRequestException(String.format(
           "rule %s produced invalid result: %s",
-          evaluator.getSubmitRule(), rec));
+          evaluator.getSubmitRuleName(), rec));
     }
 
     return rec.type;
   }
 
-  static class Get implements RestReadView<RevisionResource> {
+  public static class Get implements RestReadView<RevisionResource> {
     private final TestSubmitType test;
 
     @Inject
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
index 2b35cef..e827d3b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
@@ -231,7 +231,8 @@
       // required for this change to be submittable. Each label will indicate
       // whether or not that is actually possible given the permissions.
       return ruleError(String.format("Submit rule '%s' for change %s of %s has "
-            + "no solution.", getSubmitRule(), cd.getId(), getProjectName()));
+            + "no solution.", getSubmitRuleName(), cd.getId(),
+            getProjectName()));
     }
 
     return resultsToSubmitRecord(getSubmitRule(), results);
@@ -410,13 +411,13 @@
 
     if (results.isEmpty()) {
       // Should never occur for a well written rule
-      return typeError("Submit rule '" + getSubmitRule() + "' for change "
+      return typeError("Submit rule '" + getSubmitRuleName() + "' for change "
           + cd.getId() + " of " + getProjectName() + " has no solution.");
     }
 
     Term typeTerm = results.get(0);
     if (!(typeTerm instanceof SymbolTerm)) {
-      return typeError("Submit rule '" + getSubmitRule() + "' for change "
+      return typeError("Submit rule '" + getSubmitRuleName() + "' for change "
           + cd.getId() + " of " + getProjectName()
           + " did not return a symbol.");
     }
@@ -609,6 +610,10 @@
     return submitRule;
   }
 
+  public String getSubmitRuleName() {
+    return submitRule != null ? submitRule.toString() : "<unknown rule>";
+  }
+
   private void initPatchSet() throws OrmException {
     if (patchSet == null) {
       patchSet = cd.currentPatchSet();
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
index 5eda57c..301bc0e 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.sshd.commands;
 
+import com.google.gerrit.extensions.common.TestSubmitRuleInput;
+import com.google.gerrit.extensions.common.TestSubmitRuleInput.Filters;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
@@ -21,8 +23,6 @@
 import com.google.gerrit.server.change.ChangesCollection;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.change.Revisions;
-import com.google.gerrit.server.change.TestSubmitRule.Filters;
-import com.google.gerrit.server.change.TestSubmitRule.Input;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.inject.Inject;
 
@@ -34,7 +34,7 @@
 import java.nio.ByteBuffer;
 
 abstract class BaseTestPrologCommand extends SshCommand {
-  private Input input = new Input();
+  private TestSubmitRuleInput input = new TestSubmitRuleInput();
 
   @Inject
   private ChangesCollection changes;
@@ -55,7 +55,7 @@
     input.filters = no ? Filters.SKIP : Filters.RUN;
   }
 
-  protected abstract RestModifyView<RevisionResource, Input> createView();
+  protected abstract RestModifyView<RevisionResource, TestSubmitRuleInput> createView();
 
   @Override
   protected final void run() throws UnloggedFailure {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRuleCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRuleCommand.java
index b957a7a..9c4a680 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRuleCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitRuleCommand.java
@@ -14,10 +14,10 @@
 
 package com.google.gerrit.sshd.commands;
 
+import com.google.gerrit.extensions.common.TestSubmitRuleInput;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.change.TestSubmitRule;
-import com.google.gerrit.server.change.TestSubmitRule.Input;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -29,7 +29,7 @@
   private Provider<TestSubmitRule> view;
 
   @Override
-  protected RestModifyView<RevisionResource, Input> createView() {
+  protected RestModifyView<RevisionResource, TestSubmitRuleInput> createView() {
     return view.get();
   }
 }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitTypeCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitTypeCommand.java
index 2e7f0df..34d4bdc 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitTypeCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/TestSubmitTypeCommand.java
@@ -15,9 +15,9 @@
 
 package com.google.gerrit.sshd.commands;
 
+import com.google.gerrit.extensions.common.TestSubmitRuleInput;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.change.RevisionResource;
-import com.google.gerrit.server.change.TestSubmitRule.Input;
 import com.google.gerrit.server.change.TestSubmitType;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.inject.Inject;
@@ -29,7 +29,7 @@
   private Provider<TestSubmitType> view;
 
   @Override
-  protected RestModifyView<RevisionResource, Input> createView() {
+  protected RestModifyView<RevisionResource, TestSubmitRuleInput> createView() {
     return view.get();
   }
 }