diff --git a/pom.xml b/pom.xml index 4b36f6c44ef..61966eb58a5 100644 --- a/pom.xml +++ b/pom.xml @@ -787,8 +787,6 @@ org/elasticsearch/test/test/**/* - - true @@ -1519,6 +1517,7 @@ dev-tools/forbidden/all-signatures.txt ${forbidden.test.signatures} + **.SuppressForbidden test-compile @@ -1539,6 +1538,7 @@ + rest-api-spec/**/* org/elasticsearch/test/**/* org/elasticsearch/bootstrap/BootstrapForTesting.class org/elasticsearch/common/cli/CliToolTestCase.class diff --git a/src/test/java/org/elasticsearch/test/rest/ElasticsearchRestTestCase.java b/src/test/java/org/elasticsearch/test/rest/ElasticsearchRestTestCase.java index b7b207a6b11..9661e3950c6 100644 --- a/src/test/java/org/elasticsearch/test/rest/ElasticsearchRestTestCase.java +++ b/src/test/java/org/elasticsearch/test/rest/ElasticsearchRestTestCase.java @@ -23,11 +23,14 @@ import com.carrotsearch.randomizedtesting.RandomizedTest; import com.carrotsearch.randomizedtesting.annotations.TestGroup; import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite; import com.google.common.collect.Lists; + import org.apache.lucene.util.LuceneTestCase.Slow; import org.apache.lucene.util.LuceneTestCase.SuppressCodecs; import org.apache.lucene.util.LuceneTestCase.SuppressFsync; +import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.TimeUnits; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; @@ -48,9 +51,17 @@ import org.junit.BeforeClass; import org.junit.Test; import java.io.IOException; +import java.io.InputStream; import java.lang.annotation.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; +import java.nio.file.StandardCopyOption; import java.util.*; /** @@ -158,23 +169,29 @@ public abstract class ElasticsearchRestTestCase extends ElasticsearchIntegration } private static List collectTestCandidates(int id, int count) throws RestTestParseException, IOException { - String[] paths = resolvePathsProperty(REST_TESTS_SUITE, DEFAULT_TESTS_PATH); - Map> yamlSuites = FileUtils.findYamlSuites(DEFAULT_TESTS_PATH, paths); - List testCandidates = Lists.newArrayList(); - RestTestSuiteParser restTestSuiteParser = new RestTestSuiteParser(); - //yaml suites are grouped by directory (effectively by api) - for (String api : yamlSuites.keySet()) { - List yamlFiles = Lists.newArrayList(yamlSuites.get(api)); - for (Path yamlFile : yamlFiles) { - String key = api + yamlFile.getFileName().toString(); - if (mustExecute(key, id, count)) { - RestTestSuite restTestSuite = restTestSuiteParser.parse(api, yamlFile); - for (TestSection testSection : restTestSuite.getTestSections()) { - testCandidates.add(new RestTestCandidate(restTestSuite, testSection)); + FileSystem fileSystem = getFileSystem(); + // don't make a try-with, getFileSystem returns null + // ... and you can't close() the default filesystem + try { + String[] paths = resolvePathsProperty(REST_TESTS_SUITE, DEFAULT_TESTS_PATH); + Map> yamlSuites = FileUtils.findYamlSuites(fileSystem, DEFAULT_TESTS_PATH, paths); + RestTestSuiteParser restTestSuiteParser = new RestTestSuiteParser(); + //yaml suites are grouped by directory (effectively by api) + for (String api : yamlSuites.keySet()) { + List yamlFiles = Lists.newArrayList(yamlSuites.get(api)); + for (Path yamlFile : yamlFiles) { + String key = api + yamlFile.getFileName().toString(); + if (mustExecute(key, id, count)) { + RestTestSuite restTestSuite = restTestSuiteParser.parse(api, yamlFile); + for (TestSection testSection : restTestSuite.getTestSections()) { + testCandidates.add(new RestTestCandidate(restTestSuite, testSection)); + } } } } + } finally { + IOUtils.close(fileSystem); } //sort the candidates so they will always be in the same order before being shuffled, for repeatability @@ -202,10 +219,46 @@ public abstract class ElasticsearchRestTestCase extends ElasticsearchIntegration } } + /** + * Returns a new FileSystem to read REST resources, or null if they + * are available from classpath. + */ + @SuppressForbidden(reason = "proper use of URL, hack around a JDK bug") + static FileSystem getFileSystem() throws IOException { + // REST suite handling is currently complicated, with lots of filtering and so on + // For now, to work embedded in a jar, return a ZipFileSystem over the jar contents. + URL codeLocation = FileUtils.class.getProtectionDomain().getCodeSource().getLocation(); + + if (codeLocation.getFile().endsWith(".jar")) { + try { + // hack around a bug in the zipfilesystem implementation before java 9, + // its checkWritable was incorrect and it won't work without write permissions. + // if we add the permission, it will open jars r/w, which is too scary! so copy to a safe r-w location. + Path tmp = Files.createTempFile(null, ".jar"); + try (InputStream in = codeLocation.openStream()) { + Files.copy(in, tmp, StandardCopyOption.REPLACE_EXISTING); + } + return FileSystems.newFileSystem(new URI("jar:" + tmp.toUri()), Collections.emptyMap()); + } catch (URISyntaxException e) { + throw new IOException("couldn't open zipfilesystem: ", e); + } + } else { + return null; + } + } + @BeforeClass public static void initExecutionContext() throws IOException, RestException { String[] specPaths = resolvePathsProperty(REST_TESTS_SPEC, DEFAULT_SPEC_PATH); - RestSpec restSpec = RestSpec.parseFrom(DEFAULT_SPEC_PATH, specPaths); + RestSpec restSpec = null; + FileSystem fileSystem = getFileSystem(); + // don't make a try-with, getFileSystem returns null + // ... and you can't close() the default filesystem + try { + restSpec = RestSpec.parseFrom(fileSystem, DEFAULT_SPEC_PATH, specPaths); + } finally { + IOUtils.close(fileSystem); + } validateSpec(restSpec); restTestExecutionContext = new RestTestExecutionContext(restSpec); } diff --git a/src/test/java/org/elasticsearch/test/rest/spec/RestSpec.java b/src/test/java/org/elasticsearch/test/rest/spec/RestSpec.java index 34b6315fc2e..979bacc26c2 100644 --- a/src/test/java/org/elasticsearch/test/rest/spec/RestSpec.java +++ b/src/test/java/org/elasticsearch/test/rest/spec/RestSpec.java @@ -19,12 +19,14 @@ package org.elasticsearch.test.rest.spec; import com.google.common.collect.Maps; + import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.test.rest.support.FileUtils; import java.io.IOException; import java.io.InputStream; +import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; @@ -54,10 +56,10 @@ public class RestSpec { /** * Parses the complete set of REST spec available under the provided directories */ - public static RestSpec parseFrom(String optionalPathPrefix, String... paths) throws IOException { + public static RestSpec parseFrom(FileSystem fileSystem, String optionalPathPrefix, String... paths) throws IOException { RestSpec restSpec = new RestSpec(); for (String path : paths) { - for (Path jsonFile : FileUtils.findJsonSpec(optionalPathPrefix, path)) { + for (Path jsonFile : FileUtils.findJsonSpec(fileSystem, optionalPathPrefix, path)) { try (InputStream stream = Files.newInputStream(jsonFile)) { XContentParser parser = JsonXContent.jsonXContent.createParser(stream); RestApi restApi = new RestApiParser().parse(parser); diff --git a/src/test/java/org/elasticsearch/test/rest/support/FileUtils.java b/src/test/java/org/elasticsearch/test/rest/support/FileUtils.java index 28c3f597f9f..5e230a6f993 100644 --- a/src/test/java/org/elasticsearch/test/rest/support/FileUtils.java +++ b/src/test/java/org/elasticsearch/test/rest/support/FileUtils.java @@ -25,17 +25,14 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.PathUtils; import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.nio.file.DirectoryStream; -import java.nio.file.FileSystems; +import java.nio.file.FileSystem; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.NotDirectoryException; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.HashSet; @@ -53,10 +50,10 @@ public final class FileUtils { /** * Returns the json files found within the directory provided as argument. - * Files are looked up in the classpath first, then outside of it if not found. + * Files are looked up in the classpath, or optionally from {@code fileSystem} if its not null. */ - public static Set findJsonSpec(String optionalPathPrefix, String path) throws IOException { - Path dir = resolveFile(optionalPathPrefix, path, null); + public static Set findJsonSpec(FileSystem fileSystem, String optionalPathPrefix, String path) throws IOException { + Path dir = resolveFile(fileSystem, optionalPathPrefix, path, null); if (!Files.isDirectory(dir)) { throw new NotDirectoryException(path); @@ -81,37 +78,46 @@ public final class FileUtils { /** * Returns the yaml files found within the paths provided. * Each input path can either be a single file (the .yaml suffix is optional) or a directory. - * Each path is looked up in the classpath first, then outside of it if not found yet. + * Each path is looked up in the classpath, or optionally from {@code fileSystem} if its not null. */ - public static Map> findYamlSuites(final String optionalPathPrefix, final String... paths) throws IOException { + public static Map> findYamlSuites(FileSystem fileSystem, String optionalPathPrefix, final String... paths) throws IOException { Map> yamlSuites = Maps.newHashMap(); for (String path : paths) { - collectFiles(resolveFile(optionalPathPrefix, path, YAML_SUFFIX), YAML_SUFFIX, yamlSuites); + collectFiles(resolveFile(fileSystem, optionalPathPrefix, path, YAML_SUFFIX), YAML_SUFFIX, yamlSuites); } return yamlSuites; } - private static Path resolveFile(String optionalPathPrefix, String path, String optionalFileSuffix) throws IOException { - //try within classpath with and without file suffix (as it could be a single test suite) - URL resource = findResource(path, optionalFileSuffix); - if (resource == null) { - //try within classpath with optional prefix: /rest-api-spec/test (or /rest-api-spec/api) is optional - String newPath = optionalPathPrefix + "/" + path; - resource = findResource(newPath, optionalFileSuffix); - if (resource == null) { - //if it wasn't on classpath we look outside of the classpath - Path file = findFile(path, optionalFileSuffix); - if (!Files.exists(file)) { + private static Path resolveFile(FileSystem fileSystem, String optionalPathPrefix, String path, String optionalFileSuffix) throws IOException { + if (fileSystem != null) { + Path file = findFile(fileSystem, path, optionalFileSuffix); + if (!lenientExists(file)) { + // try with optional prefix: /rest-api-spec/test (or /rest-api-spec/api) is optional + String newPath = optionalPathPrefix + "/" + path; + file = findFile(fileSystem, newPath, optionalFileSuffix); + if (!lenientExists(file)) { throw new NoSuchFileException(path); } - return file; } - } - - try { - return PathUtils.get(resource.toURI()); - } catch (URISyntaxException e) { - throw new RuntimeException(e); + return file; + } else { + //try within classpath + URL resource = findResource(path, optionalFileSuffix); + if (resource == null) { + //try within classpath with optional prefix: /rest-api-spec/test (or /rest-api-spec/api) is optional + String newPath = optionalPathPrefix + "/" + path; + resource = findResource(newPath, optionalFileSuffix); + if (resource == null) { + throw new NoSuchFileException(path); + } + } + try { + return PathUtils.get(resource.toURI()); + } catch (Exception e) { + // some filesystems have REALLY useless exceptions here. + // ZipFileSystem I am looking at you. + throw new RuntimeException("couldn't retrieve URL: " + resource, e); + } } } @@ -125,11 +131,20 @@ public final class FileUtils { } return resource; } + + // used because this test "guesses" from like 4 different places from the filesystem! + private static boolean lenientExists(Path file) { + boolean exists = false; + try { + exists = Files.exists(file); + } catch (SecurityException ok) {} + return exists; + } - private static Path findFile(String path, String optionalFileSuffix) { - Path file = PathUtils.get(path); - if (!Files.exists(file)) { - file = PathUtils.get(path + optionalFileSuffix); + private static Path findFile(FileSystem fileSystem, String path, String optionalFileSuffix) { + Path file = fileSystem.getPath(path); + if (!lenientExists(file)) { + file = fileSystem.getPath(path + optionalFileSuffix); } return file; } diff --git a/src/test/java/org/elasticsearch/test/rest/test/FileUtilsTests.java b/src/test/java/org/elasticsearch/test/rest/test/FileUtilsTests.java index f35daa926a1..10db051e2b1 100644 --- a/src/test/java/org/elasticsearch/test/rest/test/FileUtilsTests.java +++ b/src/test/java/org/elasticsearch/test/rest/test/FileUtilsTests.java @@ -35,29 +35,29 @@ public class FileUtilsTests extends ElasticsearchTestCase { @Test public void testLoadSingleYamlSuite() throws Exception { - Map> yamlSuites = FileUtils.findYamlSuites("/rest-api-spec/test", "/rest-api-spec/test/get/10_basic"); + Map> yamlSuites = FileUtils.findYamlSuites(null, "/rest-api-spec/test", "/rest-api-spec/test/get/10_basic"); assertSingleFile(yamlSuites, "get", "10_basic.yaml"); //the path prefix is optional - yamlSuites = FileUtils.findYamlSuites("/rest-api-spec/test", "get/10_basic.yaml"); + yamlSuites = FileUtils.findYamlSuites(null, "/rest-api-spec/test", "get/10_basic.yaml"); assertSingleFile(yamlSuites, "get", "10_basic.yaml"); //extension .yaml is optional - yamlSuites = FileUtils.findYamlSuites("/rest-api-spec/test", "get/10_basic"); + yamlSuites = FileUtils.findYamlSuites(null, "/rest-api-spec/test", "get/10_basic"); assertSingleFile(yamlSuites, "get", "10_basic.yaml"); } @Test public void testLoadMultipleYamlSuites() throws Exception { //single directory - Map> yamlSuites = FileUtils.findYamlSuites("/rest-api-spec/test", "get"); + Map> yamlSuites = FileUtils.findYamlSuites(null, "/rest-api-spec/test", "get"); assertThat(yamlSuites, notNullValue()); assertThat(yamlSuites.size(), equalTo(1)); assertThat(yamlSuites.containsKey("get"), equalTo(true)); assertThat(yamlSuites.get("get").size(), greaterThan(1)); //multiple directories - yamlSuites = FileUtils.findYamlSuites("/rest-api-spec/test", "get", "index"); + yamlSuites = FileUtils.findYamlSuites(null, "/rest-api-spec/test", "get", "index"); assertThat(yamlSuites, notNullValue()); assertThat(yamlSuites.size(), equalTo(2)); assertThat(yamlSuites.containsKey("get"), equalTo(true)); @@ -66,7 +66,7 @@ public class FileUtilsTests extends ElasticsearchTestCase { assertThat(yamlSuites.get("index").size(), greaterThan(1)); //multiple paths, which can be both directories or yaml test suites (with optional file extension) - yamlSuites = FileUtils.findYamlSuites("/rest-api-spec/test", "indices.optimize/10_basic", "index"); + yamlSuites = FileUtils.findYamlSuites(null, "/rest-api-spec/test", "indices.optimize/10_basic", "index"); assertThat(yamlSuites, notNullValue()); assertThat(yamlSuites.size(), equalTo(2)); assertThat(yamlSuites.containsKey("indices.optimize"), equalTo(true)); @@ -81,22 +81,16 @@ public class FileUtilsTests extends ElasticsearchTestCase { Files.createFile(file); //load from directory outside of the classpath - yamlSuites = FileUtils.findYamlSuites("/rest-api-spec/test", "get/10_basic", dir.toAbsolutePath().toString()); + yamlSuites = FileUtils.findYamlSuites(dir.getFileSystem(), "/rest-api-spec/test", dir.toAbsolutePath().toString()); assertThat(yamlSuites, notNullValue()); - assertThat(yamlSuites.size(), equalTo(2)); - assertThat(yamlSuites.containsKey("get"), equalTo(true)); - assertThat(yamlSuites.get("get").size(), equalTo(1)); - assertSingleFile(yamlSuites.get("get"), "get", "10_basic.yaml"); + assertThat(yamlSuites.size(), equalTo(1)); assertThat(yamlSuites.containsKey(dir.getFileName().toString()), equalTo(true)); assertSingleFile(yamlSuites.get(dir.getFileName().toString()), dir.getFileName().toString(), file.getFileName().toString()); //load from external file (optional extension) - yamlSuites = FileUtils.findYamlSuites("/rest-api-spec/test", "get/10_basic", dir.resolve("test_loading").toAbsolutePath().toString()); + yamlSuites = FileUtils.findYamlSuites(dir.getFileSystem(), "/rest-api-spec/test", dir.resolve("test_loading").toAbsolutePath().toString()); assertThat(yamlSuites, notNullValue()); - assertThat(yamlSuites.size(), equalTo(2)); - assertThat(yamlSuites.containsKey("get"), equalTo(true)); - assertThat(yamlSuites.get("get").size(), equalTo(1)); - assertSingleFile(yamlSuites.get("get"), "get", "10_basic.yaml"); + assertThat(yamlSuites.size(), equalTo(1)); assertThat(yamlSuites.containsKey(dir.getFileName().toString()), equalTo(true)); assertSingleFile(yamlSuites.get(dir.getFileName().toString()), dir.getFileName().toString(), file.getFileName().toString()); }