Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read the full REST spec from a common directory #51794

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,21 @@ class RestIntegTestTask extends DefaultTask {
runner.systemProperty('test.clustername', System.getProperty("tests.clustername"))
}

// copy the rest spec/tests onto the test classpath
Copy copyRestSpec = createCopyRestSpecTask()
// copy the full rest spec (including modules, plugins, and x-pack) to a common location
Copy link
Contributor

@mark-vieira mark-vieira Feb 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably don't want this logic to live in this task, for several reasons:

  1. We shouldn't have any logic in task constructors to begin with. We actually have an existing issue to refactor this task. This pattern is a big Gradle no-no.
  2. We create lots of these tasks, across many projects, so we'll end up creating a set of copy tasks for each project which defines a RestIntegTestTask. As you can see in this scan, we end up copying this stuff 64 separate times. Presumably, all to the same location.
  3. All these copy tasks share the same copy destination which is going to cause all sorts of issues in parallel builds with concurrently executing copy tasks clobbering eachothers output.
  4. Gradle will actually issue warnings because of issue (3).
  5. We use RestIntegTestTask whenever we want to run integration tests against an external cluster. Firstly, not all these are YAML tests, and second, not all projects that use YAML tests also necessarily need to full rest test suite. So essentially, we would be copying these rest specs all over the place for test suites that don't need them.

Here's what I would suggest we do instead.

  1. Create a new plugin that we apply to the root project only to set all this stuff up. This ensure we only ever do this once. We could create this as a binary plugin but given the way we set this stuff up I'm inclined to start as a script plugin. That is. create a rest-test-spec.gradle file in the gradle folder that we then appy to the root build script via appy from: 'gradle/rest-test-spect.gradle.
  2. Define the output of all these copy/unpack tasks as a build artifact. This will allow projects that need it to depend on it, which is the correct way of modeling this, and also ensures stuff gets built only if necessary, and in the correct order. We can actually declare a directory as an artifact (this is how compiled classes are depended on in fact), and you can see an example of that here.
  3. We then in projects that require this stuff, declare a dependency on this stuff. Since we just need this stuff on the test runtime classpath, we'd so something like testRuntime project(path: ':', configuration: 'test-rest-spec') and then all this stuff will magically be copied and added to the target projects test runtime classpath. No more copying to output directories.

Please feel free to change any of these names to something that makes sense and reach out for questions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @mark-vieira, I exactly wasn't sure how to approach this (and still learning Gradle) so this helps alot.

// optionally copy the core rest tests to project resource dir
File currentCopyTo = new File(project.rootProject.buildDir, "rest-spec-api-current")
runner.nonInputProperties.systemProperty('tests.rest.spec_root', currentCopyTo)
Copy copyRestSpec = createCopyRestSpecTask(currentCopyTo)
Copy copyModuleRestSpec = createCopyModulesRestSpecTask(currentCopyTo)
Copy copyPluginRestSpec = createCopyPluginsRestSpecTask(currentCopyTo)
Copy copyXpackPluginRestSpec = createCopyXpackPluginsRestSpecTask(currentCopyTo)
copyModuleRestSpec.dependsOn(copyRestSpec)
copyPluginRestSpec.dependsOn(copyRestSpec)
copyXpackPluginRestSpec.dependsOn(copyRestSpec)
project.sourceSets.test.output.builtBy(copyRestSpec)
project.sourceSets.test.output.builtBy(copyModuleRestSpec)
project.sourceSets.test.output.builtBy(copyPluginRestSpec)
project.sourceSets.test.output.builtBy(copyXpackPluginRestSpec)

// this must run after all projects have been configured, so we know any project
// references can be accessed as a fully configured
Expand All @@ -88,7 +100,6 @@ class RestIntegTestTask extends DefaultTask {
includePackaged = include
}


@Override
public Task dependsOn(Object... dependencies) {
runner.dependsOn(dependencies)
Expand All @@ -114,7 +125,7 @@ class RestIntegTestTask extends DefaultTask {
project.tasks.getByName("${name}Runner").configure(configure)
}

Copy createCopyRestSpecTask() {
Copy createCopyRestSpecTask(File copyTo) {
Boilerplate.maybeCreate(project.configurations, 'restSpec') {
project.dependencies.add(
'restSpec',
Expand All @@ -125,12 +136,14 @@ class RestIntegTestTask extends DefaultTask {

return Boilerplate.maybeCreate(project.tasks, 'copyRestSpec', Copy) { Copy copy ->
copy.dependsOn project.configurations.restSpec
copy.into(project.sourceSets.test.output.resourcesDir)
copy.into(copyTo)
copy.from({ project.zipTree(project.configurations.restSpec.singleFile) }) {
includeEmptyDirs = false
include 'rest-api-spec/**'
filesMatching('rest-api-spec/test/**') { FileCopyDetails details ->
if (includePackaged == false) {
if (includePackaged) {
details.copyTo(new File(new File(project.sourceSets.test.output.resourcesDir, "rest-api-spec/test"), details.name))
}else{
details.exclude()
}
}
Expand All @@ -147,4 +160,46 @@ class RestIntegTestTask extends DefaultTask {
}
}
}

Copy createCopyModulesRestSpecTask(File copyTo) {
return Boilerplate.maybeCreate(project.tasks, 'copyModulesRestSpecs', Copy) { Copy copy ->
copy.into(new File(copyTo, "rest-api-spec/api"))
copy.eachFile {
path = name
}
copy.from({ project.findProject(':modules').projectDir }) {
includeEmptyDirs = false
include '**/src/**/rest-api-spec/api/**'
exclude '**/examples/**'
}
}
}

Copy createCopyPluginsRestSpecTask(File copyTo) {
return Boilerplate.maybeCreate(project.tasks, 'copyPluginsRestSpecs', Copy) { Copy copy ->
copy.into(new File(copyTo, "rest-api-spec/api"))
copy.eachFile {
path = name
}
copy.from({ project.findProject(':plugins').projectDir }) {
includeEmptyDirs = false
include '**/src/**/rest-api-spec/api/**'
exclude '**/examples/**'
}
}
}

Copy createCopyXpackPluginsRestSpecTask(File copyTo) {
return Boilerplate.maybeCreate(project.tasks, 'copyXpackPluginsRestSpecs', Copy) { Copy copy ->
copy.into(new File(copyTo, "rest-api-spec/api"))
copy.eachFile {
path = name
}
copy.from({ project.findProject(':x-pack:plugin').projectDir }) {
includeEmptyDirs = false
include '**/src/**/rest-api-spec/api/**'
exclude '**/examples/**'
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
import org.elasticsearch.common.CheckedFunction;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ToXContent;
Expand Down Expand Up @@ -813,7 +814,8 @@ public void testApiNamingConventions() throws Exception {
deprecatedMethods.add("multi_search");
deprecatedMethods.add("search_scroll");

ClientYamlSuiteRestSpec restSpec = ClientYamlSuiteRestSpec.load("/rest-api-spec/api");
ClientYamlSuiteRestSpec restSpec =
ClientYamlSuiteRestSpec.load(PathUtils.get(RestHighLevelClientTests.class.getResource("/rest-api-spec/api").toURI()));
Set<String> apiSpec = restSpec.getApis().stream().map(ClientYamlSuiteRestApi::getName).collect(Collectors.toSet());
Set<String> apiUnsupported = new HashSet<>(apiSpec);
Set<String> apiNotFound = new HashSet<>();
Expand Down
1 change: 1 addition & 0 deletions distribution/docker/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ task integTest(type: Test) {
outputs.doNotCacheIf('Build cache is disabled for Docker tests') { true }
maxParallelForks = '1'
include '**/*IT.class'
nonInputProperties.systemProperty('tests.rest.spec_root', project.sourceSets.test.output.resourcesDir)
// don't add the tasks to build the docker images if we have no way of testing them
if (TestFixturesPlugin.dockerComposeSupported()) {
dependsOn assemble
Expand Down
17 changes: 17 additions & 0 deletions plugins/examples/rest-handler/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ esplugin {
noticeFile rootProject.file('NOTICE.txt')
}

configurations {
restSpec
}

dependencies {
restSpec project(':rest-api-spec')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use project dependencies like this in our example projects. These examples are mean to be completely self-contained. That is, you should be able to copy and paste this into your own build and it work. This obviously wouldn't work in that scenario as the project :rest-api-spec only exists in the Elasticsearch build.

It's not even clear why this is necessary. Folks building plugins should not need to run our rest api tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, I think the refactoring you suggested will remove the need for this.

It's not even clear why this is necessary. Folks building plugins should not need to run our rest api tests.

There is a project with an example REST test, which needs the needs the REST spec. This was an attempt to avoid making a reference to the rootBuildDir. Moot point though :)

}

processTestResources {
from({ zipTree(configurations.restSpec.singleFile) }) {
includeEmptyDirs = false
include 'rest-api-spec/api/**'
}
dependsOn configurations.restSpec
}

// No unit tests in this example
test.enabled = false

Expand All @@ -43,6 +59,7 @@ integTest {
dependsOn exampleFixture
runner {
nonInputProperties.systemProperty 'external.address', "${-> exampleFixture.addressAndPort}"
nonInputProperties.systemProperty('tests.rest.spec_root', project.sourceSets.test.output.resourcesDir)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in my other comment, I don't see the need for the addition of this system property everywhere. Do we ever set this to any other location?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is see comment above.

}
}

Expand Down
1 change: 1 addition & 0 deletions qa/mixed-cluster/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ for (Version bwcVersion : bwcVersions.wireCompatible) {

nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}".allHttpSocketURI.join(",")}")
nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}".getName()}")
nonInputProperties.systemProperty('tests.rest.spec_root', project.sourceSets.test.output.resourcesDir)
}
systemProperty 'tests.path.repo', "${buildDir}/cluster/shared/repo/${baseName}"
onlyIf { project.bwc_tests_enabled }
Expand Down
4 changes: 4 additions & 0 deletions qa/rolling-upgrade/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ for (Version bwcVersion : bwcVersions.wireCompatible) {
systemProperty 'tests.rest.suite', 'old_cluster'
nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}".allHttpSocketURI.join(",")}")
nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}".getName()}")
nonInputProperties.systemProperty('tests.rest.spec_root', project.sourceSets.test.output.resourcesDir)
}

tasks.register("${baseName}#oneThirdUpgradedTest", RestTestRunnerTask) {
Expand All @@ -95,6 +96,7 @@ for (Version bwcVersion : bwcVersions.wireCompatible) {
systemProperty 'tests.first_round', 'true'
nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}".allHttpSocketURI.join(",")}")
nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}".getName()}")
nonInputProperties.systemProperty('tests.rest.spec_root', project.sourceSets.test.output.resourcesDir)
}

tasks.register("${baseName}#twoThirdsUpgradedTest", RestTestRunnerTask) {
Expand All @@ -107,6 +109,7 @@ for (Version bwcVersion : bwcVersions.wireCompatible) {
systemProperty 'tests.first_round', 'false'
nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}".allHttpSocketURI.join(",")}")
nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}".getName()}")
nonInputProperties.systemProperty('tests.rest.spec_root', project.sourceSets.test.output.resourcesDir)
}

tasks.register("${baseName}#upgradedClusterTest", RestTestRunnerTask) {
Expand All @@ -118,6 +121,7 @@ for (Version bwcVersion : bwcVersions.wireCompatible) {
systemProperty 'tests.rest.suite', 'upgraded_cluster'
nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}".allHttpSocketURI.join(",")}")
nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}".getName()}")
nonInputProperties.systemProperty('tests.rest.spec_root', project.sourceSets.test.output.resourcesDir)
}

if (project.bwc_tests_enabled) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,6 @@ grant codeBase "file:${gradle.worker.jar}" {
grant {
// since the gradle test worker jar is on the test classpath, our tests should be able to read it
permission java.io.FilePermission "${gradle.worker.jar}", "read";
// the rest specs are copied here and used across most modules and plugins
permission java.io.FilePermission "${tests.rest.spec_root}/-", "read";
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.sniff.ElasticsearchNodesSniffer;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
Expand All @@ -49,6 +50,7 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
Expand All @@ -57,6 +59,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
Expand All @@ -66,8 +69,8 @@
* The suite timeout is extended to account for projects with a large number of tests.
*/
@TimeoutSuite(millis = 30 * TimeUnits.MINUTE)
@SuppressForbidden(reason = "reads the REST spec from the real filesystem, not the mock one used for testing")
public abstract class ESClientYamlSuiteTestCase extends ESRestTestCase {

/**
* Property that allows to control which REST tests get run. Supports comma separated list of tests
* or directories that contain tests e.g. -Dtests.rest.suite=index,get,create/10_with_id
Expand All @@ -88,8 +91,14 @@ public abstract class ESClientYamlSuiteTestCase extends ESRestTestCase {
*/
private static final String REST_TESTS_VALIDATE_SPEC = "tests.rest.validate_spec";

private static final String TESTS_PATH = "/rest-api-spec/test";
private static final String SPEC_PATH = "/rest-api-spec/api";
/**
* Property that allows to set the root for the rest API spec. This value plus SPEC_PATH is the location of the REST API spec can be
* found.
*/
public static final String REST_SPEC_ROOT = "tests.rest.spec_root";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear what this property is doing. We seem to be adding a way to specifify where specifically in the classpath to look for this stuff, but everywhere I can tell we simply set this location to the root of the resources output directory. This seems superfluous. If we are going to continue to load these off the classpath, we should just assume they live in some particular locaiton (which we do, at /rest-api-spec). We are now just having to set an additional system property for all our tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It flexes between the rootBuildDir and the test.output.resoureDir, depending on the test. I think with the refactoring suggested above, it may be able to eliminate the variance and just use a well known path.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused, are we adding rootBuildDir to the classpath? As far as I can tell we are still loading these things from the classpath, not from the filesystem, or did I miss something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to close the loop here...
no, the rootBuildDir was not added to the classpath, the api specs (not the tests) in this pr are read from the file system which is why the security policy change was required. The refactoring you suggested removes the need for this. Update coming soon.

public static final Path SPEC_PATH = Paths.get("rest-api-spec/api");

private static final String TESTS_CLASSPATH = "/rest-api-spec/test";

/**
* This separator pattern matches ',' except it is preceded by a '\'.
Expand Down Expand Up @@ -125,7 +134,7 @@ public void initAndResetContext() throws Exception {
if (restTestExecutionContext == null) {
assert adminExecutionContext == null;
assert blacklistPathMatchers == null;
final ClientYamlSuiteRestSpec restSpec = ClientYamlSuiteRestSpec.load(SPEC_PATH);
final ClientYamlSuiteRestSpec restSpec = ClientYamlSuiteRestSpec.load(getSpecPath());
validateSpec(restSpec);
final List<HttpHost> hosts = getClusterHosts();
Tuple<Version, Version> versionVersionTuple = readVersionsFromCatNodes(adminClient());
Expand Down Expand Up @@ -164,6 +173,15 @@ protected ClientYamlTestClient initClientYamlTestClient(
return new ClientYamlTestClient(restSpec, restClient, hosts, esVersion, masterVersion, this::getClientBuilderWithSniffedHosts);
}

private Path getSpecPath() {
Path restSpec = Paths.get(
Objects.requireNonNull(
System.getProperty(REST_SPEC_ROOT), "System property [" + REST_SPEC_ROOT + "] must not be null"))
.resolve(SPEC_PATH);
logger.info("Reading REST spec from [{}]", restSpec);
return restSpec;
}

@AfterClass
public static void closeClient() throws IOException {
try {
Expand All @@ -184,7 +202,6 @@ public static void closeClient() throws IOException {
public static Iterable<Object[]> createParameters() throws Exception {
return createParameters(ExecutableSection.XCONTENT_REGISTRY);
}

/**
* Create parameters for this parameterized test.
*/
Expand Down Expand Up @@ -233,11 +250,12 @@ public static Iterable<Object[]> createParameters(NamedXContentRegistry executea
return tests;
}


/** Find all yaml suites that match the given list of paths from the root test path. */
// pkg private for tests
static Map<String, Set<Path>> loadSuites(String... paths) throws Exception {
Map<String, Set<Path>> files = new HashMap<>();
Path root = PathUtils.get(ESClientYamlSuiteTestCase.class.getResource(TESTS_PATH).toURI());
Path root = PathUtils.get(ESClientYamlSuiteTestCase.class.getResource(TESTS_CLASSPATH).toURI());
for (String strPath : paths) {
Path path = root.resolve(strPath);
if (Files.isDirectory(path)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/
package org.elasticsearch.test.rest.yaml.restspec;

import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentParser;
Expand Down Expand Up @@ -79,11 +78,10 @@ public boolean isClientParameter(String name) {
/**
* Parses the complete set of REST spec available under the provided directories
*/
public static ClientYamlSuiteRestSpec load(String classpathPrefix) throws Exception {
Path dir = PathUtils.get(ClientYamlSuiteRestSpec.class.getResource(classpathPrefix).toURI());
public static ClientYamlSuiteRestSpec load(Path specPath) throws Exception {
ClientYamlSuiteRestSpec restSpec = new ClientYamlSuiteRestSpec();
ClientYamlSuiteRestApiParser restApiParser = new ClientYamlSuiteRestApiParser();
try (Stream<Path> stream = Files.walk(dir)) {
try (Stream<Path> stream = Files.walk(specPath)) {
stream.forEach(item -> {
if (item.toString().endsWith(".json")) {
parseSpecFile(restApiParser, item, restSpec);
Expand Down
3 changes: 3 additions & 0 deletions x-pack/qa/rolling-upgrade-basic/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ for (Version bwcVersion : bwcVersions.wireCompatible) {
}
nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}".allHttpSocketURI.join(",")}")
nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}".getName()}")
nonInputProperties.systemProperty('tests.rest.spec_root', project.sourceSets.test.output.resourcesDir)
systemProperty 'tests.rest.suite', 'mixed_cluster'
systemProperty 'tests.first_round', 'true'
systemProperty 'tests.upgrade_from_version', bwcVersion.toString().replace('-SNAPSHOT', '')
Expand All @@ -82,6 +83,7 @@ for (Version bwcVersion : bwcVersions.wireCompatible) {
}
nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}".allHttpSocketURI.join(",")}")
nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}".getName()}")
nonInputProperties.systemProperty('tests.rest.spec_root', project.sourceSets.test.output.resourcesDir)
systemProperty 'tests.rest.suite', 'mixed_cluster'
systemProperty 'tests.first_round', 'false'
systemProperty 'tests.upgrade_from_version', bwcVersion.toString().replace('-SNAPSHOT', '')
Expand All @@ -95,6 +97,7 @@ for (Version bwcVersion : bwcVersions.wireCompatible) {
}
nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}".allHttpSocketURI.join(",")}")
nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}".getName()}")
nonInputProperties.systemProperty('tests.rest.spec_root', project.sourceSets.test.output.resourcesDir)
systemProperty 'tests.rest.suite', 'upgraded_cluster'
systemProperty 'tests.upgrade_from_version', bwcVersion.toString().replace('-SNAPSHOT', '')
}
Expand Down
1 change: 1 addition & 0 deletions x-pack/qa/rolling-upgrade-multi-cluster/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ for (Version bwcVersion : bwcVersions.wireCompatible) {
nonInputProperties.systemProperty('tests.leader_remote_cluster_seed', "${-> testClusters."${baseName}-leader".allTransportPortURI.last()}")
nonInputProperties.systemProperty('tests.follower_host', "${-> testClusters."${baseName}-follower".allHttpSocketURI.last()}")
nonInputProperties.systemProperty('tests.follower_remote_cluster_seed', "${-> testClusters."${baseName}-follower".allTransportPortURI.last()}")
nonInputProperties.systemProperty('tests.rest.spec_root', project.sourceSets.test.output.resourcesDir)
}
}

Expand Down
Loading