Add acceptance (IT) tests support for plugins

Create PluginDaemonTest to load plugin jars in the Gerrit test server
before running the plugin acceptance tests. Plugin IT classes shall
extend PluginDaemonTest in order to enable this functionality.

Make beforeTest in AbstractDaemonTest protected so it can be overridden
by hereby PluginDaemonTest.

Add UNIT_TEST_GERRIT_SITE constant to InMemoryTestingDatabaseModule so
it can be reused by PluginDaemonTest.

Package acceptance tests into plugin-api jar.

Support standalone and non-standalone (all) buck modes for tested
plugin. Running IT tests using a maven pom is *not* supported by this
change.

Support ability for such plugin IT tests to set plugin config string in
gerrit config.

Change-Id: Ie9e63de622708272f4f5e154a80a55a97d8ff77c
This commit is contained in:
Hector Oswaldo Caballero 2015-06-15 16:02:45 -04:00 committed by Marco Miller
parent 3420d83f39
commit b17cd6377a
5 changed files with 241 additions and 2 deletions

View File

@ -44,6 +44,7 @@ java_library(
'//lib/mina:sshd',
],
visibility = [
'//gerrit-plugin-api/...',
'//tools/eclipse:classpath',
'//gerrit-acceptance-tests/...',
],

View File

@ -222,7 +222,7 @@ public abstract class AbstractDaemonTest {
return cfg.getBoolean("change", null, "submitWholeTopic", false);
}
private void beforeTest(Description description) throws Exception {
protected void beforeTest(Description description) throws Exception {
GerritServer.Description classDesc =
GerritServer.Description.forTestClass(description, configName);
GerritServer.Description methodDesc =

View File

@ -47,6 +47,9 @@ import java.nio.file.Path;
import java.nio.file.Paths;
class InMemoryTestingDatabaseModule extends LifecycleModule {
static final String UNIT_TEST_GERRIT_SITE = "UNIT_TEST_GERRIT_SITE";
private final Config cfg;
InMemoryTestingDatabaseModule(Config cfg) {
@ -62,7 +65,7 @@ class InMemoryTestingDatabaseModule extends LifecycleModule {
// TODO(dborowitz): Use jimfs.
bind(Path.class)
.annotatedWith(SitePath.class)
.toInstance(Paths.get("UNIT_TEST_GERRIT_SITE"));
.toInstance(Paths.get(UNIT_TEST_GERRIT_SITE));
bind(GitRepositoryManager.class)
.toInstance(new InMemoryRepositoryManager());

View File

@ -0,0 +1,234 @@
// 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;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.gerrit.server.config.SitePaths;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import org.junit.AfterClass;
import org.junit.runner.Description;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class PluginDaemonTest extends AbstractDaemonTest {
private static final String BUCKLC = "buck";
private static final String BUCKOUT = "buck-out";
private static final String BUILD = "build";
private static final String EMPTY = "src/main/java/ForceJarIfMissing.java";
private static final Path testSite =
Paths.get(InMemoryTestingDatabaseModule.UNIT_TEST_GERRIT_SITE);
private Path gen;
private Path pluginRoot;
private Path pluginsSitePath;
private Path pluginSubPath;
private Path pluginSource;
private String pluginName;
private boolean standalone;
@Override
protected void beforeTest(Description description) throws Exception {
locatePaths();
retrievePluginName();
buildPluginJar();
createTestSite();
copyJarToTestSite();
super.beforeTest(description);
}
@AfterClass
public static void removeTestSite() throws IOException {
if (!Files.exists(testSite)) {
return;
}
Files.walkFileTree(testSite, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Files.deleteIfExists(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException {
Files.deleteIfExists(dir);
return FileVisitResult.CONTINUE;
}
});
}
protected void setPluginConfigString(String name, String value)
throws IOException, ConfigInvalidException {
SitePaths sitePath = new SitePaths(testSite);
FileBasedConfig cfg = getGerritConfigFile(sitePath);
cfg.load();
cfg.setString("plugin", pluginName, name, value);
cfg.save();
}
private FileBasedConfig getGerritConfigFile(SitePaths sitePath)
throws IOException {
FileBasedConfig cfg =
new FileBasedConfig(sitePath.gerrit_config.toFile(), FS.DETECTED);
if (!cfg.getFile().exists()) {
Path etc_path = Files.createDirectories(sitePath.etc_dir);
Files.createFile(etc_path.resolve("gerrit.config"));
}
return cfg;
}
private void locatePaths() {
URL pluginClassesUrl =
getClass().getProtectionDomain().getCodeSource().getLocation();
Path basePath = Paths.get(pluginClassesUrl.getPath()).getParent();
int idx = 0;
int buckOutIdx = 0;
int pluginsIdx = 0;
for (Path subPath : basePath) {
if (subPath.endsWith("plugins")) {
pluginsIdx = idx;
}
if (subPath.endsWith(BUCKOUT)) {
buckOutIdx = idx;
}
idx++;
}
standalone = checkStandalone(basePath);
pluginRoot = basePath.getRoot().resolve(basePath.subpath(0, buckOutIdx));
gen = pluginRoot.resolve(BUCKOUT).resolve("gen");
if (standalone) {
pluginSource = pluginRoot;
} else {
pluginSubPath = basePath.subpath(pluginsIdx, pluginsIdx + 2);
pluginSource = pluginRoot.resolve(pluginSubPath);
}
}
private boolean checkStandalone(Path basePath) {
String pathCharStringOrNone = "[a-zA-Z0-9._-]*?";
Pattern pattern = Pattern.compile(pathCharStringOrNone + "gerrit" +
pathCharStringOrNone);
Path partialPath = basePath;
for (int i = basePath.getNameCount(); i > 0; i--) {
int count = partialPath.getNameCount();
if (count > 1) {
String gerritDirCandidate =
partialPath.subpath(count - 2, count - 1).toString();
if (pattern.matcher(gerritDirCandidate).matches()) {
if (partialPath.endsWith(gerritDirCandidate + "/" + BUCKOUT)) {
return false;
}
}
}
partialPath = partialPath.getParent();
}
return true;
}
private void retrievePluginName() throws IOException {
Path buckFile = pluginSource.resolve("BUCK");
byte[] bytes = Files.readAllBytes(buckFile);
String buckContent =
new String(bytes, StandardCharsets.UTF_8).replaceAll("\\s+", "");
Matcher matcher =
Pattern.compile("gerrit_plugin\\(name='(.*?)'").matcher(buckContent);
if (matcher.find()) {
pluginName = matcher.group(1);
}
if (Strings.isNullOrEmpty(pluginName)) {
if (standalone) {
pluginName = pluginRoot.getFileName().toString();
} else {
pluginName = pluginSubPath.getFileName().toString();
}
}
}
private void buildPluginJar() throws IOException, InterruptedException {
Properties properties = loadBuckProperties();
String buck =
MoreObjects.firstNonNull(properties.getProperty(BUCKLC), BUCKLC);
String target;
if (standalone) {
target = "//:" + pluginName;
} else {
target = pluginSubPath.toString();
}
ProcessBuilder processBuilder =
new ProcessBuilder(buck, BUILD, target).directory(pluginRoot.toFile())
.redirectErrorStream(true);
Path forceJar = pluginSource.resolve(EMPTY);
Files.createFile(forceJar);
try {
processBuilder.start().waitFor();
} finally {
Files.delete(forceJar);
// otherwise jar not made next time if missing again:
processBuilder.start().waitFor();
}
}
private Properties loadBuckProperties() throws IOException {
Properties properties = new Properties();
Path propertiesPath = gen.resolve("tools").resolve("buck.properties");
if (Files.exists(propertiesPath)) {
try (InputStream in = Files.newInputStream(propertiesPath)) {
properties.load(in);
}
}
return properties;
}
private void createTestSite() throws IOException {
SitePaths sitePath = new SitePaths(testSite);
pluginsSitePath = Files.createDirectories(sitePath.plugins_dir);
Files.createDirectories(sitePath.tmp_dir);
}
private void copyJarToTestSite() throws IOException {
Path pluginOut;
if (standalone) {
pluginOut = gen;
} else {
pluginOut = gen.resolve(pluginSubPath);
}
Path jar = pluginOut.resolve(pluginName + ".jar");
Path dest = pluginsSitePath.resolve(jar.getFileName());
Files.copy(jar, dest, StandardCopyOption.REPLACE_EXISTING);
}
}

View File

@ -20,6 +20,7 @@ java_binary(
java_library(
name = 'lib',
exported_deps = PLUGIN_API + [
'//gerrit-acceptance-tests:lib',
'//gerrit-antlr:query_exception',
'//gerrit-antlr:query_parser',
'//gerrit-common:annotations',