Skip to content

Commit 0cce4e3

Browse files
committed
[TEST] Added ability to skip REST test suite/sections based on their required features
As we have different runners for the REST tests we need a mechanism that allows us to add features to any of them without breaking all others builds. The idea is to name a feature and temporarily use skip sections that mention the required new features, so that runners that don't support it will skip the test. Added support for `features` field in skip section. Added `Features` class that contains a static list of the features supported by the runner. If a feature mentioned in a skip section is not listed here, the test will be skipped.
1 parent 5c02350 commit 0cce4e3

File tree

9 files changed

+211
-32
lines changed

9 files changed

+211
-32
lines changed

rest-api-spec/test/README.asciidoc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,23 @@ to a string with sufficient digits to allow string comparison, eg
8484
Snapshot versions and versions of the form `1.0.0.Beta1` can be treated
8585
as the rounded down version, eg `1.0.0`.
8686

87+
The skip section can also be used to list new features that need to be
88+
supported in order to run a test. This way the up-to-date runners will
89+
run the test, while the ones that don't support the feature yet can
90+
temporarily skip it, and avoid having lots of test failures in the meantime.
91+
92+
....
93+
"Parent":
94+
- skip:
95+
features: regex
96+
97+
- do:
98+
... test definitions ...
99+
....
100+
101+
The `features` field can either be a string or an array of strings.
102+
The skip section requires to specify either a `version` or a `features` list.
103+
87104
Required operators:
88105
-------------------
89106

src/test/java/org/elasticsearch/test/rest/junit/RestTestSuiteRunner.java

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.elasticsearch.test.rest.section.RestTestSuite;
4141
import org.elasticsearch.test.rest.section.TestSection;
4242
import org.elasticsearch.test.rest.spec.RestSpec;
43+
import org.elasticsearch.test.rest.support.Features;
4344
import org.elasticsearch.test.rest.support.FileUtils;
4445
import org.junit.runner.Description;
4546
import org.junit.runner.notification.Failure;
@@ -264,7 +265,7 @@ protected List<RestTestCandidate> collectTestCandidates(Description rootDescript
264265
apiDescription.addChild(testSuiteDescription);
265266

266267
if (restTestSuite.getTestSections().size() == 0) {
267-
assert restTestSuite.getSetupSection().getSkipSection().skipVersion(restTestExecutionContext.esVersion());
268+
assert restTestSuite.getSetupSection().getSkipSection().skip(restTestExecutionContext.esVersion());
268269
testCandidates.add(RestTestCandidate.empty(restTestSuite, testSuiteDescription));
269270
continue;
270271
}
@@ -274,7 +275,7 @@ protected List<RestTestCandidate> collectTestCandidates(Description rootDescript
274275
for (TestSection testSection : restTestSuite.getTestSections()) {
275276

276277
//no need to generate seed if we are going to skip the test section
277-
if (testSection.getSkipSection().skipVersion(restTestExecutionContext.esVersion())) {
278+
if (testSection.getSkipSection().skip(restTestExecutionContext.esVersion())) {
278279
Description testSectionDescription = createTestSectionIterationDescription(restTestSuite, testSection, null);
279280
testSuiteDescription.addChild(testSectionDescription);
280281
testCandidates.add(new RestTestCandidate(restTestSuite, testSuiteDescription, testSection, testSectionDescription, -1));
@@ -448,23 +449,37 @@ public void run() {
448449
protected void runChild(RestTestCandidate testCandidate, RunNotifier notifier) {
449450

450451
//if the while suite needs to be skipped, no test sections were loaded, only an empty one that we need to mark as ignored
451-
if (testCandidate.getSetupSection().getSkipSection().skipVersion(restTestExecutionContext.esVersion())) {
452-
logger.info("skipped test suite [{}]\nreason: {}\nskip versions: {} (current version: {})",
453-
testCandidate.getSuiteDescription(), testCandidate.getSetupSection().getSkipSection().getReason(),
454-
testCandidate.getSetupSection().getSkipSection().getVersion(), restTestExecutionContext.esVersion());
455-
452+
if (testCandidate.getSetupSection().getSkipSection().skip(restTestExecutionContext.esVersion())) {
453+
if (logger.isInfoEnabled()) {
454+
if (testCandidate.getSetupSection().getSkipSection().isVersionCheck()) {
455+
logger.info("skipped test suite [{}]\nreason: {}\nskip versions: {} (current version: {})",
456+
testCandidate.getSuiteDescription(), testCandidate.getSetupSection().getSkipSection().getReason(),
457+
testCandidate.getSetupSection().getSkipSection().getVersion(), restTestExecutionContext.esVersion());
458+
} else {
459+
logger.info("skipped test suite [{}]\nreason: feature not supported\nrequired features: {} (supported features: {})",
460+
testCandidate.getSuiteDescription(), testCandidate.getSetupSection().getSkipSection().getFeatures(), Features.getSupported());
461+
}
462+
}
456463
notifier.fireTestIgnored(testCandidate.describeSuite());
457464
return;
458465
}
459466

460467
//from now on no more empty test candidates are expected
461468
assert testCandidate.getTestSection() != null;
462469

463-
if (testCandidate.getTestSection().getSkipSection().skipVersion(restTestExecutionContext.esVersion())) {
464-
logger.info("skipped test [{}/{}]\nreason: {}\nskip versions: {} (current version: {})",
465-
testCandidate.getSuiteDescription(), testCandidate.getTestSection().getName(),
466-
testCandidate.getTestSection().getSkipSection().getReason(),
467-
testCandidate.getTestSection().getSkipSection().getVersion(), restTestExecutionContext.esVersion());
470+
if (testCandidate.getTestSection().getSkipSection().skip(restTestExecutionContext.esVersion())) {
471+
if (logger.isInfoEnabled()) {
472+
if (testCandidate.getTestSection().getSkipSection().isVersionCheck()) {
473+
logger.info("skipped test [{}/{}]\nreason: {}\nskip versions: {} (current version: {})",
474+
testCandidate.getSuiteDescription(), testCandidate.getTestSection().getName(),
475+
testCandidate.getTestSection().getSkipSection().getReason(),
476+
testCandidate.getTestSection().getSkipSection().getVersion(), restTestExecutionContext.esVersion());
477+
} else {
478+
logger.info("skipped test [{}/{}]\nreason: feature not supported\nrequired features: {} (supported features: {})",
479+
testCandidate.getSuiteDescription(), testCandidate.getTestSection().getName(),
480+
testCandidate.getTestSection().getSkipSection().getFeatures(), Features.getSupported());
481+
}
482+
}
468483

469484
notifier.fireTestIgnored(testCandidate.describeTest());
470485
return;

src/test/java/org/elasticsearch/test/rest/parser/RestTestSectionParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public TestSection parse(RestTestSuiteParseContext parseContext) throws IOExcept
3939
parser.nextToken();
4040
testSection.setSkipSection(parseContext.parseSkipSection());
4141

42-
boolean skip = testSection.getSkipSection().skipVersion(parseContext.getCurrentVersion());
42+
boolean skip = testSection.getSkipSection().skip(parseContext.getCurrentVersion());
4343

4444
while ( parser.currentToken() != XContentParser.Token.END_ARRAY) {
4545
if (skip) {

src/test/java/org/elasticsearch/test/rest/parser/RestTestSuiteParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public RestTestSuite parse(RestTestSuiteParseContext parseContext) throws IOExce
6767

6868
restTestSuite.setSetupSection(parseContext.parseSetupSection());
6969

70-
boolean skip = restTestSuite.getSetupSection().getSkipSection().skipVersion(parseContext.getCurrentVersion());
70+
boolean skip = restTestSuite.getSetupSection().getSkipSection().skip(parseContext.getCurrentVersion());
7171

7272
while(true) {
7373
//the "---" section separator is not understood by the yaml parser. null is returned, same as when the parser is closed

src/test/java/org/elasticsearch/test/rest/parser/SetupSectionParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public SetupSection parse(RestTestSuiteParseContext parseContext) throws IOExcep
3636
SetupSection setupSection = new SetupSection();
3737
setupSection.setSkipSection(parseContext.parseSkipSection());
3838

39-
boolean skip = setupSection.getSkipSection().skipVersion(parseContext.getCurrentVersion());
39+
boolean skip = setupSection.getSkipSection().skip(parseContext.getCurrentVersion());
4040

4141
while (parser.currentToken() != XContentParser.Token.END_ARRAY) {
4242
if (skip) {

src/test/java/org/elasticsearch/test/rest/parser/SkipSectionParser.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
*/
1919
package org.elasticsearch.test.rest.parser;
2020

21+
import com.google.common.collect.Lists;
2122
import org.elasticsearch.common.Strings;
2223
import org.elasticsearch.common.xcontent.XContentParser;
2324
import org.elasticsearch.test.rest.section.SkipSection;
2425

2526
import java.io.IOException;
27+
import java.util.List;
2628

2729
/**
2830
* Parser for skip sections
@@ -38,6 +40,7 @@ public SkipSection parse(RestTestSuiteParseContext parseContext) throws IOExcept
3840
XContentParser.Token token;
3941
String version = null;
4042
String reason = null;
43+
List<String> features = Lists.newArrayList();
4144

4245
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
4346
if (token == XContentParser.Token.FIELD_NAME) {
@@ -47,22 +50,34 @@ public SkipSection parse(RestTestSuiteParseContext parseContext) throws IOExcept
4750
version = parser.text();
4851
} else if ("reason".equals(currentFieldName)) {
4952
reason = parser.text();
50-
} else {
53+
} else if ("features".equals(currentFieldName)) {
54+
features.add(parser.text());
55+
}
56+
else {
5157
throw new RestTestParseException("field " + currentFieldName + " not supported within skip section");
5258
}
59+
} else if (token == XContentParser.Token.START_ARRAY) {
60+
if ("features".equals(currentFieldName)) {
61+
while(parser.nextToken() != XContentParser.Token.END_ARRAY) {
62+
features.add(parser.text());
63+
}
64+
}
5365
}
5466
}
5567

5668
parser.nextToken();
5769

58-
if (!Strings.hasLength(version)) {
59-
throw new RestTestParseException("version is mandatory within skip section");
70+
if (!Strings.hasLength(version) && features.isEmpty()) {
71+
throw new RestTestParseException("version or features is mandatory within skip section");
72+
}
73+
if (Strings.hasLength(version) && !features.isEmpty()) {
74+
throw new RestTestParseException("version or features are mutually exclusive");
6075
}
61-
if (!Strings.hasLength(reason)) {
62-
throw new RestTestParseException("reason is mandatory within skip section");
76+
if (Strings.hasLength(version) && !Strings.hasLength(reason)) {
77+
throw new RestTestParseException("reason is mandatory within skip version section");
6378
}
6479

65-
return new SkipSection(version, reason);
80+
return new SkipSection(version, features, reason);
6681

6782
}
6883
}

src/test/java/org/elasticsearch/test/rest/section/SkipSection.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,37 +18,63 @@
1818
*/
1919
package org.elasticsearch.test.rest.section;
2020

21+
import com.google.common.collect.Lists;
22+
import org.elasticsearch.common.Strings;
23+
import org.elasticsearch.test.rest.support.Features;
2124
import org.elasticsearch.test.rest.support.VersionUtils;
2225

26+
import java.util.List;
27+
2328
/**
2429
* Represents a skip section that tells whether a specific test section or suite needs to be skipped
25-
* based on the elasticsearch version the tests are running against.
30+
* based on:
31+
* - the elasticsearch version the tests are running against
32+
* - a specific test feature required that might not be implemented yet by the runner
2633
*/
2734
public class SkipSection {
2835

29-
public static final SkipSection EMPTY = new SkipSection("", "");
36+
public static final SkipSection EMPTY = new SkipSection("", Lists.<String>newArrayList(), "");
3037

3138
private final String version;
39+
private final List<String> features;
3240
private final String reason;
3341

34-
public SkipSection(String version, String reason) {
42+
public SkipSection(String version, List<String> features, String reason) {
3543
this.version = version;
44+
this.features = features;
3645
this.reason = reason;
3746
}
3847

3948
public String getVersion() {
4049
return version;
4150
}
4251

52+
public List<String> getFeatures() {
53+
return features;
54+
}
55+
4356
public String getReason() {
4457
return reason;
4558
}
4659

47-
public boolean skipVersion(String currentVersion) {
60+
public boolean skip(String currentVersion) {
4861
if (isEmpty()) {
4962
return false;
5063
}
51-
return VersionUtils.skipCurrentVersion(version, currentVersion);
64+
65+
if (version != null) {
66+
return VersionUtils.skipCurrentVersion(version, currentVersion);
67+
}
68+
69+
if (features != null && !this.features.isEmpty()) {
70+
return !Features.areAllSupported(this.features);
71+
}
72+
73+
throw new IllegalArgumentException("version or feature should be not null in a non empty skip section");
74+
}
75+
76+
public boolean isVersionCheck() {
77+
return Strings.hasLength(version);
5278
}
5379

5480
public boolean isEmpty() {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.test.rest.support;
21+
22+
import com.google.common.collect.Lists;
23+
24+
import java.util.List;
25+
26+
/**
27+
* Allows to register additional features supported by the tests runner.
28+
* This way any runner can add extra features and use proper skip sections to avoid
29+
* breaking others runners till they have implemented the new feature as well.
30+
*/
31+
public final class Features {
32+
33+
private static final List<String> SUPPORTED = Lists.newArrayList();
34+
35+
private Features() {
36+
37+
}
38+
39+
/**
40+
* Tells whether all the features provided as argument are supported
41+
*/
42+
public static boolean areAllSupported(List<String> features) {
43+
for (String feature : features) {
44+
if (!SUPPORTED.contains(feature)) {
45+
return false;
46+
}
47+
}
48+
return true;
49+
}
50+
51+
/**
52+
* Returns all the supported features
53+
*/
54+
public static List<String> getSupported() {
55+
return SUPPORTED;
56+
}
57+
}

0 commit comments

Comments
 (0)