Add @UseSsh Annotation and GERRIT_USE_SSH flag

Adding a @UseSsh annotation and a command line flag lets us decide if we
want to run tests that require SSH on each test run. In this way, we can
run the test suite against a Gerrit instance that does not support SSH
connections.

Change-Id: Ibb471f312c50f0c92e1c32e55f4a5667b33b6ab5
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index 82d3d0a..b33901f 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -217,6 +217,12 @@
   bazel test --test_env=GERRIT_NOTEDB=READ_WRITE //...
 ----
 
+To run only tests that do not use SSH:
+
+----
+  bazel test --test_env=GERRIT_USE_SSH=NO //...
+----
+
 == Dependencies
 
 Dependency JARs are normally downloaded as needed, but you can
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt
index 5dfb367..a74f837 100644
--- a/Documentation/dev-buck.txt
+++ b/Documentation/dev-buck.txt
@@ -347,6 +347,12 @@
   GERRIT_NOTEDB=READ_WRITE buck test
 ----
 
+To run only tests that do not use SSH:
+
+----
+  GERRIT_USE_SSH=NO buck test
+----
+
 == Dependencies
 
 Dependency JARs are normally downloaded automatically, but Buck can inspect
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 3dbc10f..d1f0b55 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.acceptance;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.acceptance.GitUtil.initSsh;
 import static com.google.gerrit.extensions.api.changes.SubmittedTogetherOption.NON_VISIBLE_CHANGES;
 import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
@@ -95,6 +96,7 @@
 import com.google.gerrit.testutil.ConfigSuite;
 import com.google.gerrit.testutil.FakeEmailSender;
 import com.google.gerrit.testutil.FakeEmailSender.Message;
+import com.google.gerrit.testutil.SshMode;
 import com.google.gerrit.testutil.TempFileUtil;
 import com.google.gerrit.testutil.TestNotesMigration;
 import com.google.gson.Gson;
@@ -274,6 +276,7 @@
 
   private String resourcePrefix;
   private List<Repository> toClose;
+  private boolean useSsh;
 
   @Rule
   public TestRule testRunner = new TestRule() {
@@ -306,6 +309,16 @@
     eventRecorder = eventRecorderFactory.create(admin);
   }
 
+  @Before
+  public void assumeSshIfRequired() {
+    if (useSsh) {
+      // If the test uses ssh, we use assume() to make sure ssh is enabled on
+      // the test suite. JUnit will skip tests annotated with @UseSsh if we
+      // disable them using the command line flag.
+      assume().that(SshMode.useSsh()).isTrue();
+    }
+  }
+
   @After
   public void closeEventRecorder() {
     eventRecorder.close();
@@ -379,20 +392,34 @@
 
     adminRestSession = new RestSession(server, admin);
     userRestSession = new RestSession(server, user);
-    initSsh(admin);
+
     db = reviewDbProvider.open();
-    Context ctx = newRequestContext(user);
-    atrScope.set(ctx);
-    userSshSession = ctx.getSession();
-    userSshSession.open();
-    ctx = newRequestContext(admin);
-    atrScope.set(ctx);
-    adminSshSession = ctx.getSession();
-    adminSshSession.open();
+
+    if (classDesc.useSsh() || methodDesc.useSsh()) {
+      useSsh = true;
+      if (SshMode.useSsh() && (adminSshSession == null ||
+          userSshSession == null)) {
+        // Create Ssh sessions
+        initSsh(admin);
+        Context ctx = newRequestContext(user);
+        atrScope.set(ctx);
+        userSshSession = ctx.getSession();
+        userSshSession.open();
+        ctx = newRequestContext(admin);
+        atrScope.set(ctx);
+        adminSshSession = ctx.getSession();
+        adminSshSession.open();
+      }
+    } else {
+      useSsh = false;
+    }
+
     resourcePrefix = UNSAFE_PROJECT_NAME.matcher(
         description.getClassName() + "_"
         + description.getMethodName() + "_").replaceAll("");
 
+    Context ctx = newRequestContext(admin);
+    atrScope.set(ctx);
     project = createProject(projectInput(description));
     testRepo = cloneProject(project, getCloneAsAccount(description));
   }
@@ -517,8 +544,12 @@
       repo.close();
     }
     db.close();
-    adminSshSession.close();
-    userSshSession.close();
+    if (adminSshSession != null) {
+      adminSshSession.close();
+    }
+    if (userSshSession != null) {
+      userSshSession.close();
+    }
     if (server != commonServer) {
       server.stop();
     }
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
index c29e8fe..7b73505 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -65,6 +65,7 @@
           true, // @UseLocalDisk is only valid on methods.
           !has(NoHttpd.class, testDesc.getTestClass()),
           has(Sandboxed.class, testDesc.getTestClass()),
+          has(UseSsh.class, testDesc.getTestClass()),
           null, // @GerritConfig is only valid on methods.
           null); // @GerritConfigs is only valid on methods.
 
@@ -79,6 +80,8 @@
             && !has(NoHttpd.class, testDesc.getTestClass()),
           testDesc.getAnnotation(Sandboxed.class) != null ||
               has(Sandboxed.class, testDesc.getTestClass()),
+          testDesc.getAnnotation(UseSsh.class) != null ||
+              has(UseSsh.class, testDesc.getTestClass()),
           testDesc.getAnnotation(GerritConfig.class),
           testDesc.getAnnotation(GerritConfigs.class));
     }
@@ -97,6 +100,7 @@
     abstract boolean memory();
     abstract boolean httpd();
     abstract boolean sandboxed();
+    abstract boolean useSsh();
     @Nullable abstract GerritConfig config();
     @Nullable abstract GerritConfigs configs();
 
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/LightweightPluginDaemonTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/LightweightPluginDaemonTest.java
index 3a870cb..9c86391 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/LightweightPluginDaemonTest.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/LightweightPluginDaemonTest.java
@@ -49,8 +49,14 @@
 
   @After
   public void tearDown() {
-    plugin.stop(env);
-    env.onStopPlugin(plugin);
+    if (plugin != null) {
+      // plugin will be null if the plugin test requires ssh, but the command
+      // line flag says we are running tests without ssh as the assume()
+      // statement in AbstractDaemonTest will prevent the execution of setUp()
+      // in this class
+      plugin.stop(env);
+      env.onStopPlugin(plugin);
+    }
   }
 
   private static TestPlugin getTestPlugin(Class<?> clazz) {
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/UseSsh.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/UseSsh.java
new file mode 100644
index 0000000..b5ca4b2
--- /dev/null
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/UseSsh.java
@@ -0,0 +1,27 @@
+// Copyright (C) 2016 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;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Target({TYPE, METHOD})
+@Retention(RUNTIME)
+public @interface UseSsh {
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index b9736fd..8979b9f 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -80,13 +80,13 @@
 import java.util.Map;
 import java.util.Set;
 
+
 public abstract class AbstractPushForReview extends AbstractDaemonTest {
   protected enum Protocol {
     // TODO(dborowitz): TEST.
     SSH, HTTP
   }
 
-  private String sshUrl;
   private LabelType patchSetLock;
 
   @BeforeClass
@@ -101,7 +101,6 @@
 
   @Before
   public void setUp() throws Exception {
-    sshUrl = adminSshSession.getUrl();
     ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
     patchSetLock = Util.patchSetLock();
     cfg.getLabelSections().put(patchSetLock.getName(), patchSetLock);
@@ -117,7 +116,7 @@
     String url;
     switch (p) {
       case SSH:
-        url = sshUrl;
+        url = adminSshSession.getUrl();
         break;
       case HTTP:
         url = admin.getHttpUrl(server);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SshPushForReviewIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SshPushForReviewIT.java
index c7da993..ecb0be4 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SshPushForReviewIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SshPushForReviewIT.java
@@ -16,9 +16,11 @@
 
 import com.google.gerrit.acceptance.NoHttpd;
 
+import com.google.gerrit.acceptance.UseSsh;
 import org.junit.Before;
 
 @NoHttpd
+@UseSsh
 public class SshPushForReviewIT extends AbstractPushForReview {
   @Before
   public void selectSshUrl() throws Exception {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
index cac293c..63f4919 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.GerritConfig;
 import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.UseSsh;
 import com.google.gerrit.extensions.client.AccountFieldName;
 import com.google.gerrit.extensions.client.AuthType;
 import com.google.gerrit.extensions.common.ServerInfo;
@@ -129,6 +130,7 @@
   }
 
   @Test
+  @UseSsh
   @GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
   public void serverConfigWithPlugin() throws Exception {
     Path plugins = tempSiteDir.newFolder("plugins").toPath();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/AbandonRestoreIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/AbandonRestoreIT.java
index 56a56ee..09e03b6a 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/AbandonRestoreIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/AbandonRestoreIT.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.PushOneCommit.Result;
+import com.google.gerrit.acceptance.UseSsh;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.common.ChangeMessageInfo;
 
@@ -31,6 +32,7 @@
 import java.util.Locale;
 
 @NoHttpd
+@UseSsh
 public class AbandonRestoreIT extends AbstractDaemonTest {
 
   @Test
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java
index 025fcfa..6ea5976 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/BanCommitIT.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.NoHttpd;
 
+import com.google.gerrit.acceptance.UseSsh;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.junit.Test;
@@ -29,6 +30,7 @@
 import java.util.Locale;
 
 @NoHttpd
+@UseSsh
 public class BanCommitIT extends AbstractDaemonTest {
 
   @Test
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/CreateProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/CreateProjectIT.java
index 85d460e..a45cb0e 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/CreateProjectIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/CreateProjectIT.java
@@ -18,11 +18,13 @@
 import static com.google.common.truth.Truth.assert_;
 
 import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.UseSsh;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.project.ProjectState;
 
 import org.junit.Test;
 
+@UseSsh
 public class CreateProjectIT extends AbstractDaemonTest {
 
   @Test
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
index 7864cf6..7f003bf 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.acceptance.GcAssert;
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.UseSsh;
 import com.google.gerrit.common.data.GarbageCollectionResult;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.git.GarbageCollection;
@@ -34,6 +35,7 @@
 import java.util.Locale;
 
 @NoHttpd
+@UseSsh
 public class GarbageCollectionIT extends AbstractDaemonTest {
 
   @Inject
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/QueryIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/QueryIT.java
index 5c8d166..443a1fb 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/QueryIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/QueryIT.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.SshSession;
+import com.google.gerrit.acceptance.UseSsh;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.client.Side;
@@ -36,6 +37,7 @@
 import java.util.List;
 
 @NoHttpd
+@UseSsh
 public class QueryIT extends AbstractDaemonTest {
 
   private static Gson gson = new Gson();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java
index 32a0175..2f0dcd3 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.acceptance.GerritConfig;
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.UseSsh;
 import com.google.gerrit.testutil.NoteDbMode;
 
 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
@@ -40,6 +41,7 @@
 import java.util.TreeSet;
 
 @NoHttpd
+@UseSsh
 public class UploadArchiveIT extends AbstractDaemonTest {
 
   @Before
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/SshMode.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/SshMode.java
new file mode 100644
index 0000000..9b23ead
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/SshMode.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2016 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.testutil;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Enums;
+import com.google.common.base.Strings;
+
+public enum SshMode {
+  /** Tests annotated with UseSsh will be disabled. */
+  NO,
+
+  /** Tests annotated with UseSsh will be enabled. */
+  YES;
+
+  private static final String VAR = "GERRIT_USE_SSH";
+
+  public static SshMode get() {
+    String value = System.getenv(VAR);
+    if (Strings.isNullOrEmpty(value)) {
+      return YES;
+    }
+    value = value.toUpperCase();
+    SshMode mode = Enums.getIfPresent(SshMode.class, value).orNull();
+    checkArgument(mode != null,
+        "Invalid value for %s: %s", VAR, System.getenv(VAR));
+    return mode;
+  }
+
+  public static boolean useSsh() {
+    return get() == YES;
+  }
+}