StandaloneSiteTest: Ignore user and system git config

Override SystemReader to return empty files for the user and system
config. This makes StandaloneNoteDbMigrationIT pass regardless of the
configuration of the system it's running on.

This has to happen in StandaloneSiteTest prior to site initialization,
since the SystemReader is queried at FileRepository creation time, and
Gerrit by design holds all created repos open in the RepositoryCache. If
we tried to override the SystemReader in a test, it would have no
effect, because the cached repos would already have the wrong config
files loaded.

Another complication is that GerritLauncher#mainImpl was creating a new
classloader for every invocation, meaning the SystemReader class used by
the wrapper code in StandaloneSiteTest was not the same class instance
seen by the tests. Fix this by explicitly passing a ClassLoader when
running.

Change-Id: I478f3f712bced530c87b8fac3422275ec93d3557
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/StandaloneSiteTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
index ce80cdd..a218f73 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
@@ -35,9 +35,13 @@
 import com.google.inject.Injector;
 import com.google.inject.Module;
 import com.google.inject.Provider;
+import java.io.File;
 import java.util.Arrays;
 import java.util.Collections;
 import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.SystemReader;
 import org.junit.Rule;
 import org.junit.rules.RuleChain;
 import org.junit.rules.TemporaryFolder;
@@ -109,8 +113,12 @@
           return new Statement() {
             @Override
             public void evaluate() throws Throwable {
-              beforeTest(description);
-              base.evaluate();
+              try {
+                beforeTest(description);
+                base.evaluate();
+              } finally {
+                afterTest();
+              }
             }
           };
         }
@@ -122,13 +130,65 @@
   protected Account.Id adminId;
 
   private GerritServer.Description serverDesc;
+  private SystemReader oldSystemReader;
 
   private void beforeTest(Description description) throws Exception {
+    // SystemReader must be overridden before creating any repos, since they read the user/system
+    // configs at initialization time, and are then stored in the RepositoryCache forever.
+    oldSystemReader = setFakeSystemReader(tempSiteDir.getRoot());
+
     serverDesc = GerritServer.Description.forTestMethod(description, configName);
     sitePaths = new SitePaths(tempSiteDir.getRoot().toPath());
     GerritServer.init(serverDesc, baseConfig, sitePaths.site_path);
   }
 
+  private static SystemReader setFakeSystemReader(File tempDir) {
+    SystemReader oldSystemReader = SystemReader.getInstance();
+    SystemReader.setInstance(
+        new SystemReader() {
+          @Override
+          public String getHostname() {
+            return oldSystemReader.getHostname();
+          }
+
+          @Override
+          public String getenv(String variable) {
+            return oldSystemReader.getenv(variable);
+          }
+
+          @Override
+          public String getProperty(String key) {
+            return oldSystemReader.getProperty(key);
+          }
+
+          @Override
+          public FileBasedConfig openUserConfig(Config parent, FS fs) {
+            return new FileBasedConfig(parent, new File(tempDir, "user.config"), FS.detect());
+          }
+
+          @Override
+          public FileBasedConfig openSystemConfig(Config parent, FS fs) {
+            return new FileBasedConfig(parent, new File(tempDir, "system.config"), FS.detect());
+          }
+
+          @Override
+          public long getCurrentTime() {
+            return oldSystemReader.getCurrentTime();
+          }
+
+          @Override
+          public int getTimezone(long when) {
+            return oldSystemReader.getTimezone(when);
+          }
+        });
+    return oldSystemReader;
+  }
+
+  private void afterTest() throws Exception {
+    SystemReader.setInstance(oldSystemReader);
+    oldSystemReader = null;
+  }
+
   protected ServerContext startServer() throws Exception {
     return startServer(null);
   }
@@ -153,7 +213,10 @@
   }
 
   protected static void runGerrit(String... args) throws Exception {
-    assertThat(GerritLauncher.mainImpl(args))
+    // Use invokeProgram with the current classloader, rather than mainImpl, which would create a
+    // new classloader. This is necessary so that static state, particularly the SystemReader, is
+    // shared with the test method.
+    assertThat(GerritLauncher.invokeProgram(StandaloneSiteTest.class.getClassLoader(), args))
         .named("gerrit.war " + Arrays.stream(args).collect(joining(" ")))
         .isEqualTo(0);
   }
diff --git a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
index 072d1ed..a592b7e 100644
--- a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
+++ b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -63,6 +63,17 @@
     System.exit(mainImpl(argv));
   }
 
+  /**
+   * Invokes a proram.
+   *
+   * <p>Creates a new classloader to load and run the program class. To reuse a classloader across
+   * calls (e.g. from tests), use {@link #invokeProgram(ClassLoader, String[])}.
+   *
+   * @param argv arguments, as would be passed to {@code gerrit.war}. The first argument is the
+   *     program name.
+   * @return program return code.
+   * @throws Exception if any error occurs.
+   */
   public static int mainImpl(String[] argv) throws Exception {
     if (argv.length == 0) {
       File me;
@@ -163,7 +174,16 @@
     }
   }
 
-  private static int invokeProgram(ClassLoader loader, String[] origArgv) throws Exception {
+  /**
+   * Invokes a proram in the provided {@code ClassLoader}.
+   *
+   * @param loader classloader to load program class from.
+   * @param origArgv arguments, as would be passed to {@code gerrit.war}. The first argument is the
+   *     program name.
+   * @return program return code.
+   * @throws Exception if any error occurs.
+   */
+  public static int invokeProgram(ClassLoader loader, String[] origArgv) throws Exception {
     String name = origArgv[0];
     final String[] argv = new String[origArgv.length - 1];
     System.arraycopy(origArgv, 1, argv, 0, argv.length);