Merge changes from topic 'test-against-old-schema-versions'

* changes:
  Fix running against change index schema version 39
  Make change query tests work against change index schema version 40
  Allow to specify index versions for query tests via env var or system property
  Add support for dynamic config suites
  Run Lucene query tests also against previous schema version
  Allow to specify the index test version for all Lucene indexes
  Rename index.lucene.testVersion to index.lucene.changesTestVersion
This commit is contained in:
Dave Borowitz
2017-06-02 16:54:48 +00:00
committed by Gerrit Code Review
14 changed files with 575 additions and 47 deletions

View File

@@ -264,6 +264,18 @@ junit_tests(
],
)
junit_tests(
name = "testutil_test",
size = "small",
srcs = [
"src/test/java/com/google/gerrit/testutil/IndexVersionsTest.java",
],
visibility = ["//visibility:public"],
deps = TESTUTIL_DEPS + [
":testutil",
]
)
load("//tools/bzl:javadoc.bzl", "java_doc")
java_doc(

View File

@@ -18,6 +18,8 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.Nullable;
/**
* Definitions of the various schema versions over a given Gerrit data type.
@@ -53,4 +55,12 @@ public abstract class SchemaDefinitions<V> {
public final Schema<V> getLatest() {
return schemas.lastEntry().getValue();
}
@Nullable
public final Schema<V> getPrevious() {
if (schemas.size() <= 1) {
return null;
}
return Iterables.get(schemas.descendingMap().values(), 1);
}
}

View File

@@ -597,7 +597,11 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
}
if ("private".equalsIgnoreCase(value)) {
return new BooleanPredicate(ChangeField.PRIVATE, args.fillArgs);
if (args.getSchema().hasField(ChangeField.PRIVATE)) {
return new BooleanPredicate(ChangeField.PRIVATE, args.fillArgs);
}
throw new QueryParseException(
"'is:private' operator is not supported by change index version");
}
if ("assigned".equalsIgnoreCase(value)) {

View File

@@ -60,9 +60,7 @@ import org.eclipse.jgit.lib.Config;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@Ignore
public abstract class AbstractQueryAccountsTest extends GerritServerTests {
@@ -73,8 +71,6 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
return cfg;
}
@Rule public final TestName testName = new TestName();
@Inject protected AccountCache accountCache;
@Inject protected AccountManager accountManager;
@@ -453,7 +449,8 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
if (name == null) {
return null;
}
String suffix = testName.getMethodName().toLowerCase();
String suffix = getSanitizedMethodName();
if (name.contains("@")) {
return name + "." + suffix;
}

View File

@@ -14,12 +14,26 @@
package com.google.gerrit.server.query.account;
import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
import com.google.gerrit.testutil.ConfigSuite;
import com.google.gerrit.testutil.InMemoryModule;
import com.google.gerrit.testutil.IndexVersions;
import com.google.inject.Guice;
import com.google.inject.Injector;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.lib.Config;
public class LuceneQueryAccountsTest extends AbstractQueryAccountsTest {
@ConfigSuite.Configs
public static Map<String, Config> againstPreviousIndexVersion() {
// the current schema version is already tested by the inherited default config suite
List<Integer> schemaVersions =
IndexVersions.getWithoutLatest(AccountSchemaDefinitions.INSTANCE);
return IndexVersions.asConfigMap(
AccountSchemaDefinitions.INSTANCE, schemaVersions, "againstIndexVersion", defaultConfig());
}
@Override
protected Injector createInjector() {
Config luceneConfig = new Config(config);

View File

@@ -22,6 +22,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.stream.Collectors.toList;
import static org.junit.Assert.fail;
import com.google.common.base.MoreObjects;
import com.google.common.collect.FluentIterable;
@@ -77,8 +78,10 @@ import com.google.gerrit.server.change.ChangeTriplet;
import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.index.FieldDef;
import com.google.gerrit.server.index.IndexConfig;
import com.google.gerrit.server.index.QueryOptions;
import com.google.gerrit.server.index.Schema;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.index.change.ChangeIndexer;
@@ -400,6 +403,13 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byPrivate() throws Exception {
if (getSchemaVersion() < 40) {
assertMissingField(ChangeField.PRIVATE);
assertFailingQuery(
"is:private", "'is:private' operator is not supported by change index version");
return;
}
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo), userId);
Account.Id user2 =
@@ -424,6 +434,12 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byWip() throws Exception {
if (getSchemaVersion() < 42) {
assertMissingField(ChangeField.WIP);
assertFailingQuery("is:wip", "'is:wip' operator is not supported by change index version");
return;
}
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo), userId);
@@ -450,18 +466,26 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
rin.state = ReviewerState.REVIEWER;
gApi.changes().id(change1.getId().get()).addReviewer(rin);
assertQuery("is:wip");
assertQuery("reviewer:" + user1, change1);
if (getSchemaVersion() >= 42) {
assertQuery("is:wip");
assertQuery("reviewer:" + user1, change1);
gApi.changes().id(change1.getChangeId()).setWorkInProgress();
gApi.changes().id(change1.getChangeId()).setWorkInProgress();
assertQuery("is:wip", change1);
assertQuery("reviewer:" + user1);
assertQuery("is:wip", change1);
assertQuery("reviewer:" + user1);
gApi.changes().id(change1.getChangeId()).setReadyForReview();
gApi.changes().id(change1.getChangeId()).setReadyForReview();
assertQuery("is:wip");
assertQuery("reviewer:" + user1, change1);
assertQuery("is:wip");
assertQuery("reviewer:" + user1, change1);
} else {
assertMissingField(ChangeField.WIP);
assertFailingQuery("is:wip", "'is:wip' operator is not supported by change index version");
assertQuery("reviewer:" + user1, change1);
gApi.changes().id(change1.getChangeId()).setWorkInProgress();
assertQuery("reviewer:" + user1, change1);
}
}
@Test
@@ -1685,12 +1709,25 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
rin.state = ReviewerState.CC;
gApi.changes().id(change2.getId().get()).addReviewer(rin);
assertQuery("reviewer:\"" + userByEmailWithName + "\"", change1);
assertQuery("cc:\"" + userByEmailWithName + "\"", change2);
if (getSchemaVersion() >= 41) {
assertQuery("reviewer:\"" + userByEmailWithName + "\"", change1);
assertQuery("cc:\"" + userByEmailWithName + "\"", change2);
// Omitting the name:
assertQuery("reviewer:\"" + userByEmail + "\"", change1);
assertQuery("cc:\"" + userByEmail + "\"", change2);
// Omitting the name:
assertQuery("reviewer:\"" + userByEmail + "\"", change1);
assertQuery("cc:\"" + userByEmail + "\"", change2);
} else {
assertMissingField(ChangeField.REVIEWER_BY_EMAIL);
assertFailingQuery(
"reviewer:\"" + userByEmailWithName + "\"", "User " + userByEmailWithName + " not found");
assertFailingQuery(
"cc:\"" + userByEmailWithName + "\"", "User " + userByEmailWithName + " not found");
// Omitting the name:
assertFailingQuery("reviewer:\"" + userByEmail + "\"", "User " + userByEmail + " not found");
assertFailingQuery("cc:\"" + userByEmail + "\"", "User " + userByEmail + " not found");
}
}
@Test
@@ -1719,8 +1756,17 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
rin.state = ReviewerState.CC;
gApi.changes().id(change2.getId().get()).addReviewer(rin);
assertQuery("reviewer:\"someone@example.com\"");
assertQuery("cc:\"someone@example.com\"");
if (getSchemaVersion() >= 41) {
assertQuery("reviewer:\"someone@example.com\"");
assertQuery("cc:\"someone@example.com\"");
} else {
assertMissingField(ChangeField.REVIEWER_BY_EMAIL);
String someoneEmail = "someone@example.com";
assertFailingQuery(
"reviewer:\"" + someoneEmail + "\"", "User " + someoneEmail + " not found");
assertFailingQuery("cc:\"" + someoneEmail + "\"", "User " + someoneEmail + " not found");
}
}
@Test
@@ -2265,4 +2311,27 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
return id;
}
}
protected void assertMissingField(FieldDef<ChangeData, ?> field) {
assertThat(getSchema().hasField(field))
.named("schema %s has field %s", getSchemaVersion(), field.getName())
.isFalse();
}
protected void assertFailingQuery(String query, String expectedMessage) throws Exception {
try {
assertQuery(query);
fail("expected BadRequestException for query '" + query + "'");
} catch (BadRequestException e) {
assertThat(e.getMessage()).isEqualTo(expectedMessage);
}
}
protected int getSchemaVersion() {
return getSchema().getVersion();
}
protected Schema<ChangeData> getSchema() {
return indexes.getSearchIndex().getSchema();
}
}

View File

@@ -16,16 +16,29 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.testutil.ConfigSuite;
import com.google.gerrit.testutil.InMemoryModule;
import com.google.gerrit.testutil.InMemoryRepositoryManager.Repo;
import com.google.gerrit.testutil.IndexVersions;
import com.google.inject.Guice;
import com.google.inject.Injector;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
public class LuceneQueryChangesTest extends AbstractQueryChangesTest {
@ConfigSuite.Configs
public static Map<String, Config> againstPreviousIndexVersion() {
// the current schema version is already tested by the inherited default config suite
List<Integer> schemaVersions = IndexVersions.getWithoutLatest(ChangeSchemaDefinitions.INSTANCE);
return IndexVersions.asConfigMap(
ChangeSchemaDefinitions.INSTANCE, schemaVersions, "againstIndexVersion", defaultConfig());
}
@Override
protected Injector createInjector() {
Config luceneConfig = new Config(config);

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.server.query.group;
import static com.google.common.truth.Truth.assertThat;
import static java.util.stream.Collectors.toList;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.groups.GroupInput;
@@ -58,9 +59,7 @@ import org.eclipse.jgit.lib.Config;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@Ignore
public abstract class AbstractQueryGroupsTest extends GerritServerTests {
@@ -71,8 +70,6 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
return cfg;
}
@Rule public final TestName testName = new TestName();
@Inject protected AccountCache accountCache;
@Inject protected AccountManager accountManager;
@@ -195,7 +192,9 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
@Test
public void byInname() throws Exception {
String namePart = testName.getMethodName();
String namePart = getSanitizedMethodName();
namePart = CharMatcher.is('_').removeFrom(namePart);
GroupInfo group1 = createGroup("group-" + namePart);
GroupInfo group2 = createGroup("group-" + namePart + "-2");
GroupInfo group3 = createGroup("group-" + namePart + "3");
@@ -441,6 +440,7 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
if (name == null) {
return null;
}
return name + "_" + testName.getMethodName().toLowerCase();
return name + "_" + getSanitizedMethodName();
}
}

View File

@@ -14,12 +14,25 @@
package com.google.gerrit.server.query.group;
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
import com.google.gerrit.testutil.ConfigSuite;
import com.google.gerrit.testutil.InMemoryModule;
import com.google.gerrit.testutil.IndexVersions;
import com.google.inject.Guice;
import com.google.inject.Injector;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.lib.Config;
public class LuceneQueryGroupsTest extends AbstractQueryGroupsTest {
@ConfigSuite.Configs
public static Map<String, Config> againstPreviousIndexVersion() {
// the current schema version is already tested by the inherited default config suite
List<Integer> schemaVersions = IndexVersions.getWithoutLatest(GroupSchemaDefinitions.INSTANCE);
return IndexVersions.asConfigMap(
GroupSchemaDefinitions.INSTANCE, schemaVersions, "againstIndexVersion", defaultConfig());
}
@Override
protected Injector createInjector() {
Config luceneConfig = new Config(config);

View File

@@ -18,8 +18,10 @@ import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.stream.Collectors.toSet;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.lang.annotation.Annotation;
@@ -29,7 +31,10 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import org.junit.runner.Runner;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.Suite;
@@ -80,6 +85,21 @@ import org.junit.runners.model.InitializationError;
* Additionally, config values used by <strong>default</strong> can be set in a method annotated
* with {@code @ConfigSuite.Default}.
*
* <p>In addition groups of tests for different configurations can be defined by annotating a method
* that returns a Map&lt;String, Config&gt; with {@link Configs}. The map keys define the test suite
* names, while the values define the configurations for the test suites.
*
* <pre>
* {@literal @}ConfigSuite.Configs
* public static Map&lt;String, Config&gt; configs() {
* Config cfgA = new Config();
* cfgA.setString("gerrit", null, "testValue", "a");
* Config cfgB = new Config();
* cfgB.setString("gerrit", null, "testValue", "b");
* return ImmutableMap.of("testWithValueA", cfgA, "testWithValueB", cfgB);
* }
* </pre>
*
* <p>The name of the config method corresponding to the currently-running test can be stored in a
* field annotated with {@code @ConfigSuite.Name}.
*/
@@ -94,6 +114,10 @@ public class ConfigSuite extends Suite {
@Retention(RUNTIME)
public static @interface Config {}
@Target({METHOD})
@Retention(RUNTIME)
public static @interface Configs {}
@Target({FIELD})
@Retention(RUNTIME)
public static @interface Parameter {}
@@ -103,25 +127,29 @@ public class ConfigSuite extends Suite {
public static @interface Name {}
private static class ConfigRunner extends BlockJUnit4ClassRunner {
private final Method configMethod;
private final org.eclipse.jgit.lib.Config cfg;
private final Field parameterField;
private final Field nameField;
private final String name;
private ConfigRunner(
Class<?> clazz, Field parameterField, Field nameField, String name, Method configMethod)
Class<?> clazz,
Field parameterField,
Field nameField,
String name,
org.eclipse.jgit.lib.Config cfg)
throws InitializationError {
super(clazz);
this.parameterField = parameterField;
this.nameField = nameField;
this.name = name;
this.configMethod = configMethod;
this.cfg = cfg;
}
@Override
public Object createTest() throws Exception {
Object test = getTestClass().getJavaClass().newInstance();
parameterField.set(test, callConfigMethod(configMethod));
parameterField.set(test, new org.eclipse.jgit.lib.Config(cfg));
if (nameField != null) {
nameField.set(test, name);
}
@@ -143,14 +171,23 @@ public class ConfigSuite extends Suite {
private static List<Runner> runnersFor(Class<?> clazz) {
Method defaultConfig = getDefaultConfig(clazz);
List<Method> configs = getConfigs(clazz);
Map<String, org.eclipse.jgit.lib.Config> configMap =
callConfigMapMethod(getConfigMap(clazz), configs);
Field parameterField = getOnlyField(clazz, Parameter.class);
checkArgument(parameterField != null, "No @ConfigSuite.Parameter found");
Field nameField = getOnlyField(clazz, Name.class);
List<Runner> result = Lists.newArrayListWithCapacity(configs.size() + 1);
try {
result.add(new ConfigRunner(clazz, parameterField, nameField, null, defaultConfig));
result.add(
new ConfigRunner(
clazz, parameterField, nameField, null, callConfigMethod(defaultConfig)));
for (Method m : configs) {
result.add(new ConfigRunner(clazz, parameterField, nameField, m.getName(), m));
result.add(
new ConfigRunner(clazz, parameterField, nameField, m.getName(), callConfigMethod(m)));
}
for (Map.Entry<String, org.eclipse.jgit.lib.Config> e : configMap.entrySet()) {
result.add(new ConfigRunner(clazz, parameterField, nameField, e.getKey(), e.getValue()));
}
return result;
} catch (InitializationError e) {
@@ -163,15 +200,20 @@ public class ConfigSuite extends Suite {
}
private static Method getDefaultConfig(Class<?> clazz) {
return getAnnotatedMethod(clazz, Default.class);
}
private static Method getConfigMap(Class<?> clazz) {
return getAnnotatedMethod(clazz, Configs.class);
}
private static <T extends Annotation> Method getAnnotatedMethod(
Class<?> clazz, Class<T> annotationClass) {
Method result = null;
for (Method m : clazz.getMethods()) {
Default ann = m.getAnnotation(Default.class);
T ann = m.getAnnotation(annotationClass);
if (ann != null) {
checkArgument(
result == null,
"Multiple methods annotated with @ConfigSuite.Method: %s, %s",
result,
m);
checkArgument(result == null, "Multiple methods annotated with %s: %s, %s", ann, result, m);
result = m;
}
}
@@ -183,8 +225,7 @@ public class ConfigSuite extends Suite {
for (Method m : clazz.getMethods()) {
Config ann = m.getAnnotation(Config.class);
if (ann != null) {
checkArgument(
!m.getName().equals(DEFAULT), "@ConfigSuite.Config cannot be named %s", DEFAULT);
checkArgument(!m.getName().equals(DEFAULT), "%s cannot be named %s", ann, DEFAULT);
result.add(m);
}
}
@@ -208,6 +249,45 @@ public class ConfigSuite extends Suite {
}
}
private static Map<String, org.eclipse.jgit.lib.Config> callConfigMapMethod(
Method m, List<Method> configs) {
if (m == null) {
return ImmutableMap.of();
}
checkArgument(Map.class.isAssignableFrom(m.getReturnType()), "%s must return Map", m);
Type[] types = ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments();
checkArgument(
String.class.isAssignableFrom((Class<?>) types[0]),
"The map returned by %s must have String as key",
m);
checkArgument(
org.eclipse.jgit.lib.Config.class.isAssignableFrom((Class<?>) types[1]),
"The map returned by %s must have Config as value",
m);
checkArgument((m.getModifiers() & Modifier.STATIC) != 0, "%s must be static", m);
checkArgument(m.getParameterTypes().length == 0, "%s must take no parameters", m);
try {
@SuppressWarnings("unchecked")
Map<String, org.eclipse.jgit.lib.Config> configMap =
(Map<String, org.eclipse.jgit.lib.Config>) m.invoke(null);
checkArgument(
!configMap.containsKey(DEFAULT),
"The map returned by %s cannot contain key %s (duplicate test suite name)",
m,
DEFAULT);
for (String name : configs.stream().map(cm -> cm.getName()).collect(toSet())) {
checkArgument(
!configMap.containsKey(name),
"The map returned by %s cannot contain key %s (duplicate test suite name)",
m,
name);
}
return configMap;
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new IllegalArgumentException(e);
}
}
private static Field getOnlyField(Class<?> clazz, Class<? extends Annotation> ann) {
List<Field> fields = Lists.newArrayListWithExpectedSize(1);
for (Field f : clazz.getFields()) {

View File

@@ -14,11 +14,13 @@
package com.google.gerrit.testutil;
import com.google.common.base.CharMatcher;
import com.google.gwtorm.client.KeyUtil;
import com.google.gwtorm.server.StandardKeyEncoder;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
import org.junit.rules.TestName;
@Ignore
public abstract class GerritBaseTests {
@@ -27,4 +29,12 @@ public abstract class GerritBaseTests {
}
@Rule public ExpectedException exception = ExpectedException.none();
@Rule public final TestName testName = new TestName();
protected String getSanitizedMethodName() {
String name = testName.getMethodName().toLowerCase();
name = CharMatcher.javaLetterOrDigit().negate().replaceFrom(name, '_');
name = CharMatcher.is('_').trimTrailingFrom(name);
return name;
}
}

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.testutil;
import static com.google.common.base.Preconditions.checkState;
import static com.google.inject.Scopes.SINGLETON;
import com.google.common.util.concurrent.ListeningExecutorService;
@@ -48,10 +49,13 @@ import com.google.gerrit.server.git.PerThreadRequestScope;
import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.SendEmailExecutor;
import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.gerrit.server.index.SchemaDefinitions;
import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
import com.google.gerrit.server.index.account.AllAccountsIndexer;
import com.google.gerrit.server.index.change.AllChangesIndexer;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.server.index.group.AllGroupsIndexer;
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
import com.google.gerrit.server.notedb.ChangeBundleReader;
import com.google.gerrit.server.notedb.GwtormChangeBundleReader;
@@ -259,14 +263,9 @@ public class InMemoryModule extends FactoryModule {
private Module indexModule(String moduleClassName) {
try {
Map<String, Integer> singleVersions = new HashMap<>();
int version = cfg.getInt("index", "lucene", "testVersion", -1);
if (version > 0) {
singleVersions.put(ChangeSchemaDefinitions.INSTANCE.getName(), version);
}
Class<?> clazz = Class.forName(moduleClassName);
Method m = clazz.getMethod("singleVersionWithExplicitVersions", Map.class, int.class);
return (Module) m.invoke(null, singleVersions, 0);
return (Module) m.invoke(null, getSingleSchemaVersions(), 0);
} catch (ClassNotFoundException
| SecurityException
| NoSuchMethodException
@@ -279,4 +278,25 @@ public class InMemoryModule extends FactoryModule {
throw pe;
}
}
private Map<String, Integer> getSingleSchemaVersions() {
Map<String, Integer> singleVersions = new HashMap<>();
putSchemaVersion(singleVersions, AccountSchemaDefinitions.INSTANCE);
putSchemaVersion(singleVersions, ChangeSchemaDefinitions.INSTANCE);
putSchemaVersion(singleVersions, GroupSchemaDefinitions.INSTANCE);
return singleVersions;
}
private void putSchemaVersion(
Map<String, Integer> singleVersions, SchemaDefinitions<?> schemaDef) {
String schemaName = schemaDef.getName();
int version = cfg.getInt("index", "lucene", schemaName + "TestVersion", -1);
if (version > 0) {
checkState(
!singleVersions.containsKey(schemaName),
"version for schema %s was alreay set",
schemaName);
singleVersions.put(schemaName, version);
}
}
}

View File

@@ -0,0 +1,146 @@
// Copyright (C) 2017 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.testutil;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.stream.Collectors.toMap;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints;
import com.google.gerrit.server.index.Schema;
import com.google.gerrit.server.index.SchemaDefinitions;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import org.eclipse.jgit.lib.Config;
public class IndexVersions {
static final String ALL = "all";
static final String CURRENT = "current";
static final String PREVIOUS = "previous";
/**
* Returns the index versions from {@link IndexVersions#get(SchemaDefinitions)} without the latest
* schema version.
*
* @param schemaDef the schema definition
* @return the index versions from {@link IndexVersions#get(SchemaDefinitions)} without the latest
* schema version
*/
public static <V> ImmutableList<Integer> getWithoutLatest(SchemaDefinitions<V> schemaDef) {
List<Integer> schemaVersions = new ArrayList<>(get(schemaDef));
schemaVersions.remove(Integer.valueOf(schemaDef.getLatest().getVersion()));
return ImmutableList.copyOf(schemaVersions);
}
/**
* Returns the schema versions against which the query tests should be executed.
*
* <p>The schema versions are read from the '<schema-name>_INDEX_VERSIONS' env var if it is set,
* e.g. 'ACCOUNTS_INDEX_VERSIONS', 'CHANGES_INDEX_VERSIONS', 'GROUPS_INDEX_VERSIONS'.
*
* <p>If schema versions were not specified by an env var, they are read from the
* 'gerrit.index.<schema-name>.versions' system property, e.g. 'gerrit.index.accounts.version',
* 'gerrit.index.changes.version', 'gerrit.index.groups.version'.
*
* <p>As value a comma-separated list of schema versions is expected. {@code current} can be used
* for the latest schema version and {@code previous} is resolved to the second last schema
* version. Alternatively the value can also be {@code all} for all schema versions.
*
* <p>If schema versions were neither specified by an env var nor by a system property, the
* current and the second last schema versions are returned. If there is no other schema version
* than the current schema version, only the current schema version is returned.
*
* @param schemaDef the schema definition
* @return the schema versions against which the query tests should be executed
* @throws IllegalArgumentException if the value of the env var or system property is invalid or
* if any of the specified schema versions doesn't exist
*/
public static <V> ImmutableList<Integer> get(SchemaDefinitions<V> schemaDef) {
String envVar = schemaDef.getName().toUpperCase() + "_INDEX_VERSIONS";
String value = System.getenv(envVar);
if (!Strings.isNullOrEmpty(value)) {
return get(schemaDef, "env variable " + envVar, value);
}
String systemProperty = "gerrit.index." + schemaDef.getName().toLowerCase() + ".versions";
value = System.getProperty(systemProperty);
return get(schemaDef, "system property " + systemProperty, value);
}
@VisibleForTesting
static <V> ImmutableList<Integer> get(SchemaDefinitions<V> schemaDef, String name, String value) {
if (value != null) {
value = value.trim();
}
SortedMap<Integer, Schema<V>> schemas = schemaDef.getSchemas();
if (!Strings.isNullOrEmpty(value)) {
if (ALL.equals(value)) {
return ImmutableList.copyOf(schemas.keySet());
}
List<Integer> versions = new ArrayList<>();
for (String s : Splitter.on(',').trimResults().split(value)) {
if (CURRENT.equals(s)) {
versions.add(schemaDef.getLatest().getVersion());
} else if (PREVIOUS.equals(s)) {
checkArgument(schemaDef.getPrevious() != null, "previous version does not exist");
versions.add(schemaDef.getPrevious().getVersion());
} else {
Integer version = Ints.tryParse(s);
checkArgument(version != null, "Invalid value for %s: %s", name, s);
checkArgument(
schemas.containsKey(version),
"Index version %s that was specified by %s not found." + " Possible versions are: %s",
version,
name,
schemas.keySet());
versions.add(version);
}
}
return ImmutableList.copyOf(versions);
}
List<Integer> schemaVersions = new ArrayList<>(2);
if (schemaDef.getPrevious() != null) {
schemaVersions.add(schemaDef.getPrevious().getVersion());
}
schemaVersions.add(schemaDef.getLatest().getVersion());
return ImmutableList.copyOf(schemaVersions);
}
public static <V> Map<String, Config> asConfigMap(
SchemaDefinitions<V> schemaDef,
List<Integer> schemaVersions,
String testSuiteNamePrefix,
Config baseConfig) {
return schemaVersions
.stream()
.collect(
toMap(
i -> testSuiteNamePrefix + i,
i -> {
Config cfg = baseConfig;
cfg.setInt(
"index", "lucene", schemaDef.getName().toLowerCase() + "TestVersion", i);
return cfg;
}));
}
}

View File

@@ -0,0 +1,140 @@
// Copyright (C) 2017 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.testutil;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.testutil.IndexVersions.ALL;
import static com.google.gerrit.testutil.IndexVersions.CURRENT;
import static com.google.gerrit.testutil.IndexVersions.PREVIOUS;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class IndexVersionsTest extends GerritBaseTests {
private static final ChangeSchemaDefinitions SCHEMA_DEF = ChangeSchemaDefinitions.INSTANCE;
@Test
public void noValue() {
List<Integer> expected = new ArrayList<>();
if (SCHEMA_DEF.getPrevious() != null) {
expected.add(SCHEMA_DEF.getPrevious().getVersion());
}
expected.add(SCHEMA_DEF.getLatest().getVersion());
assertThat(get(null)).containsExactlyElementsIn(expected).inOrder();
}
@Test
public void emptyValue() {
List<Integer> expected = new ArrayList<>();
if (SCHEMA_DEF.getPrevious() != null) {
expected.add(SCHEMA_DEF.getPrevious().getVersion());
}
expected.add(SCHEMA_DEF.getLatest().getVersion());
assertThat(get("")).containsExactlyElementsIn(expected).inOrder();
}
@Test
public void all() {
assertThat(get(ALL)).containsExactlyElementsIn(SCHEMA_DEF.getSchemas().keySet()).inOrder();
}
@Test
public void current() {
assertThat(get(CURRENT)).containsExactly(SCHEMA_DEF.getLatest().getVersion());
}
@Test
public void previous() {
assume().that(SCHEMA_DEF.getPrevious()).isNotNull();
assertThat(get(PREVIOUS)).containsExactly(SCHEMA_DEF.getPrevious().getVersion());
}
@Test
public void versionNumber() {
assume().that(SCHEMA_DEF.getPrevious()).isNotNull();
assertThat(get(Integer.toString(SCHEMA_DEF.getPrevious().getVersion())))
.containsExactly(SCHEMA_DEF.getPrevious().getVersion());
}
@Test
public void invalid() {
assertIllegalArgument("foo", "Invalid value for test: foo");
}
@Test
public void currentAndPrevious() {
if (SCHEMA_DEF.getPrevious() == null) {
assertIllegalArgument(CURRENT + "," + PREVIOUS, "previous version does not exist");
return;
}
assertThat(get(CURRENT + "," + PREVIOUS))
.containsExactly(SCHEMA_DEF.getLatest().getVersion(), SCHEMA_DEF.getPrevious().getVersion())
.inOrder();
assertThat(get(PREVIOUS + "," + CURRENT))
.containsExactly(SCHEMA_DEF.getPrevious().getVersion(), SCHEMA_DEF.getLatest().getVersion())
.inOrder();
}
@Test
public void currentAndVersionNumber() {
assume().that(SCHEMA_DEF.getPrevious()).isNotNull();
assertThat(get(CURRENT + "," + SCHEMA_DEF.getPrevious().getVersion()))
.containsExactly(SCHEMA_DEF.getLatest().getVersion(), SCHEMA_DEF.getPrevious().getVersion())
.inOrder();
assertThat(get(SCHEMA_DEF.getPrevious().getVersion() + "," + CURRENT))
.containsExactly(SCHEMA_DEF.getPrevious().getVersion(), SCHEMA_DEF.getLatest().getVersion())
.inOrder();
}
@Test
public void currentAndAll() {
assertIllegalArgument(CURRENT + "," + ALL, "Invalid value for test: " + ALL);
}
@Test
public void currentAndInvalid() {
assertIllegalArgument(CURRENT + ",foo", "Invalid value for test: foo");
}
@Test
public void nonExistingVersion() {
int nonExistingVersion = SCHEMA_DEF.getLatest().getVersion() + 1;
assertIllegalArgument(
Integer.toString(nonExistingVersion),
"Index version "
+ nonExistingVersion
+ " that was specified by test not found. Possible versions are: "
+ SCHEMA_DEF.getSchemas().keySet());
}
private static List<Integer> get(String value) {
return IndexVersions.get(ChangeSchemaDefinitions.INSTANCE, "test", value);
}
private void assertIllegalArgument(String value, String expectedMessage) {
exception.expect(IllegalArgumentException.class);
exception.expectMessage(expectedMessage);
get(value);
}
}