Run most acceptance tests in-memory

This allows the tests to run faster, with lower system overheads.
The new @UseLocalDisk annotation must be added to any test method
that wants the classical init and daemon testing strategy.

A new sshd.requestLog configuration variable is introduced to
permit tests to disable the SSH command log.

Timing for `buck test --all` on my Linux desktop:

  before 3m37.129s
  after  2m18.789s

Change-Id: I18c07ef2d913f6abbb3925b3e7d3df5d28668497
(cherry picked from commit 318bfcae5c)
This commit is contained in:
Shawn Pearce
2013-10-17 22:15:38 -07:00
parent f22e633fca
commit 35e97c2aeb
19 changed files with 362 additions and 98 deletions

View File

@@ -30,7 +30,8 @@ public abstract class AbstractDaemonTest {
return new Statement() {
@Override
public void evaluate() throws Throwable {
beforeTest(config(description));
boolean mem = description.getAnnotation(UseLocalDisk.class) == null;
beforeTest(config(description), mem);
base.evaluate();
afterTest();
}
@@ -53,8 +54,8 @@ public abstract class AbstractDaemonTest {
}
}
private void beforeTest(Config cfg) throws Exception {
server = GerritServer.start(cfg);
private void beforeTest(Config cfg, boolean memory) throws Exception {
server = GerritServer.start(cfg, memory);
server.getTestInjector().injectMembers(this);
}

View File

@@ -14,18 +14,19 @@
package com.google.gerrit.acceptance;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.pgm.Daemon;
import com.google.gerrit.pgm.Init;
import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.util.SocketUtil;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import java.io.File;
@@ -46,8 +47,7 @@ import java.util.concurrent.TimeUnit;
public class GerritServer {
/** Returns fully started Gerrit server */
static GerritServer start(Config base) throws Exception {
final File site = initSite(base);
static GerritServer start(Config base, boolean memory) throws Exception {
final CyclicBarrier serverStarted = new CyclicBarrier(2);
final Daemon daemon = new Daemon(new Runnable() {
public void run() {
@@ -61,21 +61,34 @@ public class GerritServer {
}
});
ExecutorService daemonService = Executors.newSingleThreadExecutor();
daemonService.submit(new Callable<Void>() {
public Void call() throws Exception {
int rc = daemon.main(new String[] {"-d", site.getPath(), "--headless" });
if (rc != 0) {
System.out.println("Failed to start Gerrit daemon. Check "
+ site.getPath() + "/logs/error_log");
serverStarted.reset();
}
return null;
};
});
serverStarted.await();
System.out.println("Gerrit Server Started");
final File site;
ExecutorService daemonService = null;
if (memory) {
site = null;
Config cfg = base != null ? base : new Config();
mergeTestConfig(cfg);
cfg.setBoolean("httpd", null, "requestLog", false);
cfg.setBoolean("sshd", null, "requestLog", false);
daemon.setDatabaseForTesting(ImmutableList.<Module>of(
new InMemoryTestingDatabaseModule(cfg)));
daemon.start();
} else {
site = initSite(base);
daemonService = Executors.newSingleThreadExecutor();
daemonService.submit(new Callable<Void>() {
public Void call() throws Exception {
int rc = daemon.main(new String[] {"-d", site.getPath(), "--headless" });
if (rc != 0) {
System.out.println("Failed to start Gerrit daemon. Check "
+ site.getPath() + "/logs/error_log");
serverStarted.reset();
}
return null;
};
});
serverStarted.await();
System.out.println("Gerrit Server Started");
}
Injector i = createTestInjector(daemon);
return new GerritServer(site, i, daemon, daemonService);
@@ -91,14 +104,22 @@ public class GerritServer {
throw new RuntimeException("Couldn't initialize site");
}
InetSocketAddress http = newPort();
InetSocketAddress sshd = newPort();
String url = "http://" + format(http) + "/";
MergeableFileBasedConfig cfg = new MergeableFileBasedConfig(
new File(new File(tmp, "etc"), "gerrit.config"),
FS.DETECTED);
cfg.load();
cfg.merge(base);
mergeTestConfig(cfg);
cfg.save();
return tmp;
}
private static void mergeTestConfig(Config cfg)
throws IOException {
InetSocketAddress http = newPort();
InetSocketAddress sshd = newPort();
String url = "http://" + format(http) + "/";
cfg.setString("gerrit", null, "canonicalWebUrl", url);
cfg.setString("httpd", null, "listenUrl", url);
cfg.setString("sshd", null, "listenAddress", format(sshd));
@@ -106,8 +127,6 @@ public class GerritServer {
cfg.setBoolean("sendemail", null, "enable", false);
cfg.setInt("cache", "projects", "checkFrequency", 0);
cfg.setInt("plugins", null, "checkFrequency", 0);
cfg.save();
return tmp;
}
private static String format(InetSocketAddress s) {
@@ -169,11 +188,8 @@ public class GerritServer {
this.daemon = daemon;
this.daemonService = daemonService;
FileBasedConfig cfg = new FileBasedConfig(
new File(new File(sitePath, "etc"), "gerrit.config"),
FS.DETECTED);
cfg.load();
Config cfg = testInjector.getInstance(
Key.get(Config.class, GerritServerConfig.class));
url = cfg.getString("gerrit", null, "canonicalWebUrl");
URI uri = URI.create(url);
@@ -200,12 +216,15 @@ public class GerritServer {
}
void stop() throws Exception {
LifecycleManager manager = get(daemon, "manager");
System.out.println("Gerrit Server Shutdown");
manager.stop();
daemonService.shutdownNow();
daemonService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
TempFileUtil.recursivelyDelete(sitePath);
daemon.getLifecycleManager().stop();
if (daemonService != null) {
System.out.println("Gerrit Server Shutdown");
daemonService.shutdownNow();
daemonService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
if (sitePath != null) {
TempFileUtil.recursivelyDelete(sitePath);
}
RepositoryCache.clear();
}
}

View File

@@ -0,0 +1,129 @@
// Copyright (C) 2013 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 com.google.inject.Scopes.SINGLETON;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.config.TrackingFootersProvider;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.schema.DataSourceType;
import com.google.gerrit.server.schema.SchemaModule;
import com.google.gerrit.server.schema.SchemaVersion;
import com.google.gerrit.testutil.InMemoryDatabase;
import com.google.gerrit.testutil.InMemoryH2Type;
import com.google.gerrit.testutil.InMemoryRepositoryManager;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.OrmRuntimeException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import org.apache.sshd.common.KeyPairProvider;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.eclipse.jgit.lib.Config;
import java.io.File;
class InMemoryTestingDatabaseModule extends AbstractModule {
private final Config cfg;
InMemoryTestingDatabaseModule(Config cfg) {
this.cfg = cfg;
}
@Override
protected void configure() {
bind(Config.class)
.annotatedWith(GerritServerConfig.class)
.toInstance(cfg);
bind(File.class)
.annotatedWith(SitePath.class)
.toInstance(new File("UNIT_TEST_GERRIT_SITE"));
bind(GitRepositoryManager.class)
.toInstance(new InMemoryRepositoryManager());
bind(DataSourceType.class).to(InMemoryH2Type.class);
bind(InMemoryDatabase.class).in(SINGLETON);
bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {})
.to(InMemoryDatabase.class);
install(new LifecycleModule() {
@Override
protected void configure() {
listener().to(CreateDatabase.class);
}
});
bind(SitePaths.class);
bind(TrackingFooters.class)
.toProvider(TrackingFootersProvider.class)
.in(SINGLETON);
install(new SchemaModule());
bind(SchemaVersion.class).to(SchemaVersion.C);
}
@Provides
@Singleton
KeyPairProvider createHostKey() {
return getHostKeys();
}
private static SimpleGeneratorHostKeyProvider keys;
private static synchronized KeyPairProvider getHostKeys() {
if (keys == null) {
keys = new SimpleGeneratorHostKeyProvider();
keys.setAlgorithm("RSA");
keys.loadKeys();
}
return keys;
}
static class CreateDatabase implements LifecycleListener {
private final InMemoryDatabase mem;
@Inject
CreateDatabase(InMemoryDatabase mem) {
this.mem = mem;
}
@Override
public void start() {
try {
mem.create();
} catch (OrmException e) {
throw new OrmRuntimeException(e);
}
}
@Override
public void stop() {
mem.drop();
}
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (C) 2013 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.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target({METHOD})
@Retention(RUNTIME)
public @interface UseLocalDisk {
}

View File

@@ -24,6 +24,7 @@ import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.RestSession;
import com.google.gerrit.acceptance.SshSession;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.UseLocalDisk;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gwtorm.server.OrmException;
@@ -83,8 +84,8 @@ public class GarbageCollectionIT extends AbstractDaemonTest {
}
@Test
@UseLocalDisk
public void testGcOneProject() throws JSchException, IOException {
assertEquals(HttpStatus.SC_OK, POST("/projects/" + allProjects.get() + "/gc"));
gcAssert.assertHasPackFile(allProjects);
gcAssert.assertHasNoPackFile(project1, project2);

View File

@@ -24,6 +24,7 @@ import com.google.gerrit.acceptance.AccountCreator;
import com.google.gerrit.acceptance.GcAssert;
import com.google.gerrit.acceptance.SshSession;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.UseLocalDisk;
import com.google.gerrit.common.data.GarbageCollectionResult;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllProjectsName;
@@ -83,6 +84,7 @@ public class GarbageCollectionIT extends AbstractDaemonTest {
}
@Test
@UseLocalDisk
public void testGc() throws JSchException, IOException {
String response =
sshSession.exec("gerrit gc \"" + project1.get() + "\" \""
@@ -94,6 +96,7 @@ public class GarbageCollectionIT extends AbstractDaemonTest {
}
@Test
@UseLocalDisk
public void testGcAll() throws JSchException, IOException {
String response = sshSession.exec("gerrit gc --all");
assertFalse(sshSession.hasError());
@@ -110,6 +113,7 @@ public class GarbageCollectionIT extends AbstractDaemonTest {
}
@Test
@UseLocalDisk
public void testGcAlreadyScheduled() {
gcQueue.addAll(Arrays.asList(project1));
GarbageCollectionResult result = garbageCollectionFactory.create().run(