Expose acceptance test framework as new plugin artifact
Split the current gerrit-acceptance-tests in two parts:
* framework + some needed deps, that is exposed as additional plugin
artifact
* rest of the gerrit-acceptance-test project
To implement the split and not to pull in too many dependencies, some
refactoring was needed. Particularly, gerrit-server:testutil depends
on gerrit-server:server, that depends on almost everything. Similar
problem was with gerrit-pgm:pgm, that is needed for AbstractDaemonTest
to work. Split the rules in gerrit-pgm to break transitive dependency
chain. We shouldn't ship artifacts twice, in plugin-api and in
acceptance-framework.
This change also partially reverts Ie9e63de622, where
//gerrit-acceptance-tests:lib with all its transitive dependencies was
included in plugin-api artifact.
Expose gerrit-acceptance-framework as new plugin artifact. This allows
us to support unit tests in plugins in three different build modes:
* Buck in tree build mode
* Buck standalone build mode
* Maven build
To install gerrit-acceptance-framework locally, the following command
is used:
buck build api_install
To deploy gerrit-acceptance-framework to Maven Central, the following
command is used:
buck build api_deploy
To support unit tests in tree build mode, the following Buck variable
is exposed: GERRIT_TESTS and can be used, e.g.:
java_test(
name = 'cookbook_tests',
srcs = glob(['src/test/java/**/*IT.java']),
labels = ['cookbook-plugin'],
source_under_test = [':cookbook-plugin__plugin'],
deps = GERRIT_PLUGIN_API + GERRIT_TESTS + [
':cookbook-plugin__plugin',
],
)
To support unit tests in standalone build mode, acceptance-framework
maven jar is defined in lib/gerrit/BUCK file:
maven_jar(
name = 'acceptance-framework',
id = 'com.google.gerrit:gerrit-acceptance-framework:' + VER,
license = 'Apache2.0',
attach_source = False,
repository = REPO,
)
bucklets/gerrit_plugin.bucklet is extended with the same variable
that points to the new maven_jar artifact, so that the same Buck
java_test() rule can be used in both modes.
Test plan:
1. run tests in gerrit tree
2. apply corresponding change to cookbook-plugin and run tests in
gerrit tree mode
3. apply corresponding change to bucklets, and run tests for
cookbook-plugin in standalone build mode
Change-Id: I4cadf6616de36ca24712f8b07d282b7a50911105
This commit is contained in:
committed by
Hugo Arès
parent
aaaf9faea6
commit
947b5e53ac
@@ -0,0 +1,165 @@
|
||||
// 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 com.google.common.base.Optional;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.gerrit.common.FooterConstants;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
|
||||
import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.Session;
|
||||
|
||||
import org.eclipse.jgit.api.FetchCommand;
|
||||
import org.eclipse.jgit.api.PushCommand;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
|
||||
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
||||
import org.eclipse.jgit.junit.TestRepository;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.transport.FetchResult;
|
||||
import org.eclipse.jgit.transport.JschConfigSessionFactory;
|
||||
import org.eclipse.jgit.transport.OpenSshConfig.Host;
|
||||
import org.eclipse.jgit.transport.PushResult;
|
||||
import org.eclipse.jgit.transport.RefSpec;
|
||||
import org.eclipse.jgit.transport.SshSessionFactory;
|
||||
import org.eclipse.jgit.util.FS;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class GitUtil {
|
||||
private static final AtomicInteger testRepoCount = new AtomicInteger();
|
||||
private static final int TEST_REPO_WINDOW_DAYS = 2;
|
||||
|
||||
public static void initSsh(final TestAccount a) {
|
||||
final Properties config = new Properties();
|
||||
config.put("StrictHostKeyChecking", "no");
|
||||
JSch.setConfig(config);
|
||||
|
||||
// register a JschConfigSessionFactory that adds the private key as identity
|
||||
// to the JSch instance of JGit so that SSH communication via JGit can
|
||||
// succeed
|
||||
SshSessionFactory.setInstance(new JschConfigSessionFactory() {
|
||||
@Override
|
||||
protected void configure(Host hc, Session session) {
|
||||
try {
|
||||
final JSch jsch = getJSch(hc, FS.DETECTED);
|
||||
jsch.addIdentity("KeyPair", a.privateKey(),
|
||||
a.sshKey.getPublicKeyBlob(), null);
|
||||
} catch (JSchException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TestRepository} with a distinct commit clock.
|
||||
* <p>
|
||||
* It is very easy for tests to create commits with identical subjects and
|
||||
* trees; if such commits also have identical authors/committers, then the
|
||||
* computed Change-Id is identical as well. Tests may generally assume that
|
||||
* Change-Ids are unique, so to ensure this, we provision TestRepository
|
||||
* instances with non-overlapping commit clock times.
|
||||
* <p>
|
||||
* Space test repos 1 day apart, which allows for about 86k ticks per repo
|
||||
* before overlapping, and about 8k instances per process before hitting
|
||||
* JGit's year 2038 limit.
|
||||
*
|
||||
* @param repo repository to wrap.
|
||||
* @return wrapped test repository with distinct commit time space.
|
||||
*/
|
||||
public static <R extends Repository> TestRepository<R> newTestRepository(
|
||||
R repo) throws IOException {
|
||||
TestRepository<R> tr = new TestRepository<>(repo);
|
||||
tr.tick(Ints.checkedCast(TimeUnit.SECONDS.convert(
|
||||
testRepoCount.getAndIncrement() * TEST_REPO_WINDOW_DAYS,
|
||||
TimeUnit.DAYS)));
|
||||
return tr;
|
||||
}
|
||||
|
||||
public static TestRepository<InMemoryRepository> cloneProject(
|
||||
Project.NameKey project, String uri) throws Exception {
|
||||
DfsRepositoryDescription desc =
|
||||
new DfsRepositoryDescription("clone of " + project.get());
|
||||
InMemoryRepository dest = new InMemoryRepository.Builder()
|
||||
.setRepositoryDescription(desc)
|
||||
// SshTransport depends on a real FS to read ~/.ssh/config, but
|
||||
// InMemoryRepository by default uses a null FS.
|
||||
// TODO(dborowitz): Remove when we no longer depend on SSH.
|
||||
.setFS(FS.detect())
|
||||
.build();
|
||||
Config cfg = dest.getConfig();
|
||||
cfg.setString("remote", "origin", "url", uri);
|
||||
cfg.setString("remote", "origin", "fetch",
|
||||
"+refs/heads/*:refs/remotes/origin/*");
|
||||
TestRepository<InMemoryRepository> testRepo = newTestRepository(dest);
|
||||
FetchResult result = testRepo.git().fetch().setRemote("origin").call();
|
||||
String originMaster = "refs/remotes/origin/master";
|
||||
if (result.getTrackingRefUpdate(originMaster) != null) {
|
||||
testRepo.reset(originMaster);
|
||||
}
|
||||
return testRepo;
|
||||
}
|
||||
|
||||
public static TestRepository<InMemoryRepository> cloneProject(
|
||||
Project.NameKey project, SshSession sshSession) throws Exception {
|
||||
return cloneProject(project, sshSession.getUrl() + "/" + project.get());
|
||||
}
|
||||
|
||||
public static void fetch(TestRepository<?> testRepo, String spec)
|
||||
throws GitAPIException {
|
||||
FetchCommand fetch = testRepo.git().fetch();
|
||||
fetch.setRefSpecs(new RefSpec(spec));
|
||||
fetch.call();
|
||||
}
|
||||
|
||||
public static PushResult pushHead(TestRepository<?> testRepo, String ref,
|
||||
boolean pushTags) throws GitAPIException {
|
||||
return pushHead(testRepo, ref, pushTags, false);
|
||||
}
|
||||
|
||||
public static PushResult pushHead(TestRepository<?> testRepo, String ref,
|
||||
boolean pushTags, boolean force) throws GitAPIException {
|
||||
PushCommand pushCmd = testRepo.git().push();
|
||||
pushCmd.setForce(force);
|
||||
pushCmd.setRefSpecs(new RefSpec("HEAD:" + ref));
|
||||
if (pushTags) {
|
||||
pushCmd.setPushTags();
|
||||
}
|
||||
Iterable<PushResult> r = pushCmd.call();
|
||||
return Iterables.getOnlyElement(r);
|
||||
}
|
||||
|
||||
public static Optional<String> getChangeId(TestRepository<?> tr, ObjectId id)
|
||||
throws IOException {
|
||||
RevCommit c = tr.getRevWalk().parseCommit(id);
|
||||
tr.getRevWalk().parseBody(c);
|
||||
List<String> ids = c.getFooterLines(FooterConstants.CHANGE_ID);
|
||||
if (ids.isEmpty()) {
|
||||
return Optional.absent();
|
||||
}
|
||||
return Optional.of(ids.get(ids.size() - 1));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user