Merge branch 'stable-3.0' into stable-3.1

* stable-3.0:
  Disk cache metrics require cache.enableDiskStatMetrics
  Set version to 2.14.22
  ElasticContainer: Upgrade V6_8 to elasticsearch 6.8.13
  Upgrade testcontainers to 1.15.0
  Workaround Gitiles bug on All-Users visibility
  Validate Gerrit changes on stable-2.15 with Jenkins
  Set version to 2.15.22-SNAPSHOT
  Set version to 3.0.16-SNAPSHOT
  Set version to 2.15.21
  Set version to 3.0.15
  Set version to 2.16.26-SNAPSHOT
  Set version to 2.16.25
  Workaround Gitiles bug on All-Users visibility
  Workaround Gitiles bug on All-Users visibility
  Workaround Gitiles bug on All-Users visibility
  Set version to 2.15.21-SNAPSHOT
  Set version to 2.15.20
  Fetch JGit documentation from the archive site
  Remove generation for c.g.gwtexpui.* JavaDoc
  Set version to 3.0.15-SNAPSHOT
  Set version to 2.16.25-SNAPSHOT
  Set version to 3.0.14
  Set version to 2.16.24
  Make PermissionBackend#ForRef authoritative
  Validate Gerrit changes on stable-2.15 with Jenkins
  Fix tests for stable-2.15 branch
  Make PermissionBackend#ForRef authoritative
  Make PermissionBackend#ForRef authoritative

Change-Id: I04b831523d35856d88718f31ccb9005911ec64bd
diff --git a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
index 8ed9729..0d1f9cd 100644
--- a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
+++ b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
@@ -32,6 +32,7 @@
 import com.google.gerrit.elasticsearch.builders.QueryBuilder;
 import com.google.gerrit.elasticsearch.builders.SearchSourceBuilder;
 import com.google.gerrit.elasticsearch.bulk.DeleteRequest;
+import com.google.gerrit.entities.converter.ProtoConverter;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.FieldType;
@@ -45,7 +46,6 @@
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.index.query.ResultSet;
 import com.google.gerrit.proto.Protos;
-import com.google.gerrit.reviewdb.converter.ProtoConverter;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.index.IndexUtils;
 import com.google.gson.Gson;
diff --git a/java/com/google/gerrit/elasticsearch/BUILD b/java/com/google/gerrit/elasticsearch/BUILD
index a9b145b..edbd82c 100644
--- a/java/com/google/gerrit/elasticsearch/BUILD
+++ b/java/com/google/gerrit/elasticsearch/BUILD
@@ -6,6 +6,7 @@
     visibility = ["//visibility:public"],
     deps = [
         "//java/com/google/gerrit/common:annotations",
+        "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/index",
@@ -13,10 +14,10 @@
         "//java/com/google/gerrit/index/project",
         "//java/com/google/gerrit/lifecycle",
         "//java/com/google/gerrit/proto",
-        "//java/com/google/gerrit/reviewdb:server",
         "//java/com/google/gerrit/server",
         "//lib:gson",
         "//lib:guava",
+        "//lib:jgit",
         "//lib:protobuf",
         "//lib/commons:codec",
         "//lib/commons:lang",
@@ -29,6 +30,5 @@
         "//lib/httpcomponents:httpcore",
         "//lib/httpcomponents:httpcore-nio",
         "//lib/jackson:jackson-core",
-        "//lib/jgit/org.eclipse.jgit:jgit",
     ],
 )
diff --git a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
index f646efc..374120e 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
@@ -14,19 +14,17 @@
 
 package com.google.gerrit.elasticsearch;
 
-import static com.google.gerrit.server.index.account.AccountField.ID;
-
 import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
 import com.google.gerrit.elasticsearch.bulk.BulkRequest;
 import com.google.gerrit.elasticsearch.bulk.IndexRequest;
 import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.entities.Account;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.query.DataSource;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.config.SitePaths;
@@ -83,15 +81,23 @@
       throw new StorageException(
           String.format(
               "Failed to replace account %s in index %s: %s",
-              as.getAccount().getId(), indexName, statusCode));
+              as.account().id(), indexName, statusCode));
     }
   }
 
   @Override
   public DataSource<AccountState> getSource(Predicate<AccountState> p, QueryOptions opts)
       throws QueryParseException {
-    JsonArray sortArray = getSortArray(AccountField.ID.getName());
-    return new ElasticQuerySource(p, opts.filterFields(IndexUtils::accountFields), type, sortArray);
+    JsonArray sortArray =
+        getSortArray(
+            schema.useLegacyNumericFields()
+                ? AccountField.ID.getName()
+                : AccountField.ID_STR.getName());
+    return new ElasticQuerySource(
+        p,
+        opts.filterFields(o -> IndexUtils.accountFields(o, schema.useLegacyNumericFields())),
+        type,
+        sortArray);
   }
 
   @Override
@@ -106,7 +112,7 @@
 
   @Override
   protected String getId(AccountState as) {
-    return as.getAccount().getId().toString();
+    return as.account().id().toString();
   }
 
   @Override
@@ -116,7 +122,15 @@
       source = json.getAsJsonObject().get("fields");
     }
 
-    Account.Id id = new Account.Id(source.getAsJsonObject().get(ID.getName()).getAsInt());
+    Account.Id id =
+        Account.id(
+            source
+                .getAsJsonObject()
+                .get(
+                    schema.useLegacyNumericFields()
+                        ? AccountField.ID.getName()
+                        : AccountField.ID_STR.getName())
+                .getAsInt());
     // Use the AccountCache rather than depending on any stored fields in the document (of which
     // there shouldn't be any). The most expensive part to compute anyway is the effective group
     // IDs, and we don't have a good way to reindex when those change.
diff --git a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
index ca2b1a8..a658400 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
@@ -30,18 +30,19 @@
 import com.google.gerrit.elasticsearch.bulk.BulkRequest;
 import com.google.gerrit.elasticsearch.bulk.IndexRequest;
 import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.converter.ChangeProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetProtoConverter;
 import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.query.DataSource;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.converter.ChangeProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetApprovalProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetProtoConverter;
 import com.google.gerrit.server.ReviewerByEmailSet;
 import com.google.gerrit.server.ReviewerSet;
 import com.google.gerrit.server.StarredChangesUtil;
@@ -87,6 +88,7 @@
   private final ChangeMapping mapping;
   private final ChangeData.Factory changeDataFactory;
   private final Schema<ChangeData> schema;
+  private final FieldDef<ChangeData, ?> idField;
 
   @Inject
   ElasticChangeIndex(
@@ -98,7 +100,9 @@
     super(cfg, sitePaths, schema, clientBuilder, CHANGES);
     this.changeDataFactory = changeDataFactory;
     this.schema = schema;
-    mapping = new ChangeMapping(schema, client.adapter());
+    this.mapping = new ChangeMapping(schema, client.adapter());
+    this.idField =
+        this.schema.useLegacyNumericFields() ? ChangeField.LEGACY_ID : ChangeField.LEGACY_ID_STR;
   }
 
   @Override
@@ -136,7 +140,8 @@
       }
     }
 
-    QueryOptions filteredOpts = opts.filterFields(IndexUtils::changeFields);
+    QueryOptions filteredOpts =
+        opts.filterFields(o -> IndexUtils.changeFields(o, schema.useLegacyNumericFields()));
     return new ElasticQuerySource(p, filteredOpts, getURI(indexes), getSortArray());
   }
 
@@ -146,7 +151,7 @@
 
     JsonArray sortArray = new JsonArray();
     addNamedElement(ChangeField.UPDATED.getName(), properties, sortArray);
-    addNamedElement(ChangeField.LEGACY_ID.getName(), properties, sortArray);
+    addNamedElement(idField.getName(), properties, sortArray);
     return sortArray;
   }
 
@@ -179,10 +184,10 @@
     JsonElement c = source.get(ChangeField.CHANGE.getName());
 
     if (c == null) {
-      int id = source.get(ChangeField.LEGACY_ID.getName()).getAsInt();
+      int id = source.get(idField.getName()).getAsInt();
       // IndexUtils#changeFields ensures either CHANGE or PROJECT is always present.
       String projectName = requireNonNull(source.get(ChangeField.PROJECT.getName()).getAsString());
-      return changeDataFactory.create(new Project.NameKey(projectName), new Change.Id(id));
+      return changeDataFactory.create(Project.nameKey(projectName), Change.id(id));
     }
 
     ChangeData cd =
@@ -252,7 +257,7 @@
           if (reviewedBy.size() == 1 && aId == ChangeField.NOT_REVIEWED) {
             break;
           }
-          accounts.add(new Account.Id(aId));
+          accounts.add(Account.id(aId));
         }
         cd.setReviewedBy(accounts);
       }
diff --git a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
index eff3d52..3922f89 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
@@ -18,13 +18,13 @@
 import com.google.gerrit.elasticsearch.bulk.BulkRequest;
 import com.google.gerrit.elasticsearch.bulk.IndexRequest;
 import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.query.DataSource;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.group.InternalGroup;
@@ -116,8 +116,7 @@
     }
 
     AccountGroup.UUID uuid =
-        new AccountGroup.UUID(
-            source.getAsJsonObject().get(GroupField.UUID.getName()).getAsString());
+        AccountGroup.uuid(source.getAsJsonObject().get(GroupField.UUID.getName()).getAsString());
     // Use the GroupCache rather than depending on any stored fields in the
     // document (of which there shouldn't be any).
     return groupCache.get().get(uuid).orElse(null);
diff --git a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
index a0ebb07..7e45f4f 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.elasticsearch.bulk.BulkRequest;
 import com.google.gerrit.elasticsearch.bulk.IndexRequest;
 import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
@@ -27,7 +28,6 @@
 import com.google.gerrit.index.query.DataSource;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.index.IndexUtils;
 import com.google.gerrit.server.project.ProjectCache;
@@ -117,8 +117,7 @@
     }
 
     Project.NameKey nameKey =
-        new Project.NameKey(
-            source.getAsJsonObject().get(ProjectField.NAME.getName()).getAsString());
+        Project.nameKey(source.getAsJsonObject().get(ProjectField.NAME.getName()).getAsString());
     return projectCache.get().get(nameKey).toProjectData();
   }
 }
diff --git a/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java b/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java
index 394158d..d05e91c 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java
@@ -145,7 +145,7 @@
     String name = p.getField().getName();
     String value = p.getValue();
 
-    if (value.isEmpty()) {
+    if (!p.getField().isRepeatable() && value.isEmpty()) {
       return new BoolQueryBuilder().mustNot(QueryBuilders.existsQuery(name));
     } else if (p instanceof RegexPredicate) {
       if (value.startsWith("^")) {
diff --git a/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java b/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java
index a693f6d..2f0bd01 100644
--- a/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java
+++ b/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java
@@ -40,11 +40,7 @@
       for (Values<V> values : schema.buildFields(v)) {
         String name = values.getField().getName();
         if (values.getField().isRepeatable()) {
-          builder.field(
-              name,
-              Streams.stream(values.getValues())
-                  .filter(e -> shouldAddElement(e))
-                  .collect(toList()));
+          builder.field(name, Streams.stream(values.getValues()).collect(toList()));
         } else {
           Object element = Iterables.getOnlyElement(values.getValues(), "");
           if (shouldAddElement(element)) {
diff --git a/java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java b/java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java
deleted file mode 100644
index d886baf..0000000
--- a/java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2019 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.server.query.change;
-
-import com.google.gerrit.index.query.PostFilterPredicate;
-import com.google.gerrit.server.index.change.ChangeField;
-
-public class FileWithNoExtensionInElasticPredicate extends PostFilterPredicate<ChangeData> {
-
-  private static final String NO_EXT = "";
-
-  public FileWithNoExtensionInElasticPredicate() {
-    super(ChangeField.EXTENSION.getName(), NO_EXT);
-  }
-
-  @Override
-  public boolean match(ChangeData cd) {
-    return ChangeField.getExtensions(cd).contains(NO_EXT);
-  }
-
-  @Override
-  public int getCost() {
-    return 1;
-  }
-}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java b/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
index 339e8ae..43a5ceb 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
@@ -18,10 +18,12 @@
 import static com.google.gerrit.elasticsearch.ElasticTestUtils.getConfig;
 
 import com.google.gerrit.elasticsearch.ElasticVersion;
+import com.google.gerrit.index.IndexType;
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.inject.Injector;
 import org.eclipse.jgit.lib.Config;
 
+/** Tests for every supported {@link IndexType#isElasticsearch()} most recent index version. */
 public class ElasticIndexIT extends AbstractIndexTests {
 
   @ConfigSuite.Default
diff --git a/javatests/com/google/gerrit/elasticsearch/BUILD b/javatests/com/google/gerrit/elasticsearch/BUILD
index e2a8483..e269fc2 100644
--- a/javatests/com/google/gerrit/elasticsearch/BUILD
+++ b/javatests/com/google/gerrit/elasticsearch/BUILD
@@ -12,13 +12,12 @@
     deps = [
         "//java/com/google/gerrit/elasticsearch",
         "//java/com/google/gerrit/index",
-        "//java/com/google/gerrit/server",
         "//lib:guava",
+        "//lib:jgit",
         "//lib:junit",
         "//lib/guice",
         "//lib/httpcomponents:httpcore",
         "//lib/jackson:jackson-annotations",
-        "//lib/jgit/org.eclipse.jgit:jgit",
         "//lib/log:api",
         "//lib/testcontainers",
         "//lib/testcontainers:docker-java-api",
@@ -32,7 +31,7 @@
     "//java/com/google/gerrit/elasticsearch",
     "//java/com/google/gerrit/testing:gerrit-test-util",
     "//lib/guice",
-    "//lib/jgit/org.eclipse.jgit:jgit",
+    "//lib:jgit",
 ]
 
 HTTP_TEST_DEPS = [
@@ -89,9 +88,9 @@
         "//java/com/google/gerrit/elasticsearch",
         "//java/com/google/gerrit/testing:gerrit-test-util",
         "//lib:guava",
+        "//lib:jgit",
         "//lib/guice",
         "//lib/httpcomponents:httpcore",
-        "//lib/jgit/org.eclipse.jgit:jgit",
         "//lib/truth",
     ],
 )
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java
index 9ce1456..7e044c3 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java
@@ -21,17 +21,17 @@
 import static com.google.gerrit.elasticsearch.ElasticConfiguration.KEY_SERVER;
 import static com.google.gerrit.elasticsearch.ElasticConfiguration.KEY_USERNAME;
 import static com.google.gerrit.elasticsearch.ElasticConfiguration.SECTION_ELASTICSEARCH;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 import static java.util.stream.Collectors.toList;
 
 import com.google.common.collect.ImmutableList;
-import com.google.gerrit.testing.GerritBaseTests;
 import com.google.inject.ProvisionException;
 import java.util.Arrays;
 import org.apache.http.HttpHost;
 import org.eclipse.jgit.lib.Config;
 import org.junit.Test;
 
-public class ElasticConfigurationTest extends GerritBaseTests {
+public class ElasticConfigurationTest {
   @Test
   public void singleServerNoOtherConfig() throws Exception {
     Config cfg = newConfig();
@@ -121,9 +121,9 @@
         .containsExactly(hostURIs);
   }
 
-  private void assertProvisionException(Config cfg) throws Exception {
-    exception.expect(ProvisionException.class);
-    exception.expectMessage("No valid Elasticsearch servers configured");
-    new ElasticConfiguration(cfg);
+  private void assertProvisionException(Config cfg) {
+    ProvisionException thrown =
+        assertThrows(ProvisionException.class, () -> new ElasticConfiguration(cfg));
+    assertThat(thrown).hasMessageThat().contains("No valid Elasticsearch servers configured");
   }
 }
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java b/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java
index 0203a22..dcc6880 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.elasticsearch;
 
 import com.google.gerrit.index.IndexDefinition;
-import com.google.gerrit.server.index.IndexModule.IndexType;
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.TypeLiteral;
@@ -27,7 +26,7 @@
   public static void configure(Config config, ElasticContainer container, String prefix) {
     String hostname = container.getHttpHost().getHostName();
     int port = container.getHttpHost().getPort();
-    config.setEnum("index", null, "type", IndexType.ELASTICSEARCH);
+    config.setString("index", null, "type", "elasticsearch");
     config.setString("elasticsearch", null, "server", "http://" + hostname + ":" + port);
     config.setString("elasticsearch", null, "prefix", prefix);
     config.setInt("index", null, "maxLimit", 10000);
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
index d4d321d..15d8dd6 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
@@ -57,7 +57,7 @@
   protected Injector createInjector() {
     Config elasticsearchConfig = new Config(config);
     InMemoryModule.setDefaults(elasticsearchConfig);
-    String indicesPrefix = getSanitizedMethodName();
+    String indicesPrefix = testName.getSanitizedMethodName();
     ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
     return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
   }
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
index 68c4b71..d734f1e 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
@@ -14,8 +14,11 @@
 
 package com.google.gerrit.elasticsearch;
 
+import static java.util.concurrent.TimeUnit.MINUTES;
+
 import com.google.gerrit.server.query.change.AbstractQueryChangesTest;
 import com.google.gerrit.testing.ConfigSuite;
+import com.google.gerrit.testing.GerritTestName;
 import com.google.gerrit.testing.InMemoryModule;
 import com.google.gerrit.testing.IndexConfig;
 import com.google.inject.Guice;
@@ -28,6 +31,7 @@
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
+import org.junit.Rule;
 
 public class ElasticV6QueryChangesTest extends AbstractQueryChangesTest {
   @ConfigSuite.Default
@@ -55,19 +59,23 @@
     }
   }
 
+  @Rule public final GerritTestName testName = new GerritTestName();
+
   @After
-  public void closeIndex() {
+  public void closeIndex() throws Exception {
     // Close the index after each test to prevent exceeding Elasticsearch's
     // shard limit (see Issue 10120).
-    client.execute(
-        new HttpPost(
-            String.format(
-                "http://%s:%d/%s*/_close",
-                container.getHttpHost().getHostName(),
-                container.getHttpHost().getPort(),
-                getSanitizedMethodName())),
-        HttpClientContext.create(),
-        null);
+    client
+        .execute(
+            new HttpPost(
+                String.format(
+                    "http://%s:%d/%s*/_close",
+                    container.getHttpHost().getHostName(),
+                    container.getHttpHost().getPort(),
+                    testName.getSanitizedMethodName())),
+            HttpClientContext.create(),
+            null)
+        .get(5, MINUTES);
   }
 
   @Override
@@ -80,7 +88,7 @@
   protected Injector createInjector() {
     Config elasticsearchConfig = new Config(config);
     InMemoryModule.setDefaults(elasticsearchConfig);
-    String indicesPrefix = getSanitizedMethodName();
+    String indicesPrefix = testName.getSanitizedMethodName();
     ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
     return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
   }
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
index 99c07f4..28d798e 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
@@ -57,7 +57,7 @@
   protected Injector createInjector() {
     Config elasticsearchConfig = new Config(config);
     InMemoryModule.setDefaults(elasticsearchConfig);
-    String indicesPrefix = getSanitizedMethodName();
+    String indicesPrefix = testName.getSanitizedMethodName();
     ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
     return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
   }
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
index 89c7774..6658d72 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
@@ -57,7 +57,7 @@
   protected Injector createInjector() {
     Config elasticsearchConfig = new Config(config);
     InMemoryModule.setDefaults(elasticsearchConfig);
-    String indicesPrefix = getSanitizedMethodName();
+    String indicesPrefix = testName.getSanitizedMethodName();
     ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
     return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
   }
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java
index 91d366e..4826490 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java
@@ -57,7 +57,7 @@
   protected Injector createInjector() {
     Config elasticsearchConfig = new Config(config);
     InMemoryModule.setDefaults(elasticsearchConfig);
-    String indicesPrefix = getSanitizedMethodName();
+    String indicesPrefix = testName.getSanitizedMethodName();
     ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
     return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
   }
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java
index f11dc5b..d9a4d2e 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java
@@ -14,8 +14,11 @@
 
 package com.google.gerrit.elasticsearch;
 
+import static java.util.concurrent.TimeUnit.MINUTES;
+
 import com.google.gerrit.server.query.change.AbstractQueryChangesTest;
 import com.google.gerrit.testing.ConfigSuite;
+import com.google.gerrit.testing.GerritTestName;
 import com.google.gerrit.testing.InMemoryModule;
 import com.google.gerrit.testing.IndexConfig;
 import com.google.inject.Guice;
@@ -28,6 +31,7 @@
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
+import org.junit.Rule;
 
 public class ElasticV7QueryChangesTest extends AbstractQueryChangesTest {
   @ConfigSuite.Default
@@ -55,19 +59,23 @@
     }
   }
 
+  @Rule public final GerritTestName testName = new GerritTestName();
+
   @After
-  public void closeIndex() {
+  public void closeIndex() throws Exception {
     // Close the index after each test to prevent exceeding Elasticsearch's
     // shard limit (see Issue 10120).
-    client.execute(
-        new HttpPost(
-            String.format(
-                "http://%s:%d/%s*/_close",
-                container.getHttpHost().getHostName(),
-                container.getHttpHost().getPort(),
-                getSanitizedMethodName())),
-        HttpClientContext.create(),
-        null);
+    client
+        .execute(
+            new HttpPost(
+                String.format(
+                    "http://%s:%d/%s*/_close",
+                    container.getHttpHost().getHostName(),
+                    container.getHttpHost().getPort(),
+                    testName.getSanitizedMethodName())),
+            HttpClientContext.create(),
+            null)
+        .get(5, MINUTES);
   }
 
   @Override
@@ -80,7 +88,7 @@
   protected Injector createInjector() {
     Config elasticsearchConfig = new Config(config);
     InMemoryModule.setDefaults(elasticsearchConfig);
-    String indicesPrefix = getSanitizedMethodName();
+    String indicesPrefix = testName.getSanitizedMethodName();
     ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
     return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
   }
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java
index 282763a..0fc96f8 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java
@@ -57,7 +57,7 @@
   protected Injector createInjector() {
     Config elasticsearchConfig = new Config(config);
     InMemoryModule.setDefaults(elasticsearchConfig);
-    String indicesPrefix = getSanitizedMethodName();
+    String indicesPrefix = testName.getSanitizedMethodName();
     ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
     return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
   }
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java
index 4d83cf5..1e56af9 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java
@@ -57,7 +57,7 @@
   protected Injector createInjector() {
     Config elasticsearchConfig = new Config(config);
     InMemoryModule.setDefaults(elasticsearchConfig);
-    String indicesPrefix = getSanitizedMethodName();
+    String indicesPrefix = testName.getSanitizedMethodName();
     ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
     return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
   }
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
index 0a7a422..9325a1b 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
@@ -15,11 +15,11 @@
 package com.google.gerrit.elasticsearch;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
-import com.google.gerrit.testing.GerritBaseTests;
 import org.junit.Test;
 
-public class ElasticVersionTest extends GerritBaseTests {
+public class ElasticVersionTest {
   @Test
   public void supportedVersion() throws Exception {
     assertThat(ElasticVersion.forVersion("6.8.0")).isEqualTo(ElasticVersion.V6_8);
@@ -55,10 +55,14 @@
 
   @Test
   public void unsupportedVersion() throws Exception {
-    exception.expect(ElasticVersion.UnsupportedVersion.class);
-    exception.expectMessage(
-        "Unsupported version: [4.0.0]. Supported versions: " + ElasticVersion.supportedVersions());
-    ElasticVersion.forVersion("4.0.0");
+    ElasticVersion.UnsupportedVersion thrown =
+        assertThrows(
+            ElasticVersion.UnsupportedVersion.class, () -> ElasticVersion.forVersion("4.0.0"));
+    assertThat(thrown)
+        .hasMessageThat()
+        .contains(
+            "Unsupported version: [4.0.0]. Supported versions: "
+                + ElasticVersion.supportedVersions());
   }
 
   @Test