Merge "Add gerrit-acceptance-tests"

This commit is contained in:
Shawn Pearce 2013-02-08 15:11:36 +00:00 committed by Gerrit Code Review
commit 7733740096
12 changed files with 753 additions and 3 deletions

7
gerrit-acceptance-tests/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/target
/.classpath
/.project
/.settings/org.eclipse.core.resources.prefs
/.settings/org.eclipse.jdt.core.prefs
/.settings/org.eclipse.m2e.core.prefs
/gerrit-common.iml

View File

@ -0,0 +1,155 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-parent</artifactId>
<version>2.6-SNAPSHOT</version>
</parent>
<artifactId>gerrit-acceptance-tests</artifactId>
<name>Gerrit Code Review - Acceptance Tests</name>
<description>
Gerrit Acceptance Tests
</description>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-reviewdb</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-main</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-server</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>bouncycastle</groupId>
<artifactId>bcprov-jdk15</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>bouncycastle</groupId>
<artifactId>bcpg-jdk15</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-openid</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-sshd</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-httpd</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-pgm</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>acceptance</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
<execution>
<id>verify</id>
<goals>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,35 @@
// 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 org.junit.After;
import org.junit.Before;
public abstract class AbstractDaemonTest {
private GerritServer server;
@Before
public final void beforeTest() throws Exception {
server = GerritServer.start();
server.getTestInjector().injectMembers(this);
}
@After
public final void afterTest() throws Exception {
server.stop();
}
}

View File

@ -0,0 +1,124 @@
// 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 java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import javax.inject.Inject;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountExternalId;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroupMember;
import com.google.gerrit.reviewdb.client.AccountSshKey;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountByEmailCache;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.ssh.SshKeyCache;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.KeyPair;
import com.jcraft.jsch.Session;
class AccountCreator {
private SchemaFactory<ReviewDb> reviewDbProvider;
private GroupCache groupCache;
private SshKeyCache sshKeyCache;
private AccountCache accountCache;
private AccountByEmailCache byEmailCache;
@Inject
AccountCreator(SchemaFactory<ReviewDb> schema, GroupCache groupCache,
SshKeyCache sshKeyCache, AccountCache accountCache,
AccountByEmailCache byEmailCache) {
reviewDbProvider = schema;
this.groupCache = groupCache;
this.sshKeyCache = sshKeyCache;
this.accountCache = accountCache;
this.byEmailCache = byEmailCache;
}
TestAccount create(String username, String email, String fullName,
String... groups)
throws OrmException, UnsupportedEncodingException, JSchException {
ReviewDb db = reviewDbProvider.open();
try {
Account.Id id = new Account.Id(db.nextAccountId());
KeyPair sshKey = genSshKey();
AccountSshKey key =
new AccountSshKey(new AccountSshKey.Id(id, 1), publicKey(sshKey, email));
AccountExternalId extUser =
new AccountExternalId(id, new AccountExternalId.Key(
AccountExternalId.SCHEME_USERNAME, username));
String httpPass = "http-pass";
extUser.setPassword(httpPass);
db.accountExternalIds().insert(Collections.singleton(extUser));
if (email != null) {
AccountExternalId extMailto = new AccountExternalId(id, getEmailKey(email));
extMailto.setEmailAddress(email);
db.accountExternalIds().insert(Collections.singleton(extMailto));
}
Account a = new Account(id);
a.setFullName(fullName);
a.setPreferredEmail(email);
db.accounts().insert(Collections.singleton(a));
db.accountSshKeys().insert(Collections.singleton(key));
for (String n : groups) {
AccountGroup.NameKey k = new AccountGroup.NameKey(n);
AccountGroup g = groupCache.get(k);
AccountGroupMember m =
new AccountGroupMember(new AccountGroupMember.Key(id, g.getId()));
db.accountGroupMembers().insert(Collections.singleton(m));
}
sshKeyCache.evict(username);
accountCache.evictByUsername(username);
byEmailCache.evict(email);
return new TestAccount(username, email, fullName, sshKey, httpPass);
} finally {
db.close();
}
}
private AccountExternalId.Key getEmailKey(String email) {
return new AccountExternalId.Key(AccountExternalId.SCHEME_MAILTO, email);
}
private static KeyPair genSshKey() throws JSchException {
JSch jsch = new JSch();
return KeyPair.genKeyPair(jsch, KeyPair.RSA);
}
private static String publicKey(KeyPair sshKey, String comment)
throws UnsupportedEncodingException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
sshKey.writePublicKey(out, comment);
return out.toString("ASCII");
}
}

View File

@ -0,0 +1,126 @@
// 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 java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.pgm.Daemon;
import com.google.gerrit.pgm.Init;
import com.google.gerrit.server.config.FactoryModule;
import com.google.inject.Injector;
import com.google.inject.Module;
class GerritServer {
/** Returns fully started Gerrit server */
static GerritServer start() throws Exception {
final String sitePath = initSite();
final CyclicBarrier serverStarted = new CyclicBarrier(2);
final Daemon daemon = new Daemon(new Runnable() {
public void run() {
try {
serverStarted.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
}
});
ExecutorService daemonService = Executors.newSingleThreadExecutor();
daemonService.submit(new Callable<Void>() {
public Void call() throws Exception {
int rc = daemon.main(new String[] {"-d", sitePath, "--headless" });
if (rc != 0) {
serverStarted.reset();
}
return null;
};
});
serverStarted.await();
System.out.println("Gerrit Server Started");
Injector i = createTestInjector(daemon);
return new GerritServer(i, daemon, daemonService);
}
private static String initSite() throws Exception {
DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
String path = "target/test_site_" + df.format(new Date());
Init init = new Init();
int rc = init.main(new String[] {"-d", path, "--batch", "--no-auto-start"});
if (rc != 0) {
throw new RuntimeException("Couldn't initialize site");
}
return path;
}
private static Injector createTestInjector(Daemon daemon) throws Exception {
Injector sysInjector = get(daemon, "sysInjector");
Module module = new FactoryModule() {
@Override
protected void configure() {
bind(AccountCreator.class);
}
};
return sysInjector.createChildInjector(module);
}
@SuppressWarnings("unchecked")
private static <T> T get(Object obj, String field) throws SecurityException,
NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field f = obj.getClass().getDeclaredField(field);
f.setAccessible(true);
return (T) f.get(obj);
}
private Daemon daemon;
private ExecutorService daemonService;
private Injector testInjector;
private GerritServer(Injector testInjector,
Daemon daemon, ExecutorService daemonService) {
this.testInjector = testInjector;
this.daemon = daemon;
this.daemonService = daemonService;
}
Injector getTestInjector() {
return testInjector;
}
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);
}
}

View File

@ -0,0 +1,58 @@
// 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 java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
class RestSession {
private final TestAccount account;
DefaultHttpClient client;
RestSession(TestAccount account) {
this.account = account;
}
Reader get(String endPoint) throws IOException {
HttpGet get = new HttpGet("http://localhost:8080/a" + endPoint);
HttpResponse response = getClient().execute(get);
Reader reader = new InputStreamReader(response.getEntity().getContent());
reader.skip(4);
return reader;
}
Reader post(String endPoint) {
// TODO
return null;
}
private DefaultHttpClient getClient() {
if (client == null) {
client = new DefaultHttpClient();
client.getCredentialsProvider().setCredentials(
new AuthScope("localhost", 8080),
new UsernamePasswordCredentials(account.username, account.httpPassword));
}
return client;
}
}

View File

@ -0,0 +1,68 @@
// 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 java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
class SshSession {
private final TestAccount account;
private Session session;
SshSession(TestAccount account) {
this.account = account;
}
String exec(String command) throws JSchException, IOException {
ChannelExec channel = (ChannelExec) getSession().openChannel("exec");
try {
channel.setCommand(command);
channel.setInputStream(null);
InputStream in = channel.getInputStream();
channel.connect();
Scanner s = new Scanner(in).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
} finally {
channel.disconnect();
}
}
void close() {
if (session != null) {
session.disconnect();
session = null;
}
}
private Session getSession() throws JSchException {
if (session == null) {
JSch jsch = new JSch();
jsch.addIdentity("KeyPair",
account.privateKey(), account.sshKey.getPublicKeyBlob(), null);
session = jsch.getSession(account.username, "localhost", 29418);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
}
return session;
}
}

View File

@ -0,0 +1,115 @@
// 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 org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.Reader;
import java.util.Map;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.Sets;
import com.google.common.reflect.TypeToken;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gson.Gson;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.jcraft.jsch.JSchException;
/**
* An example test that tests presence of system groups in a newly initialized
* review site.
*
* The test shows how to perform these checks via SSH, REST or using Gerrit
* internals.
*/
public class SystemGroupsIT extends AbstractDaemonTest {
@Inject
private SchemaFactory<ReviewDb> reviewDbProvider;
@Inject
private AccountCreator accounts;
protected TestAccount admin;
@Before
public void setUp() throws Exception {
admin = accounts.create("admin", "admin@sap.com", "Administrator",
"Administrators");
}
@Test
public void systemGroupsCreated_ssh() throws JSchException, IOException {
SshSession session = new SshSession(admin);
String result = session.exec("gerrit ls-groups");
assertTrue(result.contains("Administrators"));
assertTrue(result.contains("Anonymous Users"));
assertTrue(result.contains("Non-Interactive Users"));
assertTrue(result.contains("Project Owners"));
assertTrue(result.contains("Registered Users"));
session.close();
}
private static class Group {
String id;
String name;
String url;
String description;
Integer groupId;
String ownerId;
};
@Test
public void systemGroupsCreated_rest() throws IOException {
RestSession session = new RestSession(admin);
Reader r = session.get("/groups/");
Gson gson = new Gson();
Map<String, Group> result =
gson.fromJson(r, new TypeToken<Map<String, Group>>() {}.getType());
Set<String> names = result.keySet();
assertTrue(names.contains("Administrators"));
assertTrue(names.contains("Administrators"));
assertTrue(names.contains("Anonymous Users"));
assertTrue(names.contains("Non-Interactive Users"));
assertTrue(names.contains("Project Owners"));
assertTrue(names.contains("Registered Users"));
}
@Test
public void systemGroupsCreated_internals() throws OrmException {
ReviewDb db = reviewDbProvider.open();
try {
Set<String> names = Sets.newHashSet();
for (AccountGroup g : db.accountGroups().all()) {
names.add(g.getName());
}
assertTrue(names.contains("Administrators"));
assertTrue(names.contains("Administrators"));
assertTrue(names.contains("Anonymous Users"));
assertTrue(names.contains("Non-Interactive Users"));
assertTrue(names.contains("Project Owners"));
assertTrue(names.contains("Registered Users"));
} finally {
db.close();
}
}
}

View File

@ -0,0 +1,43 @@
// 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 java.io.ByteArrayOutputStream;
import com.jcraft.jsch.KeyPair;
public class TestAccount {
final String username;
final String email;
final String fullName;
final KeyPair sshKey;
final String httpPassword;
TestAccount(String username, String email, String fullName,
KeyPair sshKey, String httpPassword) {
this.username = username;
this.email = email;
this.fullName = fullName;
this.sshKey = sshKey;
this.httpPassword = httpPassword;
}
byte[] privateKey() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
sshKey.writePrivateKey(out);
return out.toByteArray();
}
}

View File

@ -128,6 +128,15 @@ public class Daemon extends SiteProgram {
private Injector httpdInjector; private Injector httpdInjector;
private File runFile; private File runFile;
private Runnable serverStarted;
public Daemon() {
}
public Daemon(Runnable serverStarted) {
this.serverStarted = serverStarted;
}
@Override @Override
public int run() throws Exception { public int run() throws Exception {
mustHaveValidSite(); mustHaveValidSite();
@ -204,6 +213,10 @@ public class Daemon extends SiteProgram {
} }
} }
if (serverStarted != null) {
serverStarted.run();
}
RuntimeShutdown.waitFor(); RuntimeShutdown.waitFor();
return 0; return 0;
} catch (Throwable err) { } catch (Throwable err) {

View File

@ -103,9 +103,7 @@ public class RuntimeShutdown {
try { try {
wait(); wait();
} catch (InterruptedException e) { } catch (InterruptedException e) {
log.warn("Thread " + Thread.currentThread().getName() return;
+ " interrupted while waiting for graceful shutdown;"
+ " ignoring interrupt request");
} }
} }
} }

View File

@ -91,6 +91,8 @@ limitations under the License.
<module>gerrit-extension-api</module> <module>gerrit-extension-api</module>
<module>gerrit-gwtui</module> <module>gerrit-gwtui</module>
<module>gerrit-acceptance-tests</module>
</modules> </modules>
<profiles> <profiles>
@ -571,6 +573,12 @@ limitations under the License.
<version>0.5.1-r1095809</version> <version>0.5.1-r1095809</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.0</version>
</dependency>
<dependency> <dependency>
<groupId>com.jcraft</groupId> <groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId> <artifactId>jsch</artifactId>