Rework testing conventions gradle plugin (#87213)
This PR reworks the testing conventions precommit plugin. This plugin now: - is compatible with yaml, java rest tests and internalClusterTest (aka different sourceSets per test type) - enforces test base class and simple naming conventions (as it did before) - adds one check task per test sourceSet - uses the worker api to improve task execution parallelism and encapsulation - is gradle configuration cache compatible This also ports the TestingConventions integration testing to Spock and removes the build-tools-internal/test kit folder that is not required anymore. We also add some common logic for testing java related gradle plugins. We will apply further cleanup on other tests within our test suite in a dedicated follow up cleanup
This commit is contained in:
parent
52e2e374e8
commit
cdf5bd7ed0
|
@ -213,6 +213,7 @@ repositories {
|
|||
configurations {
|
||||
integTestRuntimeOnly.extendsFrom(testRuntimeOnly)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
constraints {
|
||||
integTestImplementation('org.ow2.asm:asm:9.3')
|
||||
|
@ -297,7 +298,6 @@ tasks.named('test').configure {
|
|||
useJUnitPlatform()
|
||||
}
|
||||
tasks.register("integTest", Test) {
|
||||
inputs.dir(file("src/testKit")).withPropertyName("testkit dir").withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
systemProperty 'test.version_under_test', version
|
||||
testClassesDirs = sourceSets.integTest.output.classesDirs
|
||||
classpath = sourceSets.integTest.runtimeClasspath
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.fixtures
|
||||
|
||||
import org.elasticsearch.gradle.internal.conventions.precommit.PrecommitPlugin
|
||||
|
||||
abstract class AbstractGradlePrecommitPluginFuncTest extends AbstractJavaGradleFuncTest {
|
||||
|
||||
abstract <T extends PrecommitPlugin> Class<T> getPluginClassUnderTest();
|
||||
|
||||
def setup() {
|
||||
buildFile << """
|
||||
import ${getPluginClassUnderTest().getName()}
|
||||
|
||||
plugins {
|
||||
// bring in build-tools-internal onto the classpath
|
||||
id 'elasticsearch.global-build-info'
|
||||
}
|
||||
// internally used plugins do not have a plugin id as they are
|
||||
// not intended to be used directly from build scripts
|
||||
plugins.apply(${getPluginClassUnderTest().getSimpleName()})
|
||||
|
||||
"""
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.fixtures
|
||||
|
||||
import net.bytebuddy.ByteBuddy
|
||||
import net.bytebuddy.dynamic.DynamicType
|
||||
import org.junit.rules.ExternalResource
|
||||
import org.junit.rules.TemporaryFolder
|
||||
|
||||
class LocalRepositoryFixture extends ExternalResource{
|
||||
|
||||
private TemporaryFolder temporaryFolder
|
||||
|
||||
LocalRepositoryFixture(TemporaryFolder temporaryFolder){
|
||||
this.temporaryFolder = temporaryFolder
|
||||
}
|
||||
|
||||
void generateJar(String group, String module, String version, String... clazzNames){
|
||||
def baseGroupFolderPath = group.replace('.', '/')
|
||||
def targetFolder = new File(repoDir, "${baseGroupFolderPath}/$module/$version")
|
||||
targetFolder.mkdirs()
|
||||
|
||||
def jarFile = new File(targetFolder, "${module}-${version}.jar")
|
||||
clazzNames.each {clazzName ->
|
||||
DynamicType.Unloaded<?> dynamicType = new ByteBuddy().subclass(Object.class)
|
||||
.name(clazzName)
|
||||
.make()
|
||||
if(jarFile.exists()) {
|
||||
dynamicType.inject(jarFile);
|
||||
}else {
|
||||
dynamicType.toJar(jarFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void configureBuild(File buildFile) {
|
||||
buildFile << """
|
||||
repositories {
|
||||
maven {
|
||||
name = "local-test"
|
||||
url = "${getRepoDir()}"
|
||||
metadataSources {
|
||||
artifact()
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
File getRepoDir() {
|
||||
new File(temporaryFolder.root, 'local-repo')
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ class ElasticsearchJavaModulePathPluginFuncTest extends AbstractJavaGradleFuncTe
|
|||
public static final String ES_VERSION = VersionProperties.getElasticsearch()
|
||||
|
||||
def setup() {
|
||||
javaMainClass()
|
||||
clazz("org.acme.JavaMainClass")
|
||||
subProject("some-lib") << """
|
||||
plugins {
|
||||
id 'java-library'
|
||||
|
|
|
@ -8,19 +8,22 @@
|
|||
|
||||
package org.elasticsearch.gradle.internal.precommit
|
||||
|
||||
import org.elasticsearch.gradle.fixtures.AbstractGradleFuncTest
|
||||
import org.elasticsearch.gradle.fixtures.AbstractGradlePrecommitPluginFuncTest
|
||||
import org.elasticsearch.gradle.internal.conventions.precommit.LicenseHeadersPrecommitPlugin
|
||||
import org.elasticsearch.gradle.internal.conventions.precommit.PrecommitPlugin
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
|
||||
class LicenseHeadersPrecommitPluginFuncTest extends AbstractGradleFuncTest {
|
||||
class LicenseHeadersPrecommitPluginFuncTest extends AbstractGradlePrecommitPluginFuncTest {
|
||||
|
||||
Class<? extends PrecommitPlugin> pluginClassUnderTest = LicenseHeadersPrecommitPlugin.class
|
||||
|
||||
def setup() {
|
||||
buildFile << """
|
||||
apply plugin:'java'
|
||||
"""
|
||||
}
|
||||
def "detects invalid files with invalid license header"() {
|
||||
given:
|
||||
buildFile << """
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'elasticsearch.internal-licenseheaders'
|
||||
}
|
||||
"""
|
||||
dualLicensedFile()
|
||||
unknownSourceFile()
|
||||
unapprovedSourceFile()
|
||||
|
@ -39,11 +42,6 @@ class LicenseHeadersPrecommitPluginFuncTest extends AbstractGradleFuncTest {
|
|||
def "can filter source files"() {
|
||||
given:
|
||||
buildFile << """
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'elasticsearch.internal-licenseheaders'
|
||||
}
|
||||
|
||||
tasks.named("licenseHeaders").configure {
|
||||
excludes << 'org/acme/filtered/**/*'
|
||||
}
|
||||
|
@ -61,12 +59,6 @@ class LicenseHeadersPrecommitPluginFuncTest extends AbstractGradleFuncTest {
|
|||
|
||||
def "supports sspl by convention"() {
|
||||
given:
|
||||
buildFile << """
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'elasticsearch.internal-licenseheaders'
|
||||
}
|
||||
"""
|
||||
dualLicensedFile()
|
||||
|
||||
when:
|
||||
|
@ -79,11 +71,6 @@ class LicenseHeadersPrecommitPluginFuncTest extends AbstractGradleFuncTest {
|
|||
def "sspl default additional license can be overridden"() {
|
||||
given:
|
||||
buildFile << """
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'elasticsearch.internal-licenseheaders'
|
||||
}
|
||||
|
||||
tasks.named("licenseHeaders").configure {
|
||||
additionalLicense 'ELAST', 'Elastic License 2.0', '2.0; you may not use this file except in compliance with the Elastic License'
|
||||
}
|
||||
|
@ -173,4 +160,5 @@ class LicenseHeadersPrecommitPluginFuncTest extends AbstractGradleFuncTest {
|
|||
String normalizedPath = normalized(sourceFile.getPath())
|
||||
(normalizedPath.substring(normalizedPath.indexOf("src/main/java")) - "src/main/java/" - ("/" + sourceFile.getName())).replaceAll("/", ".")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.precommit
|
||||
|
||||
import org.elasticsearch.gradle.fixtures.AbstractGradlePrecommitPluginFuncTest
|
||||
import org.elasticsearch.gradle.fixtures.LocalRepositoryFixture
|
||||
import org.elasticsearch.gradle.internal.conventions.precommit.PrecommitPlugin
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import org.junit.ClassRule
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import spock.lang.Shared
|
||||
import spock.lang.Unroll
|
||||
|
||||
class TestingConventionsPrecommitPluginFuncTest extends AbstractGradlePrecommitPluginFuncTest {
|
||||
|
||||
Class<? extends PrecommitPlugin> pluginClassUnderTest = TestingConventionsPrecommitPlugin.class
|
||||
|
||||
@ClassRule
|
||||
@Shared
|
||||
public TemporaryFolder repoFolder = new TemporaryFolder()
|
||||
|
||||
@Shared
|
||||
@ClassRule
|
||||
public LocalRepositoryFixture repository = new LocalRepositoryFixture(repoFolder)
|
||||
|
||||
def setupSpec() {
|
||||
repository.generateJar('org.apache.lucene', 'tests.util', "1.0",
|
||||
"org.apache.lucene.tests.util.LuceneTestCase",
|
||||
"org.elasticsearch.test.ESSingleNodeTestCase",
|
||||
"org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase",
|
||||
"org.elasticsearch.test.AbstractMultiClustersTestCase"
|
||||
)
|
||||
repository.generateJar('org.junit', 'junit', "4.42",
|
||||
"org.junit.Assert", "org.junit.Test"
|
||||
)
|
||||
}
|
||||
|
||||
def setup() {
|
||||
repository.configureBuild(buildFile)
|
||||
}
|
||||
|
||||
def "skips convention check if no tests available"() {
|
||||
given:
|
||||
buildFile << """
|
||||
apply plugin:'java'
|
||||
"""
|
||||
when:
|
||||
def result = gradleRunner("precommit").build()
|
||||
|
||||
then:
|
||||
result.task(":testTestingConventions").outcome == TaskOutcome.NO_SOURCE
|
||||
result.task(":testingConventions").outcome == TaskOutcome.UP_TO_DATE
|
||||
}
|
||||
|
||||
def "testing convention tasks are cacheable and uptodate"() {
|
||||
given:
|
||||
simpleJavaBuild()
|
||||
testClazz("org.acme.valid.SomeTests", "org.apache.lucene.tests.util.LuceneTestCase") {
|
||||
"""
|
||||
public void testMe() {
|
||||
}
|
||||
"""
|
||||
}
|
||||
when:
|
||||
gradleRunner("clean", "precommit", "--build-cache").build()
|
||||
def result = gradleRunner("clean", "precommit", "--build-cache").build()
|
||||
then:
|
||||
result.task(":testTestingConventions").outcome == TaskOutcome.FROM_CACHE
|
||||
result.task(":testingConventions").outcome == TaskOutcome.UP_TO_DATE
|
||||
|
||||
when:
|
||||
result = gradleRunner("precommit").build()
|
||||
then:
|
||||
result.task(":testTestingConventions").outcome == TaskOutcome.UP_TO_DATE
|
||||
result.task(":testingConventions").outcome == TaskOutcome.UP_TO_DATE
|
||||
}
|
||||
|
||||
def "testing convention plugin is configuration cache compatible"() {
|
||||
given:
|
||||
simpleJavaBuild()
|
||||
testClazz("org.acme.valid.SomeTests", "org.apache.lucene.tests.util.LuceneTestCase") {
|
||||
"""
|
||||
public void testMe() {
|
||||
}
|
||||
"""
|
||||
}
|
||||
when:
|
||||
def result = gradleRunner("precommit", "--configuration-cache").build()
|
||||
then:
|
||||
assertOutputContains(result.getOutput(), "0 problems were found storing the configuration cache.")
|
||||
|
||||
when:
|
||||
result = gradleRunner("precommit", "--configuration-cache").build()
|
||||
then:
|
||||
assertOutputContains(result.getOutput(), "Configuration cache entry reused.")
|
||||
}
|
||||
|
||||
def "checks base class convention"() {
|
||||
given:
|
||||
simpleJavaBuild()
|
||||
testClazz("org.acme.valid.SomeTests", "org.apache.lucene.tests.util.LuceneTestCase") {
|
||||
"""
|
||||
public void testMe() {
|
||||
}
|
||||
"""
|
||||
}
|
||||
when:
|
||||
def result = gradleRunner("precommit").build()
|
||||
then:
|
||||
result.task(":testTestingConventions").outcome == TaskOutcome.SUCCESS
|
||||
result.task(":testingConventions").outcome == TaskOutcome.SUCCESS
|
||||
|
||||
when:
|
||||
testClazz("org.acme.InvalidTests") {
|
||||
"""
|
||||
public void testMe() {
|
||||
}
|
||||
"""
|
||||
}
|
||||
result = gradleRunner("precommit").buildAndFail()
|
||||
then:
|
||||
result.task(":testTestingConventions").outcome == TaskOutcome.FAILED
|
||||
|
||||
assertOutputContains(result.getOutput(), """\
|
||||
* What went wrong:
|
||||
Execution failed for task ':testTestingConventions'.
|
||||
> A failure occurred while executing org.elasticsearch.gradle.internal.precommit.TestingConventionsCheckTask\$TestingConventionsCheckWorkAction
|
||||
> Following test classes do not extend any supported base class:
|
||||
\torg.acme.InvalidTests""".stripIndent()
|
||||
)
|
||||
}
|
||||
|
||||
def "checks naming convention"() {
|
||||
given:
|
||||
simpleJavaBuild()
|
||||
buildFile << """
|
||||
tasks.named('testTestingConventions').configure {
|
||||
suffix 'UnitTest'
|
||||
}
|
||||
"""
|
||||
|
||||
testClazz("org.acme.valid.SomeNameMissmatchingTest", "org.apache.lucene.tests.util.LuceneTestCase") {
|
||||
"""
|
||||
public void testMe() {
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
testClazz("org.acme.valid.SomeMatchingUnitTest", "org.apache.lucene.tests.util.LuceneTestCase") {
|
||||
"""
|
||||
public void testMe() {
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
when:
|
||||
def result = gradleRunner("precommit").buildAndFail()
|
||||
then:
|
||||
result.task(":testTestingConventions").outcome == TaskOutcome.FAILED
|
||||
assertOutputContains(result.getOutput(), """\
|
||||
* What went wrong:
|
||||
Execution failed for task ':testTestingConventions'.
|
||||
> A failure occurred while executing org.elasticsearch.gradle.internal.precommit.TestingConventionsCheckTask\$TestingConventionsCheckWorkAction
|
||||
> Following test classes do not match naming convention to use suffix 'UnitTest':
|
||||
\torg.acme.valid.SomeNameMissmatchingTest""".stripIndent()
|
||||
)
|
||||
}
|
||||
|
||||
def "provided base classes do not need match naming convention"() {
|
||||
given:
|
||||
simpleJavaBuild()
|
||||
buildFile << """
|
||||
tasks.named('testTestingConventions').configure {
|
||||
baseClass 'org.acme.SomeCustomTestBaseClass'
|
||||
}
|
||||
"""
|
||||
|
||||
testClazz("org.acme.SomeCustomTestBaseClass", "org.junit.Assert")
|
||||
testClazz("org.acme.valid.SomeNameMatchingTests", "org.acme.SomeCustomTestBaseClass") {
|
||||
"""
|
||||
public void testMe() {
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
when:
|
||||
def result = gradleRunner("precommit").build()
|
||||
then:
|
||||
result.task(":testTestingConventions").outcome == TaskOutcome.SUCCESS
|
||||
}
|
||||
|
||||
def "applies conventions on yaml-rest-test tests"() {
|
||||
given:
|
||||
clazz(dir('src/yamlRestTest/java'), "org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase")
|
||||
buildFile << """
|
||||
apply plugin:'elasticsearch.internal-yaml-rest-test'
|
||||
|
||||
dependencies {
|
||||
yamlRestTestImplementation "org.apache.lucene:tests.util:1.0"
|
||||
yamlRestTestImplementation "org.junit:junit:4.42"
|
||||
}
|
||||
"""
|
||||
|
||||
clazz(dir("src/yamlRestTest/java"), "org.acme.valid.SomeMatchingIT", "org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase") {
|
||||
"""
|
||||
public void testMe() {
|
||||
}
|
||||
"""
|
||||
}
|
||||
clazz(dir("src/yamlRestTest/java"), "org.acme.valid.SomeOtherMatchingIT", null) {
|
||||
"""
|
||||
public void testMe() {
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
when:
|
||||
def result = gradleRunner("testingConventions").buildAndFail()
|
||||
then:
|
||||
result.task(":yamlRestTestTestingConventions").outcome == TaskOutcome.FAILED
|
||||
assertOutputContains(result.getOutput(), """\
|
||||
* What went wrong:
|
||||
Execution failed for task ':yamlRestTestTestingConventions'.
|
||||
> A failure occurred while executing org.elasticsearch.gradle.internal.precommit.TestingConventionsCheckTask\$TestingConventionsCheckWorkAction
|
||||
> Following test classes do not extend any supported base class:
|
||||
\torg.acme.valid.SomeOtherMatchingIT""".stripIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "applies conventions on #sourceSetName tests"() {
|
||||
given:
|
||||
clazz(dir("src/${sourceSetName}/java"), "org.elasticsearch.test.ESIntegTestCase")
|
||||
clazz(dir("src/${sourceSetName}/java"), "org.elasticsearch.test.rest.ESRestTestCase")
|
||||
buildFile << """
|
||||
import org.elasticsearch.gradle.internal.precommit.TestingConventionsCheckTask
|
||||
apply plugin:'$pluginName'
|
||||
|
||||
dependencies {
|
||||
${sourceSetName}Implementation "org.apache.lucene:tests.util:1.0"
|
||||
${sourceSetName}Implementation "org.junit:junit:4.42"
|
||||
}
|
||||
tasks.withType(TestingConventionsCheckTask).configureEach {
|
||||
suffix 'IT'
|
||||
suffix 'Tests'
|
||||
}
|
||||
"""
|
||||
|
||||
clazz(dir("src/${sourceSetName}/java"), "org.acme.valid.SomeMatchingIT", "org.elasticsearch.test.ESIntegTestCase") {
|
||||
"""
|
||||
public void testMe() {}
|
||||
"""
|
||||
}
|
||||
|
||||
clazz(dir("src/${sourceSetName}/java"), "org.acme.valid.SomeNonMatchingTest", "org.elasticsearch.test.ESIntegTestCase") {
|
||||
"""
|
||||
public void testMe() {}
|
||||
"""
|
||||
}
|
||||
|
||||
when:
|
||||
def result = gradleRunner("testingConventions").buildAndFail()
|
||||
then:
|
||||
result.task(taskName).outcome == TaskOutcome.FAILED
|
||||
assertOutputContains(result.getOutput(), """\
|
||||
* What went wrong:
|
||||
Execution failed for task '${taskName}'.
|
||||
> A failure occurred while executing org.elasticsearch.gradle.internal.precommit.TestingConventionsCheckTask\$TestingConventionsCheckWorkAction
|
||||
> Following test classes do not match naming convention to use suffix 'IT' or 'Tests':
|
||||
\torg.acme.valid.SomeNonMatchingTest""".stripIndent()
|
||||
)
|
||||
|
||||
where:
|
||||
pluginName | taskName | sourceSetName
|
||||
"elasticsearch.internal-java-rest-test" | ":javaRestTestTestingConventions" | "javaRestTest"
|
||||
"elasticsearch.internal-cluster-test" | ":internalClusterTestTestingConventions" | "internalClusterTest"
|
||||
}
|
||||
|
||||
private void simpleJavaBuild() {
|
||||
buildFile << """
|
||||
apply plugin:'java'
|
||||
|
||||
dependencies {
|
||||
testImplementation "org.apache.lucene:tests.util:1.0"
|
||||
testImplementation "org.junit:junit:4.42"
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.jarhell;
|
||||
|
||||
import org.elasticsearch.gradle.internal.test.GradleIntegrationTestCase;
|
||||
import org.gradle.testkit.runner.BuildResult;
|
||||
import org.gradle.testkit.runner.GradleRunner;
|
||||
|
||||
public class TestingConventionsTasksIT extends GradleIntegrationTestCase {
|
||||
|
||||
@Override
|
||||
public String projectName() {
|
||||
return "testingConventions";
|
||||
}
|
||||
|
||||
public void testInnerClasses() {
|
||||
GradleRunner runner = getGradleRunner().withArguments("clean", ":no_tests_in_inner_classes:testingConventions", "-i", "-s");
|
||||
BuildResult result = runner.buildAndFail();
|
||||
assertOutputContains(
|
||||
result.getOutput(),
|
||||
"Test classes implemented by inner classes will not run:",
|
||||
" * org.elasticsearch.gradle.testkit.NastyInnerClasses$LooksLikeATestWithoutNamingConvention1",
|
||||
" * org.elasticsearch.gradle.testkit.NastyInnerClasses$LooksLikeATestWithoutNamingConvention2",
|
||||
" * org.elasticsearch.gradle.testkit.NastyInnerClasses$LooksLikeATestWithoutNamingConvention3",
|
||||
" * org.elasticsearch.gradle.testkit.NastyInnerClasses$NamingConventionIT",
|
||||
" * org.elasticsearch.gradle.testkit.NastyInnerClasses$NamingConventionTests"
|
||||
);
|
||||
}
|
||||
|
||||
public void testNamingConvention() {
|
||||
GradleRunner runner = getGradleRunner().withArguments("clean", ":incorrect_naming_conventions:testingConventions", "-i", "-s");
|
||||
BuildResult result = runner.buildAndFail();
|
||||
assertOutputContains(
|
||||
result.getOutput(),
|
||||
"Seem like test classes but don't match naming convention:",
|
||||
" * org.elasticsearch.gradle.testkit.LooksLikeATestWithoutNamingConvention1",
|
||||
" * org.elasticsearch.gradle.testkit.LooksLikeATestWithoutNamingConvention2",
|
||||
" * org.elasticsearch.gradle.testkit.LooksLikeATestWithoutNamingConvention3"
|
||||
);
|
||||
assertOutputMissing(result.getOutput(), "LooksLikeTestsButAbstract");
|
||||
}
|
||||
|
||||
public void testNoEmptyTasks() {
|
||||
GradleRunner runner = getGradleRunner().withArguments("clean", ":empty_test_task:testingConventions", "-i", "-s");
|
||||
BuildResult result = runner.buildAndFail();
|
||||
assertOutputContains(
|
||||
result.getOutput(),
|
||||
"Expected at least one test class included in task :empty_test_task:emptyTest, but found none.",
|
||||
"Expected at least one test class included in task :empty_test_task:test, but found none."
|
||||
);
|
||||
}
|
||||
|
||||
public void testAllTestTasksIncluded() {
|
||||
GradleRunner runner = getGradleRunner().withArguments("clean", ":all_classes_in_tasks:testingConventions", "-i", "-s");
|
||||
BuildResult result = runner.buildAndFail();
|
||||
assertOutputContains(
|
||||
result.getOutput(),
|
||||
"Test classes are not included in any enabled task (:all_classes_in_tasks:test):",
|
||||
" * org.elasticsearch.gradle.testkit.NamingConventionIT"
|
||||
);
|
||||
}
|
||||
|
||||
public void testTaskNotImplementBaseClass() {
|
||||
GradleRunner runner = getGradleRunner().withArguments("clean", ":not_implementing_base:testingConventions", "-i", "-s");
|
||||
BuildResult result = runner.buildAndFail();
|
||||
assertOutputContains(
|
||||
result.getOutput(),
|
||||
"Tests classes with suffix `IT` should extend org.elasticsearch.gradle.testkit.Integration but the following classes do not:",
|
||||
" * org.elasticsearch.gradle.testkit.NamingConventionIT",
|
||||
" * org.elasticsearch.gradle.testkit.NamingConventionMissmatchIT",
|
||||
"Tests classes with suffix `Tests` should extend org.elasticsearch.gradle.testkit.Unit but the following classes do not:",
|
||||
" * org.elasticsearch.gradle.testkit.NamingConventionMissmatchTests",
|
||||
" * org.elasticsearch.gradle.testkit.NamingConventionTests"
|
||||
);
|
||||
}
|
||||
|
||||
public void testValidSetupWithoutBaseClass() {
|
||||
GradleRunner runner = getGradleRunner().withArguments("clean", ":valid_setup_no_base:testingConventions", "-i", "-s");
|
||||
BuildResult result = runner.build();
|
||||
assertTaskSuccessful(result, ":valid_setup_no_base:testingConventions");
|
||||
}
|
||||
|
||||
public void testValidSetupWithBaseClass() {
|
||||
GradleRunner runner = getGradleRunner().withArguments("clean", ":valid_setup_with_base:testingConventions", "-i", "-s");
|
||||
BuildResult result = runner.build();
|
||||
assertTaskSuccessful(result, ":valid_setup_with_base:testingConventions");
|
||||
}
|
||||
|
||||
public void testTestsInMain() {
|
||||
GradleRunner runner = getGradleRunner().withArguments("clean", ":tests_in_main:testingConventions", "-i", "-s");
|
||||
BuildResult result = runner.buildAndFail();
|
||||
assertOutputContains(
|
||||
result.getOutput(),
|
||||
"Classes matching the test naming convention should be in test not main:",
|
||||
" * NamingConventionIT",
|
||||
" * NamingConventionTests"
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
package org.elasticsearch.gradle.internal;
|
||||
|
||||
import org.elasticsearch.gradle.internal.precommit.TestingConventionsPrecommitPlugin;
|
||||
import org.elasticsearch.gradle.internal.precommit.TestingConventionsTasks;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
public class InternalPluginBuildPlugin implements InternalPlugin {
|
||||
|
@ -17,23 +15,5 @@ public class InternalPluginBuildPlugin implements InternalPlugin {
|
|||
public void apply(Project project) {
|
||||
project.getPluginManager().apply(BuildPlugin.class);
|
||||
project.getPluginManager().apply(BaseInternalPluginBuildPlugin.class);
|
||||
|
||||
project.getPlugins()
|
||||
.withType(
|
||||
TestingConventionsPrecommitPlugin.class,
|
||||
plugin -> project.getTasks().withType(TestingConventionsTasks.class).named("testingConventions").configure(t -> {
|
||||
t.getNaming().clear();
|
||||
t.getNaming()
|
||||
.create(
|
||||
"Tests",
|
||||
testingConventionRule -> testingConventionRule.baseClass("org.apache.lucene.tests.util.LuceneTestCase")
|
||||
);
|
||||
t.getNaming().create("IT", testingConventionRule -> {
|
||||
testingConventionRule.baseClass("org.elasticsearch.test.ESIntegTestCase");
|
||||
testingConventionRule.baseClass("org.elasticsearch.test.rest.ESRestTestCase");
|
||||
testingConventionRule.baseClass("org.elasticsearch.test.ESSingleNodeTestCase");
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,17 +26,7 @@ public class InternalPrecommitTasks {
|
|||
project.getPluginManager().apply(LicenseHeadersPrecommitPlugin.class);
|
||||
project.getPluginManager().apply(FilePermissionsPrecommitPlugin.class);
|
||||
project.getPluginManager().apply(LoggerUsagePrecommitPlugin.class);
|
||||
|
||||
// TestingConventionsPlugin is incompatible with projects without
|
||||
// test source sets. We wanna remove this plugin once we moved away from
|
||||
// StandaloneRestTest plugin and RestTestPlugin. For apply this plugin only
|
||||
// when tests are available.
|
||||
// Long Term we want remove the need for most of this plugins functionality
|
||||
// and not rely on multiple test tasks against a sourceSet.
|
||||
if (project.file("src/test").exists()) {
|
||||
project.getPluginManager().apply(TestingConventionsPrecommitPlugin.class);
|
||||
}
|
||||
|
||||
project.getPluginManager().apply(TestingConventionsPrecommitPlugin.class);
|
||||
// tasks with just tests don't need certain tasks to run, so this flag makes adding
|
||||
// the task optional
|
||||
if (withProductiveCode) {
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.internal.precommit;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Represent rules for tests enforced by the @{link {@link TestingConventionsTasks}}
|
||||
*
|
||||
* Rules are identified by name, tests must have this name as a suffix and implement one of the base classes
|
||||
* and be part of all the specified tasks.
|
||||
*/
|
||||
public class TestingConventionRule implements Serializable {
|
||||
|
||||
private final String suffix;
|
||||
|
||||
private Set<String> baseClasses = new HashSet<>();
|
||||
|
||||
private Set<Pattern> taskNames = new HashSet<>();
|
||||
|
||||
public TestingConventionRule(String suffix) {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public String getSuffix() {
|
||||
return suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for @{link getSuffix} as Gradle requires a name property
|
||||
*
|
||||
*/
|
||||
public String getName() {
|
||||
return suffix;
|
||||
}
|
||||
|
||||
public void baseClass(String clazz) {
|
||||
baseClasses.add(clazz);
|
||||
}
|
||||
|
||||
public void setBaseClasses(Collection<String> baseClasses) {
|
||||
this.baseClasses.clear();
|
||||
this.baseClasses.addAll(baseClasses);
|
||||
}
|
||||
|
||||
public void taskName(Pattern expression) {
|
||||
taskNames.add(expression);
|
||||
}
|
||||
|
||||
public void taskName(String expression) {
|
||||
taskNames.add(Pattern.compile(expression));
|
||||
}
|
||||
|
||||
public void setTaskNames(Collection<Pattern> expressions) {
|
||||
taskNames.clear();
|
||||
taskNames.addAll(expressions);
|
||||
}
|
||||
|
||||
public Set<String> getBaseClasses() {
|
||||
return baseClasses;
|
||||
}
|
||||
|
||||
public Set<Pattern> getTaskNames() {
|
||||
return taskNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TestingConventionRule that = (TestingConventionRule) o;
|
||||
return Objects.equals(suffix, that.suffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(suffix);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.precommit;
|
||||
|
||||
import org.elasticsearch.gradle.internal.conventions.precommit.PrecommitTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.EmptyFileVisitor;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.file.FileVisitDetails;
|
||||
import org.gradle.api.logging.Logging;
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.tasks.CacheableTask;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.IgnoreEmptyDirectories;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.Internal;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SkipWhenEmpty;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.workers.WorkAction;
|
||||
import org.gradle.workers.WorkParameters;
|
||||
import org.gradle.workers.WorkQueue;
|
||||
import org.gradle.workers.WorkerExecutor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@CacheableTask
|
||||
public abstract class TestingConventionsCheckTask extends PrecommitTask {
|
||||
|
||||
@Input
|
||||
abstract ListProperty<String> getSuffixes();
|
||||
|
||||
@Internal
|
||||
abstract ConfigurableFileCollection getTestClassesDirs();
|
||||
|
||||
@InputFiles
|
||||
@SkipWhenEmpty
|
||||
@IgnoreEmptyDirectories
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public FileTree getTestClasses() {
|
||||
return getTestClassesDirs().getAsFileTree().matching(pattern -> pattern.include("**/*.class"));
|
||||
}
|
||||
|
||||
@Classpath
|
||||
abstract ConfigurableFileCollection getClasspath();
|
||||
|
||||
@Input
|
||||
public abstract ListProperty<String> getBaseClasses();
|
||||
|
||||
@Inject
|
||||
public abstract WorkerExecutor getWorkerExecutor();
|
||||
|
||||
public void baseClass(String qualifiedClassname) {
|
||||
getBaseClasses().add(qualifiedClassname);
|
||||
}
|
||||
|
||||
public void suffix(String suffix) {
|
||||
getSuffixes().add(suffix);
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void validate() {
|
||||
WorkQueue workQueue = getWorkerExecutor().classLoaderIsolation(spec -> spec.getClasspath().from(getClasspath()));
|
||||
workQueue.submit(TestingConventionsCheckWorkAction.class, parameters -> {
|
||||
parameters.getClasspath().setFrom(getClasspath());
|
||||
parameters.getClassDirectories().setFrom(getTestClassesDirs());
|
||||
parameters.getBaseClassesNames().set(getBaseClasses().get());
|
||||
parameters.getSuffixes().set(getSuffixes().get());
|
||||
});
|
||||
}
|
||||
|
||||
abstract static class TestingConventionsCheckWorkAction implements WorkAction<Parameters> {
|
||||
|
||||
private static final String JUNIT3_TEST_METHOD_PREFIX = "test";
|
||||
private static final Predicate<Class<?>> isAbstractClass = clazz -> Modifier.isAbstract(clazz.getModifiers());
|
||||
private static final Predicate<Class<?>> isPublicClass = clazz -> Modifier.isPublic(clazz.getModifiers());
|
||||
private static final Predicate<Class<?>> isStaticClass = clazz -> Modifier.isStatic(clazz.getModifiers());
|
||||
private static final Predicate<Class<?>> testClassDefaultPredicate = isAbstractClass.negate()
|
||||
.and(isPublicClass)
|
||||
.and(isStaticClass.negate());
|
||||
|
||||
@Inject
|
||||
public TestingConventionsCheckWorkAction() {}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
ClassLoadingFileVisitor fileVisitor = new ClassLoadingFileVisitor();
|
||||
getParameters().getClassDirectories().getAsFileTree().visit(fileVisitor);
|
||||
checkTestClasses(
|
||||
fileVisitor.getTestClassCandidates(),
|
||||
getParameters().getBaseClassesNames().get(),
|
||||
getParameters().getSuffixes().get()
|
||||
);
|
||||
}
|
||||
|
||||
private void checkTestClasses(List<String> testClassesCandidates, List<String> baseClassNames, List<String> suffixes) {
|
||||
var testClassCandidates = testClassesCandidates.stream()
|
||||
.map(className -> loadClassWithoutInitializing(className, getClass().getClassLoader()))
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
var baseClasses = baseClassNames.stream()
|
||||
.map(className -> loadClassWithoutInitializing(className, getClass().getClassLoader()))
|
||||
.toList();
|
||||
testClassCandidates.removeAll(baseClasses);
|
||||
var matchingBaseClass = getBaseClassMatching(testClassCandidates, baseClasses);
|
||||
assertMatchesSuffix(suffixes, matchingBaseClass);
|
||||
testClassCandidates.removeAll(matchingBaseClass);
|
||||
assertNoMissmatchingTest(testClassCandidates);
|
||||
}
|
||||
|
||||
private void assertNoMissmatchingTest(List<? extends Class<?>> testClassesCandidate) {
|
||||
var mismatchingBaseClasses = testClassesCandidate.stream()
|
||||
.filter(testClassDefaultPredicate)
|
||||
.filter(TestingConventionsCheckWorkAction::seemsLikeATest)
|
||||
.collect(Collectors.toList());
|
||||
if (mismatchingBaseClasses.isEmpty() == false) {
|
||||
throw new GradleException(
|
||||
"Following test classes do not extend any supported base class:\n\t"
|
||||
+ mismatchingBaseClasses.stream().map(c -> c.getName()).collect(Collectors.joining("\n\t"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMatchesSuffix(List<String> suffixes, List<Class> matchingBaseClass) {
|
||||
// ensure base class matching do match suffix
|
||||
var matchingBaseClassNotMatchingSuffix = matchingBaseClass.stream()
|
||||
.filter(c -> suffixes.stream().allMatch(s -> c.getName().endsWith(s) == false))
|
||||
.collect(Collectors.toList());
|
||||
if (matchingBaseClassNotMatchingSuffix.isEmpty() == false) {
|
||||
throw new GradleException(
|
||||
"Following test classes do not match naming convention to use suffix "
|
||||
+ suffixes.stream().map(s -> "'" + s + "'").collect(Collectors.joining(" or "))
|
||||
+ ":\n\t"
|
||||
+ matchingBaseClassNotMatchingSuffix.stream().map(c -> c.getName()).collect(Collectors.joining("\n\t"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Class> getBaseClassMatching(List<? extends Class<?>> testClassCandidates, List<? extends Class<?>> baseClasses) {
|
||||
Predicate<Class<?>> extendsBaseClass = clazz -> baseClasses.stream().anyMatch(baseClass -> baseClass.isAssignableFrom(clazz));
|
||||
return testClassCandidates.stream()
|
||||
.filter(testClassDefaultPredicate)
|
||||
.filter(extendsBaseClass)
|
||||
.filter(TestingConventionsCheckWorkAction::seemsLikeATest)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static boolean seemsLikeATest(Class<?> clazz) {
|
||||
try {
|
||||
Class<?> junitTest = loadClassWithoutInitializing("org.junit.Assert", clazz.getClassLoader());
|
||||
if (junitTest.isAssignableFrom(clazz)) {
|
||||
Logging.getLogger(TestingConventionsCheckWorkAction.class)
|
||||
.debug("{} is a test because it extends {}", clazz.getName(), junitTest.getName());
|
||||
return true;
|
||||
}
|
||||
|
||||
Class<?> junitAnnotation = loadClassWithoutInitializing("org.junit.Test", clazz.getClassLoader());
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (matchesTestMethodNamingConvention(method)) {
|
||||
Logging.getLogger(TestingConventionsCheckWorkAction.class)
|
||||
.debug("{} is a test because it has method named '{}'", clazz.getName(), method.getName());
|
||||
return true;
|
||||
}
|
||||
if (isAnnotated(method, junitAnnotation)) {
|
||||
Logging.getLogger(TestingConventionsCheckWorkAction.class)
|
||||
.debug(
|
||||
"{} is a test because it has method '{}' annotated with '{}'",
|
||||
clazz.getName(),
|
||||
method.getName(),
|
||||
junitAnnotation.getName()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (NoClassDefFoundError e) {
|
||||
// Include the message to get more info to get more a more useful message when running Gradle without -s
|
||||
throw new IllegalStateException("Failed to inspect class " + clazz.getName() + ". Missing class? " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean matchesTestMethodNamingConvention(Method method) {
|
||||
return method.getName().startsWith(JUNIT3_TEST_METHOD_PREFIX)
|
||||
&& Modifier.isStatic(method.getModifiers()) == false
|
||||
&& method.getReturnType().equals(Void.TYPE);
|
||||
}
|
||||
|
||||
private static boolean isAnnotated(Method method, Class<?> annotation) {
|
||||
return List.of(method.getAnnotations())
|
||||
.stream()
|
||||
.anyMatch(presentAnnotation -> annotation.isAssignableFrom(presentAnnotation.getClass()));
|
||||
}
|
||||
|
||||
private static Class<?> loadClassWithoutInitializing(String name, ClassLoader classLoader) {
|
||||
try {
|
||||
return Class.forName(
|
||||
name,
|
||||
// Don't initialize the class to save time. Not needed for this test and this doesn't share a VM with any other tests.
|
||||
false,
|
||||
classLoader
|
||||
);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Failed to load class " + name + ". Incorrect classpath?", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ClassLoadingFileVisitor extends EmptyFileVisitor {
|
||||
private static final String CLASS_POSTFIX = ".class";
|
||||
private List<String> fullQualifiedClassNames = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void visitFile(FileVisitDetails fileVisitDetails) {
|
||||
String fileName = fileVisitDetails.getName();
|
||||
if (fileName.endsWith(CLASS_POSTFIX)) {
|
||||
String packageName = Arrays.stream(fileVisitDetails.getRelativePath().getSegments())
|
||||
.takeWhile(s -> s.equals(fileName) == false)
|
||||
.collect(Collectors.joining("."));
|
||||
String simpleClassName = fileName.replace(CLASS_POSTFIX, "");
|
||||
String fullQualifiedClassName = packageName + (packageName.isEmpty() ? "" : ".") + simpleClassName;
|
||||
fullQualifiedClassNames.add(fullQualifiedClassName);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getTestClassCandidates() {
|
||||
return fullQualifiedClassNames;
|
||||
}
|
||||
}
|
||||
|
||||
interface Parameters extends WorkParameters {
|
||||
ConfigurableFileCollection getClassDirectories();
|
||||
|
||||
ConfigurableFileCollection getClasspath();
|
||||
|
||||
ListProperty<String> getSuffixes();
|
||||
|
||||
ListProperty<String> getBaseClassesNames();
|
||||
}
|
||||
}
|
|
@ -8,41 +8,96 @@
|
|||
|
||||
package org.elasticsearch.gradle.internal.precommit;
|
||||
|
||||
import org.elasticsearch.gradle.internal.InternalPlugin;
|
||||
import org.elasticsearch.gradle.internal.conventions.precommit.PrecommitPlugin;
|
||||
import org.elasticsearch.gradle.util.GradleUtils;
|
||||
import org.elasticsearch.gradle.internal.test.InternalClusterTestPlugin;
|
||||
import org.elasticsearch.gradle.internal.test.rest.InternalJavaRestTestPlugin;
|
||||
import org.elasticsearch.gradle.internal.test.rest.InternalYamlRestTestPlugin;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.NamedDomainObjectProvider;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.List;
|
||||
|
||||
public class TestingConventionsPrecommitPlugin extends PrecommitPlugin {
|
||||
|
||||
public static final String TESTING_CONVENTIONS_TASK_NAME = "testingConventions";
|
||||
|
||||
public class TestingConventionsPrecommitPlugin extends PrecommitPlugin implements InternalPlugin {
|
||||
@Override
|
||||
public TaskProvider<? extends Task> createTask(Project project) {
|
||||
TaskProvider<TestingConventionsTasks> testingConventions = project.getTasks()
|
||||
.register("testingConventions", TestingConventionsTasks.class, t -> {
|
||||
TestingConventionRule testsRule = t.getNaming().maybeCreate("Tests");
|
||||
testsRule.baseClass("org.apache.lucene.tests.util.LuceneTestCase");
|
||||
TestingConventionRule itRule = t.getNaming().maybeCreate("IT");
|
||||
itRule.baseClass("org.elasticsearch.test.ESIntegTestCase");
|
||||
itRule.baseClass("org.elasticsearch.test.rest.ESRestTestCase");
|
||||
t.setCandidateClassFilesProvider(
|
||||
project.provider(
|
||||
() -> project.getTasks()
|
||||
.withType(Test.class)
|
||||
.matching(Task::getEnabled)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Task::getPath, task -> task.getCandidateClassFiles().getFiles()))
|
||||
)
|
||||
);
|
||||
SourceSetContainer javaSourceSets = GradleUtils.getJavaSourceSets(project);
|
||||
t.setSourceSets(javaSourceSets);
|
||||
// Run only after everything is compiled
|
||||
javaSourceSets.all(sourceSet -> t.dependsOn(sourceSet.getOutput().getClassesDirs()));
|
||||
project.getPlugins().apply(JavaBasePlugin.class);
|
||||
var javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);
|
||||
var sourceSets = javaPluginExtension.getSourceSets();
|
||||
var tasks = project.getTasks();
|
||||
|
||||
project.getPlugins().withType(JavaPlugin.class, javaPlugin -> {
|
||||
NamedDomainObjectProvider<SourceSet> sourceSet = sourceSets.named(SourceSet.TEST_SOURCE_SET_NAME);
|
||||
setupTaskForSourceSet(project, sourceSet, t -> {
|
||||
t.getSuffixes().convention(List.of("Tests"));
|
||||
t.getBaseClasses().convention(List.of("org.apache.lucene.tests.util.LuceneTestCase"));
|
||||
});
|
||||
return testingConventions;
|
||||
});
|
||||
|
||||
project.getPlugins().withType(InternalYamlRestTestPlugin.class, yamlRestTestPlugin -> {
|
||||
NamedDomainObjectProvider<SourceSet> sourceSet = sourceSets.named(InternalYamlRestTestPlugin.SOURCE_SET_NAME);
|
||||
setupTaskForSourceSet(project, sourceSet, t -> {
|
||||
t.getSuffixes().convention(List.of("IT"));
|
||||
t.getBaseClasses().convention(List.of("org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase"));
|
||||
});
|
||||
});
|
||||
|
||||
project.getPlugins().withType(InternalClusterTestPlugin.class, internalClusterTestPlugin -> {
|
||||
NamedDomainObjectProvider<SourceSet> sourceSet = sourceSets.named(InternalClusterTestPlugin.SOURCE_SET_NAME);
|
||||
setupTaskForSourceSet(project, sourceSet, t -> {
|
||||
// Unfortunately we see both in our build, so we by default support both for now.
|
||||
t.getSuffixes().convention(List.of("IT", "Tests"));
|
||||
t.getBaseClasses()
|
||||
.convention(
|
||||
List.of(
|
||||
"org.elasticsearch.test.ESIntegTestCase",
|
||||
"org.elasticsearch.test.ESSingleNodeTestCase",
|
||||
"org.elasticsearch.test.rest.ESRestTestCase",
|
||||
"org.elasticsearch.test.AbstractMultiClustersTestCase"
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
project.getPlugins().withType(InternalJavaRestTestPlugin.class, javaRestTestPlugin -> {
|
||||
NamedDomainObjectProvider<SourceSet> sourceSet = sourceSets.named(InternalJavaRestTestPlugin.SOURCE_SET_NAME);
|
||||
setupTaskForSourceSet(project, sourceSet, t -> {
|
||||
t.getSuffixes().convention(List.of("IT"));
|
||||
t.getBaseClasses()
|
||||
.convention(List.of("org.elasticsearch.test.ESIntegTestCase", "org.elasticsearch.test.rest.ESRestTestCase"));
|
||||
});
|
||||
});
|
||||
|
||||
// Create a convenience task for all checks (this does not conflict with extension, as it has higher priority in DSL):
|
||||
return tasks.register(TESTING_CONVENTIONS_TASK_NAME, task -> {
|
||||
task.setDescription("Runs all testing conventions checks.");
|
||||
task.dependsOn(tasks.withType(TestingConventionsCheckTask.class));
|
||||
});
|
||||
}
|
||||
|
||||
private void setupTaskForSourceSet(
|
||||
Project project,
|
||||
NamedDomainObjectProvider<SourceSet> sourceSetProvider,
|
||||
Action<TestingConventionsCheckTask> config
|
||||
) {
|
||||
sourceSetProvider.configure(sourceSet -> {
|
||||
String taskName = sourceSet.getTaskName(null, TESTING_CONVENTIONS_TASK_NAME);
|
||||
TaskProvider<TestingConventionsCheckTask> register = project.getTasks()
|
||||
.register(taskName, TestingConventionsCheckTask.class, task -> {
|
||||
task.getTestClassesDirs().from(sourceSet.getOutput().getClassesDirs());
|
||||
task.getClasspath().from(sourceSet.getRuntimeClasspath());
|
||||
});
|
||||
register.configure(config);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,422 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.internal.precommit;
|
||||
|
||||
import groovy.lang.Closure;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.NamedDomainObjectContainer;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.file.ProjectLayout;
|
||||
import org.gradle.api.model.ObjectFactory;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.FileVisitor;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class TestingConventionsTasks extends DefaultTask {
|
||||
|
||||
private static final String TEST_METHOD_PREFIX = "test";
|
||||
|
||||
private Map<String, File> testClassNames;
|
||||
|
||||
private NamedDomainObjectContainer<TestingConventionRule> naming;
|
||||
private ProjectLayout projectLayout;
|
||||
|
||||
private SourceSetContainer sourceSets;
|
||||
private Provider<Map<String, Set<File>>> candidateClassFilesProvider;
|
||||
private Map<String, Set<File>> candidateClassFiles;
|
||||
|
||||
public void setCandidateClassFilesProvider(Provider<Map<String, Set<File>>> candidateClassFilesProvider) {
|
||||
this.candidateClassFilesProvider = candidateClassFilesProvider;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public TestingConventionsTasks(ProjectLayout projectLayout, ObjectFactory objectFactory) {
|
||||
this.projectLayout = projectLayout;
|
||||
this.naming = objectFactory.domainObjectContainer(TestingConventionRule.class);
|
||||
setDescription("Tests various testing conventions");
|
||||
}
|
||||
|
||||
@Input
|
||||
public Map<String, Set<File>> getClassFilesPerEnabledTask() {
|
||||
candidateClassFiles = candidateClassFilesProvider.get();
|
||||
return candidateClassFiles;
|
||||
}
|
||||
|
||||
@Input
|
||||
public Map<String, File> getTestClassNames() {
|
||||
if (testClassNames == null) {
|
||||
testClassNames = sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME)
|
||||
.getOutput()
|
||||
.getClassesDirs()
|
||||
.getFiles()
|
||||
.stream()
|
||||
.filter(File::exists)
|
||||
.flatMap(testRoot -> walkPathAndLoadClasses(testRoot).entrySet().stream())
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
return testClassNames;
|
||||
}
|
||||
|
||||
@Input
|
||||
public NamedDomainObjectContainer<TestingConventionRule> getNaming() {
|
||||
return naming;
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public File getSuccessMarker() {
|
||||
return new File(projectLayout.getBuildDirectory().getAsFile().get(), "markers/" + getName());
|
||||
}
|
||||
|
||||
public void naming(Closure<?> action) {
|
||||
naming.configure(action);
|
||||
}
|
||||
|
||||
@Input
|
||||
public Set<String> getMainClassNamedLikeTests() {
|
||||
if (sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME) == null) {
|
||||
// some test projects don't have a main source set
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
|
||||
.getOutput()
|
||||
.getClassesDirs()
|
||||
.getAsFileTree()
|
||||
.getFiles()
|
||||
.stream()
|
||||
.filter(file -> file.getName().endsWith(".class"))
|
||||
.map(File::getName)
|
||||
.map(name -> name.substring(0, name.length() - 6))
|
||||
.filter(this::implementsNamingConvention)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void doCheck() throws IOException {
|
||||
final String problems;
|
||||
|
||||
try (
|
||||
URLClassLoader isolatedClassLoader = new URLClassLoader(
|
||||
getTestsClassPath().getFiles().stream().map(this::fileToUrl).toArray(URL[]::new)
|
||||
)
|
||||
) {
|
||||
Predicate<Class<?>> isStaticClass = clazz -> Modifier.isStatic(clazz.getModifiers());
|
||||
Predicate<Class<?>> isPublicClass = clazz -> Modifier.isPublic(clazz.getModifiers());
|
||||
Predicate<Class<?>> isAbstractClass = clazz -> Modifier.isAbstract(clazz.getModifiers());
|
||||
|
||||
final Map<File, ? extends Class<?>> classes = getTestClassNames().entrySet()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Map.Entry::getValue, entry -> loadClassWithoutInitializing(entry.getKey(), isolatedClassLoader)));
|
||||
|
||||
final FileTree allTestClassFiles = projectLayout.files(
|
||||
classes.values()
|
||||
.stream()
|
||||
.filter(isStaticClass.negate())
|
||||
.filter(isPublicClass)
|
||||
.filter((Predicate<Class<?>>) this::implementsNamingConvention)
|
||||
.map(clazz -> testClassNames.get(clazz.getName()))
|
||||
.collect(Collectors.toList())
|
||||
).getAsFileTree();
|
||||
|
||||
final Map<String, Set<File>> classFilesPerTask = candidateClassFiles;
|
||||
|
||||
final Set<File> testSourceSetFiles = sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME).getRuntimeClasspath().getFiles();
|
||||
final Map<String, Set<Class<?>>> testClassesPerTask = classFilesPerTask.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> testSourceSetFiles.containsAll(entry.getValue()))
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
Map.Entry::getKey,
|
||||
entry -> entry.getValue()
|
||||
.stream()
|
||||
.map(classes::get)
|
||||
.filter(this::implementsNamingConvention)
|
||||
.collect(Collectors.toSet())
|
||||
)
|
||||
);
|
||||
|
||||
final Map<String, Set<Class<?>>> suffixToBaseClass;
|
||||
if (classes.isEmpty()) {
|
||||
// Don't load base classes if we don't have any tests.
|
||||
// This allows defaults to be configured for projects that don't have any tests
|
||||
//
|
||||
suffixToBaseClass = Collections.emptyMap();
|
||||
} else {
|
||||
suffixToBaseClass = naming.stream()
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
TestingConventionRule::getSuffix,
|
||||
rule -> rule.getBaseClasses()
|
||||
.stream()
|
||||
.map(each -> loadClassWithoutInitializing(each, isolatedClassLoader))
|
||||
.collect(Collectors.toSet())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
problems = collectProblems(
|
||||
checkNoneExists(
|
||||
"Test classes implemented by inner classes will not run",
|
||||
classes.values()
|
||||
.stream()
|
||||
.filter(isStaticClass)
|
||||
.filter(isPublicClass)
|
||||
.filter(((Predicate<Class<?>>) this::implementsNamingConvention).or(this::seemsLikeATest))
|
||||
),
|
||||
checkNoneExists(
|
||||
"Seem like test classes but don't match naming convention",
|
||||
classes.values()
|
||||
.stream()
|
||||
.filter(isStaticClass.negate())
|
||||
.filter(isPublicClass)
|
||||
.filter(isAbstractClass.negate())
|
||||
.filter(this::seemsLikeATest) // TODO when base classes are set, check for classes that extend them
|
||||
.filter(((Predicate<Class<?>>) this::implementsNamingConvention).negate())
|
||||
),
|
||||
// TODO: check for non public classes that seem like tests
|
||||
// TODO: check for abstract classes that implement the naming conventions
|
||||
// No empty enabled tasks
|
||||
collectProblems(
|
||||
testClassesPerTask.entrySet()
|
||||
.stream()
|
||||
.map(entry -> checkAtLeastOneExists("test class included in task " + entry.getKey(), entry.getValue().stream()))
|
||||
.sorted()
|
||||
.collect(Collectors.joining("\n"))
|
||||
),
|
||||
checkNoneExists(
|
||||
"Test classes are not included in any enabled task ("
|
||||
+ classFilesPerTask.keySet().stream().collect(Collectors.joining(","))
|
||||
+ ")",
|
||||
allTestClassFiles.getFiles()
|
||||
.stream()
|
||||
.filter(testFile -> classFilesPerTask.values().stream().anyMatch(fileSet -> fileSet.contains(testFile)) == false)
|
||||
.map(classes::get)
|
||||
),
|
||||
collectProblems(suffixToBaseClass.entrySet().stream().filter(entry -> entry.getValue().isEmpty() == false).map(entry -> {
|
||||
return checkNoneExists(
|
||||
"Tests classes with suffix `"
|
||||
+ entry.getKey()
|
||||
+ "` should extend "
|
||||
+ entry.getValue().stream().map(Class::getName).collect(Collectors.joining(" or "))
|
||||
+ " but the following classes do not",
|
||||
classes.values()
|
||||
.stream()
|
||||
.filter(clazz -> clazz.getName().endsWith(entry.getKey()))
|
||||
.filter(clazz -> entry.getValue().stream().anyMatch(test -> test.isAssignableFrom(clazz)) == false)
|
||||
);
|
||||
}).sorted().collect(Collectors.joining("\n"))),
|
||||
// TODO: check that the testing tasks are included in the right task based on the name ( from the rule )
|
||||
checkNoneExists("Classes matching the test naming convention should be in test not main", getMainClassNamedLikeTests())
|
||||
);
|
||||
}
|
||||
|
||||
if (problems.isEmpty()) {
|
||||
getSuccessMarker().getParentFile().mkdirs();
|
||||
Files.write(getSuccessMarker().toPath(), new byte[] {}, StandardOpenOption.CREATE);
|
||||
} else {
|
||||
getLogger().error(problems);
|
||||
throw new IllegalStateException(String.format("Testing conventions [%s] are not honored", problems));
|
||||
}
|
||||
}
|
||||
|
||||
private String collectProblems(String... problems) {
|
||||
return Stream.of(problems).map(String::trim).filter(s -> s.isEmpty() == false).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
private String checkNoneExists(String message, Stream<? extends Class<?>> stream) {
|
||||
String problem = stream.map(each -> " * " + each.getName()).sorted().collect(Collectors.joining("\n"));
|
||||
if (problem.isEmpty() == false) {
|
||||
return message + ":\n" + problem;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private String checkNoneExists(String message, Set<? extends String> candidates) {
|
||||
String problem = candidates.stream().map(each -> " * " + each).sorted().collect(Collectors.joining("\n"));
|
||||
if (problem.isEmpty() == false) {
|
||||
return message + ":\n" + problem;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private String checkAtLeastOneExists(String message, Stream<? extends Class<?>> stream) {
|
||||
if (stream.findAny().isPresent()) {
|
||||
return "";
|
||||
} else {
|
||||
return "Expected at least one " + message + ", but found none.";
|
||||
}
|
||||
}
|
||||
|
||||
private boolean seemsLikeATest(Class<?> clazz) {
|
||||
try {
|
||||
ClassLoader classLoader = clazz.getClassLoader();
|
||||
|
||||
Class<?> junitTest = loadClassWithoutInitializing("org.junit.Assert", classLoader);
|
||||
if (junitTest.isAssignableFrom(clazz)) {
|
||||
getLogger().debug("{} is a test because it extends {}", clazz.getName(), junitTest.getName());
|
||||
return true;
|
||||
}
|
||||
|
||||
Class<?> junitAnnotation = loadClassWithoutInitializing("org.junit.Test", classLoader);
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (matchesTestMethodNamingConvention(method)) {
|
||||
getLogger().debug("{} is a test because it has method named '{}'", clazz.getName(), method.getName());
|
||||
return true;
|
||||
}
|
||||
if (isAnnotated(method, junitAnnotation)) {
|
||||
getLogger().debug(
|
||||
"{} is a test because it has method '{}' annotated with '{}'",
|
||||
clazz.getName(),
|
||||
method.getName(),
|
||||
junitAnnotation.getName()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (NoClassDefFoundError e) {
|
||||
// Include the message to get more info to get more a more useful message when running Gradle without -s
|
||||
throw new IllegalStateException("Failed to inspect class " + clazz.getName() + ". Missing class? " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean implementsNamingConvention(Class<?> clazz) {
|
||||
Objects.requireNonNull(clazz);
|
||||
return implementsNamingConvention(clazz.getName());
|
||||
}
|
||||
|
||||
private boolean implementsNamingConvention(String className) {
|
||||
if (naming.stream().map(TestingConventionRule::getSuffix).anyMatch(suffix -> className.endsWith(suffix))) {
|
||||
getLogger().debug("{} is a test because it matches the naming convention", className);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean matchesTestMethodNamingConvention(Method method) {
|
||||
return method.getName().startsWith(TEST_METHOD_PREFIX) && Modifier.isStatic(method.getModifiers()) == false;
|
||||
}
|
||||
|
||||
private boolean isAnnotated(Method method, Class<?> annotation) {
|
||||
for (Annotation presentAnnotation : method.getAnnotations()) {
|
||||
if (annotation.isAssignableFrom(presentAnnotation.getClass())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Classpath
|
||||
public FileCollection getTestsClassPath() {
|
||||
return sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME).getRuntimeClasspath();
|
||||
}
|
||||
|
||||
private Map<String, File> walkPathAndLoadClasses(File testRoot) {
|
||||
Map<String, File> classes = new HashMap<>();
|
||||
try {
|
||||
Files.walkFileTree(testRoot.toPath(), new FileVisitor<Path>() {
|
||||
private String packageName;
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
// First we visit the root directory
|
||||
if (packageName == null) {
|
||||
// And it package is empty string regardless of the directory name
|
||||
packageName = "";
|
||||
} else {
|
||||
packageName += dir.getFileName() + ".";
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
// Go up one package by jumping back to the second to last '.'
|
||||
packageName = packageName.substring(0, 1 + packageName.lastIndexOf('.', packageName.length() - 2));
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
String filename = file.getFileName().toString();
|
||||
if (filename.endsWith(".class")) {
|
||||
String className = filename.substring(0, filename.length() - ".class".length());
|
||||
classes.put(packageName + className, file.toFile());
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
|
||||
throw new IOException("Failed to visit " + file, exc);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
private Class<?> loadClassWithoutInitializing(String name, ClassLoader isolatedClassLoader) {
|
||||
try {
|
||||
return Class.forName(
|
||||
name,
|
||||
// Don't initialize the class to save time. Not needed for this test and this doesn't share a VM with any other tests.
|
||||
false,
|
||||
isolatedClassLoader
|
||||
);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Failed to load class " + name + ". Incorrect test runtime classpath?", e);
|
||||
}
|
||||
}
|
||||
|
||||
private URL fileToUrl(File file) {
|
||||
try {
|
||||
return file.toURI().toURL();
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSourceSets(SourceSetContainer sourceSets) {
|
||||
this.sourceSets = sourceSets;
|
||||
}
|
||||
}
|
|
@ -89,13 +89,21 @@ public class RestResourcesPlugin implements Plugin<Project> {
|
|||
Configuration testConfig = project.getConfigurations().create("restTestConfig");
|
||||
Configuration xpackTestConfig = project.getConfigurations().create("restXpackTestConfig");
|
||||
// core
|
||||
Dependency restTestdependency = project.getDependencies().project(Map.of("path", ":rest-api-spec", "configuration", "restTests"));
|
||||
project.getDependencies().add(testConfig.getName(), restTestdependency);
|
||||
// x-pack
|
||||
Dependency restXPackTestdependency = project.getDependencies()
|
||||
.project(Map.of("path", ":x-pack:plugin", "configuration", "restXpackTests"));
|
||||
project.getDependencies().add(xpackTestConfig.getName(), restXPackTestdependency);
|
||||
// we guard this reference to :rest-api-spec with a find to make testing easier
|
||||
var restApiSpecProjectAvailable = project.findProject(":rest-api-spec") != null;
|
||||
if (restApiSpecProjectAvailable) {
|
||||
Dependency restTestdependency = project.getDependencies()
|
||||
.project(Map.of("path", ":rest-api-spec", "configuration", "restTests"));
|
||||
project.getDependencies().add(testConfig.getName(), restTestdependency);
|
||||
}
|
||||
|
||||
// x-pack
|
||||
var restXpackTests = project.findProject(":x-pack:plugin") != null;
|
||||
if (restXpackTests) {
|
||||
Dependency restXPackTestdependency = project.getDependencies()
|
||||
.project(Map.of("path", ":x-pack:plugin", "configuration", "restXpackTests"));
|
||||
project.getDependencies().add(xpackTestConfig.getName(), restXPackTestdependency);
|
||||
}
|
||||
project.getConfigurations().create("restTests");
|
||||
project.getConfigurations().create("restXpackTests");
|
||||
|
||||
|
@ -114,8 +122,11 @@ public class RestResourcesPlugin implements Plugin<Project> {
|
|||
|
||||
// api
|
||||
Configuration specConfig = project.getConfigurations().create("restSpec"); // name chosen for passivity
|
||||
Dependency restSpecDependency = project.getDependencies().project(Map.of("path", ":rest-api-spec", "configuration", "restSpecs"));
|
||||
project.getDependencies().add(specConfig.getName(), restSpecDependency);
|
||||
if (restApiSpecProjectAvailable) {
|
||||
Dependency restSpecDependency = project.getDependencies()
|
||||
.project(Map.of("path", ":rest-api-spec", "configuration", "restSpecs"));
|
||||
project.getDependencies().add(specConfig.getName(), restSpecDependency);
|
||||
}
|
||||
project.getConfigurations().create("restSpecs");
|
||||
|
||||
Provider<CopyRestApiTask> copyRestYamlApiTask = project.getTasks()
|
||||
|
|
|
@ -49,7 +49,11 @@ public class RestTestUtil {
|
|||
* Setup the dependencies needed for the YAML REST tests.
|
||||
*/
|
||||
public static void setupYamlRestTestDependenciesDefaults(Project project, SourceSet sourceSet) {
|
||||
project.getDependencies().add(sourceSet.getImplementationConfigurationName(), project.project(":test:yaml-rest-runner"));
|
||||
Project yamlTestRunnerProject = project.findProject(":test:yaml-rest-runner");
|
||||
// we shield the project dependency to make integration tests easier
|
||||
if (yamlTestRunnerProject != null) {
|
||||
project.getDependencies().add(sourceSet.getImplementationConfigurationName(), yamlTestRunnerProject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,6 +61,10 @@ public class RestTestUtil {
|
|||
*/
|
||||
public static void setupJavaRestTestDependenciesDefaults(Project project, SourceSet sourceSet) {
|
||||
// TODO: this should just be test framework, but some cleanup is needed in places incorrectly specifying java vs yaml
|
||||
project.getDependencies().add(sourceSet.getImplementationConfigurationName(), project.project(":test:yaml-rest-runner"));
|
||||
// we shield the project dependency to make integration tests easier
|
||||
Project yamlTestRunnerProject = project.findProject(":test:yaml-rest-runner");
|
||||
if (yamlTestRunnerProject != null) {
|
||||
project.getDependencies().add(sourceSet.getImplementationConfigurationName(), yamlTestRunnerProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import com.avast.gradle.dockercompose.tasks.ComposeUp;
|
|||
import org.elasticsearch.gradle.internal.docker.DockerSupportPlugin;
|
||||
import org.elasticsearch.gradle.internal.docker.DockerSupportService;
|
||||
import org.elasticsearch.gradle.internal.info.BuildParams;
|
||||
import org.elasticsearch.gradle.internal.precommit.TestingConventionsTasks;
|
||||
import org.elasticsearch.gradle.test.SystemPropertyCommandLineArgumentProvider;
|
||||
import org.elasticsearch.gradle.util.GradleUtils;
|
||||
import org.gradle.api.Action;
|
||||
|
@ -139,7 +138,6 @@ public class TestFixturesPlugin implements Plugin<Project> {
|
|||
// Skip docker compose tasks if it is unavailable
|
||||
maybeSkipTasks(tasks, dockerSupport, Test.class);
|
||||
maybeSkipTasks(tasks, dockerSupport, getTaskClass("org.elasticsearch.gradle.internal.test.RestIntegTestTask"));
|
||||
maybeSkipTasks(tasks, dockerSupport, TestingConventionsTasks.class);
|
||||
maybeSkipTasks(tasks, dockerSupport, getTaskClass("org.elasticsearch.gradle.internal.test.AntFixture"));
|
||||
maybeSkipTasks(tasks, dockerSupport, ComposeUp.class);
|
||||
maybeSkipTasks(tasks, dockerSupport, ComposePull.class);
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionIT {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionTests {
|
||||
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
plugins {
|
||||
id 'elasticsearch.build' apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply plugin: 'java'
|
||||
apply plugin: org.elasticsearch.gradle.internal.precommit.TestingConventionsPrecommitPlugin
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation "junit:junit:4.12"
|
||||
}
|
||||
|
||||
testingConventions.naming {
|
||||
// Reset default to no baseClass checks
|
||||
Tests {
|
||||
baseClasses = []
|
||||
}
|
||||
IT {
|
||||
baseClasses = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project(':empty_test_task') {
|
||||
tasks.register("emptyTest", Test)
|
||||
}
|
||||
|
||||
project(':all_classes_in_tasks') {
|
||||
test {
|
||||
include "**/Convention*"
|
||||
}
|
||||
}
|
||||
|
||||
project(':not_implementing_base') {
|
||||
testingConventions.naming {
|
||||
Tests {
|
||||
baseClass 'org.elasticsearch.gradle.testkit.Unit'
|
||||
}
|
||||
IT {
|
||||
baseClass 'org.elasticsearch.gradle.testkit.Integration'
|
||||
}
|
||||
}
|
||||
test {
|
||||
include "**/*IT.class"
|
||||
include "**/*Tests.class"
|
||||
}
|
||||
}
|
||||
|
||||
project(':valid_setup_no_base') {
|
||||
test {
|
||||
include "**/*IT.class"
|
||||
include "**/*Tests.class"
|
||||
}
|
||||
}
|
||||
|
||||
project(':tests_in_main') {
|
||||
|
||||
}
|
||||
|
||||
project(':valid_setup_with_base') {
|
||||
test {
|
||||
include "**/*IT.class"
|
||||
include "**/*Tests.class"
|
||||
}
|
||||
testingConventions.naming {
|
||||
Tests {
|
||||
baseClass 'org.elasticsearch.gradle.testkit.Unit'
|
||||
}
|
||||
IT {
|
||||
baseClass 'org.elasticsearch.gradle.testkit.Integration'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class LooksLikeATestWithoutNamingConvention1 {
|
||||
|
||||
@Test
|
||||
public void annotatedTestMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
public class LooksLikeATestWithoutNamingConvention2 extends Assert {
|
||||
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class LooksLikeATestWithoutNamingConvention3 {
|
||||
|
||||
public void testMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public abstract class LooksLikeTestsButAbstract {
|
||||
|
||||
public void testMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionIT {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionTests {
|
||||
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NastyInnerClasses {
|
||||
|
||||
public static class NamingConventionTests {
|
||||
|
||||
}
|
||||
|
||||
public static class NamingConventionIT {
|
||||
|
||||
}
|
||||
|
||||
public static class LooksLikeATestWithoutNamingConvention1 {
|
||||
@Test
|
||||
public void annotatedTestMethod() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class LooksLikeATestWithoutNamingConvention2 extends Assert {
|
||||
|
||||
}
|
||||
|
||||
public static class LooksLikeATestWithoutNamingConvention3 {
|
||||
|
||||
public void testMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static abstract public class NonOffendingAbstractTests {
|
||||
|
||||
}
|
||||
|
||||
private static class NonOffendingPrivateTests {
|
||||
|
||||
}
|
||||
|
||||
static class NonOffendingPackageTests {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public abstract class AbstractIT {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class Integration {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionIT {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionMissmatchIT extends Unit {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionMissmatchTests extends Integration {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionTests {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class Unit {
|
||||
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
include 'no_tests_in_inner_classes'
|
||||
include 'incorrect_naming_conventions'
|
||||
include 'empty_test_task'
|
||||
include 'all_classes_in_tasks'
|
||||
include 'not_implementing_base'
|
||||
include 'valid_setup_no_base'
|
||||
include 'valid_setup_with_base'
|
||||
include 'tests_in_main'
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionIT {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionTests {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionIT {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionTests {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class Integration {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionIT extends Integration {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class NamingConventionTests extends Unit {
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
package org.elasticsearch.gradle.testkit;
|
||||
|
||||
public class Unit {
|
||||
|
||||
}
|
|
@ -10,14 +10,44 @@ package org.elasticsearch.gradle.fixtures
|
|||
|
||||
class AbstractJavaGradleFuncTest extends AbstractGradleFuncTest {
|
||||
|
||||
|
||||
File javaMainClass() {
|
||||
file('src/main/java/org/acme/JavaMainClass.java') << """
|
||||
public class JavaMainClass {
|
||||
}
|
||||
"""
|
||||
File testClazz(String testClassName) {
|
||||
testClazz(testClassName, null)
|
||||
}
|
||||
|
||||
File testClazz(String testClassName, String parent) {
|
||||
testClazz(testClassName, parent, null)
|
||||
}
|
||||
|
||||
File testClazz(String testClassName, Closure<String> content) {
|
||||
testClazz(testClassName, null, content)
|
||||
}
|
||||
|
||||
File testClazz(String testClassName, String parent, Closure<String> content) {
|
||||
clazz(dir("src/test/java"), testClassName, parent, content)
|
||||
}
|
||||
|
||||
File clazz(File sourceDir, String className, String parent = null, Closure<String> content = null) {
|
||||
def classFile = new File(sourceDir, "${className.replace('.', '/')}.java")
|
||||
classFile.getParentFile().mkdirs()
|
||||
writeClazz(className, parent, classFile, content)
|
||||
}
|
||||
|
||||
File clazz(String className, parent = null, Closure<String> content = null) {
|
||||
def classFile = file("src/main/java/${className.replace('.', '/')}.java")
|
||||
writeClazz(className, parent, classFile, content)
|
||||
}
|
||||
|
||||
static File writeClazz(String className, String parent, File classFile, Closure<String> content) {
|
||||
def packageName = className.substring(0, className.lastIndexOf('.'))
|
||||
def simpleClassName = className.substring(className.lastIndexOf('.') + 1)
|
||||
|
||||
classFile << """
|
||||
package ${packageName};
|
||||
public class ${simpleClassName} ${parent == null ? "" : "extends $parent"} {
|
||||
${content == null ? "" : content.call()}
|
||||
}
|
||||
"""
|
||||
classFile
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -76,13 +76,8 @@ tasks.named("jarHell").configure {
|
|||
enabled = false
|
||||
}
|
||||
|
||||
tasks.named("testingConventions").configure {
|
||||
naming.clear()
|
||||
naming {
|
||||
Tests {
|
||||
baseClass 'org.elasticsearch.client.RestClientTestCase'
|
||||
}
|
||||
}
|
||||
tasks.named('testTestingConventions').configure {
|
||||
baseClass 'org.elasticsearch.client.RestClientTestCase'
|
||||
}
|
||||
|
||||
tasks.named("thirdPartyAudit").configure {
|
||||
|
|
|
@ -50,6 +50,7 @@ tasks.named('forbiddenApisMain').configure {
|
|||
replaceSignatureFiles 'jdk-signatures'
|
||||
}
|
||||
|
||||
|
||||
tasks.named('forbiddenApisTest').configure {
|
||||
//we are using jdk-internal instead of jdk-non-portable to allow for com.sun.net.httpserver.* usage
|
||||
bundledSignatures -= 'jdk-non-portable'
|
||||
|
@ -67,13 +68,8 @@ tasks.named("dependencyLicenses").configure {
|
|||
// TODO: Not anymore. Now in :libs:elasticsearch-core
|
||||
tasks.named("jarHell").configure { enabled = false }
|
||||
|
||||
tasks.named("testingConventions").configure {
|
||||
naming.clear()
|
||||
naming {
|
||||
Tests {
|
||||
baseClass 'org.elasticsearch.client.RestClientTestCase'
|
||||
}
|
||||
}
|
||||
tasks.named("testTestingConventions").configure {
|
||||
baseClass 'org.elasticsearch.client.RestClientTestCase'
|
||||
}
|
||||
|
||||
tasks.named("thirdPartyAudit").configure {
|
||||
|
|
|
@ -25,12 +25,6 @@ tasks.named('forbiddenApisMain').configure {
|
|||
|
||||
// JAR hell is part of core which we do not want to add as a dependency
|
||||
tasks.named("jarHell").configure { enabled = false }
|
||||
|
||||
tasks.named("testingConventions").configure {
|
||||
naming.clear()
|
||||
naming {
|
||||
Tests {
|
||||
baseClass 'junit.framework.TestCase'
|
||||
}
|
||||
}
|
||||
tasks.named("testTestingConventions").configure {
|
||||
baseClass 'junit.framework.TestCase'
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@ esplugin {
|
|||
}
|
||||
}
|
||||
|
||||
tasks.named('internalClusterTestTestingConventions').configure {
|
||||
baseClass 'org.elasticsearch.ingest.geoip.AbstractGeoIpIT'
|
||||
baseClass 'org.elasticsearch.test.ESTestCase'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation('com.maxmind.geoip2:geoip2:3.0.0')
|
||||
// geoip2 dependencies:
|
||||
|
|
|
@ -115,5 +115,4 @@ if (BuildParams.inFipsJvm) {
|
|||
tasks.named("test").configure { enabled = false }
|
||||
tasks.named("yamlRestTest").configure { enabled = false };
|
||||
tasks.named("yamlRestTestV7CompatTest").configure { enabled = false };
|
||||
tasks.named("testingConventions").configure { enabled = false };
|
||||
}
|
||||
|
|
|
@ -22,6 +22,10 @@ if (BuildParams.isSnapshotBuild() == false) {
|
|||
}
|
||||
}
|
||||
|
||||
tasks.named('internalClusterTestTestingConventions').configure {
|
||||
baseClass 'org.elasticsearch.index.mapper.MapperTestCase'
|
||||
}
|
||||
|
||||
restResources {
|
||||
restApi {
|
||||
include '_common', 'indices', 'index', 'search'
|
||||
|
|
|
@ -17,14 +17,11 @@ testClusters.configureEach {
|
|||
extraConfigFile 'ingest-geoip/GeoLite2-City.mmdb', file("src/yamlRestTest/resources/GeoLite2-City.mmdb")
|
||||
}
|
||||
|
||||
//tasks.named("testingConventions").configure {
|
||||
// naming {
|
||||
// IT {
|
||||
// baseClass 'org.elasticsearch.ingest.AbstractScriptTestCase'
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
tasks.named("yamlRestTestTestingConventions").configure {
|
||||
baseClass 'org.elasticsearch.ingest.AbstractScriptTestCase'
|
||||
baseClass 'org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase'
|
||||
}
|
||||
|
||||
tasks.named("forbiddenPatterns").configure {
|
||||
exclude '**/*.mmdb'
|
||||
}
|
||||
}
|
|
@ -90,17 +90,10 @@ tasks.named('forbiddenApisTest').configure {
|
|||
addSignatureFiles 'hppc-signatures'
|
||||
}
|
||||
|
||||
tasks.named("testingConventions").configure {
|
||||
naming.clear()
|
||||
naming {
|
||||
Tests {
|
||||
baseClass "org.apache.lucene.tests.util.LuceneTestCase"
|
||||
}
|
||||
IT {
|
||||
baseClass "org.elasticsearch.test.ESIntegTestCase"
|
||||
baseClass "org.elasticsearch.test.ESSingleNodeTestCase"
|
||||
}
|
||||
}
|
||||
tasks.named('internalClusterTestTestingConventions').configure {
|
||||
baseClass "org.elasticsearch.test.AbstractMultiClustersTestCase"
|
||||
baseClass "org.elasticsearch.test.ESIntegTestCase"
|
||||
baseClass "org.elasticsearch.test.ESSingleNodeTestCase"
|
||||
}
|
||||
|
||||
File generatedResourcesDir = new File(buildDir, 'generated-resources')
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.elasticsearch.common.unit.ByteSizeUnit;
|
|||
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||
import org.elasticsearch.repositories.blobstore.ESFsBasedRepositoryIntegTestCase;
|
||||
|
||||
public class FsBlobStoreRepositoryIntegTests extends ESFsBasedRepositoryIntegTestCase {
|
||||
public class FsBlobStoreRepositoryIT extends ESFsBasedRepositoryIntegTestCase {
|
||||
|
||||
@Override
|
||||
protected Settings repositorySettings(String repositoryName) {
|
|
@ -32,6 +32,13 @@ dependencies {
|
|||
api "org.elasticsearch:mocksocket:${versions.mocksocket}"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
integTest {
|
||||
compileClasspath += sourceSets["main"].output + configurations["testRuntimeClasspath"]
|
||||
runtimeClasspath += output + compileClasspath
|
||||
}
|
||||
}
|
||||
|
||||
// the main files are actually test files, so use the appropriate forbidden api sigs
|
||||
tasks.named('forbiddenApisMain').configure {
|
||||
replaceSignatureFiles 'jdk-signatures', 'es-all-signatures', 'es-test-signatures'
|
||||
|
@ -41,7 +48,6 @@ tasks.named('forbiddenApisMain').configure {
|
|||
tasks.named("dependencyLicenses").configure { enabled = false }
|
||||
tasks.named("dependenciesInfo").configure { enabled = false }
|
||||
tasks.named("dependenciesGraph").configure { enabled = false }
|
||||
|
||||
tasks.named("thirdPartyAudit").configure {
|
||||
ignoreMissingClasses(
|
||||
// classes are missing
|
||||
|
@ -74,7 +80,12 @@ tasks.named("test").configure {
|
|||
}
|
||||
|
||||
tasks.register("integTest", Test) {
|
||||
include "**/*IT.class"
|
||||
testClassesDirs = sourceSets.integTest.output.classesDirs
|
||||
classpath = sourceSets.integTest.runtimeClasspath
|
||||
}
|
||||
|
||||
tasks.named('check').configure {
|
||||
dependsOn 'integTest'
|
||||
}
|
||||
|
||||
tasks.register("verifyVersions") {
|
||||
|
|
|
@ -22,7 +22,7 @@ import org.elasticsearch.xpack.autoscaling.action.GetAutoscalingCapacityAction;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class AutoscalingStorageIntegTestCase extends DiskUsageIntegTestCase {
|
||||
public abstract class AutoscalingStorageIntegTestCase extends DiskUsageIntegTestCase {
|
||||
protected static final long HIGH_WATERMARK_BYTES = 10240;
|
||||
protected static final long LOW_WATERMARK_BYTES = 2 * HIGH_WATERMARK_BYTES;
|
||||
|
||||
|
|
|
@ -25,6 +25,12 @@ tasks.named('internalClusterTest').configure {
|
|||
exclude noSecurityManagerITClasses
|
||||
}
|
||||
|
||||
tasks.named('internalClusterTestTestingConventions').configure {
|
||||
baseClass 'org.elasticsearch.xpack.CcrIntegTestCase'
|
||||
baseClass 'org.elasticsearch.xpack.CcrSingleNodeTestCase'
|
||||
baseClass 'org.elasticsearch.test.ESIntegTestCase'
|
||||
}
|
||||
|
||||
addQaCheckDependencies(project)
|
||||
|
||||
dependencies {
|
||||
|
@ -36,11 +42,3 @@ dependencies {
|
|||
testImplementation(project(":modules:analysis-common"))
|
||||
testImplementation(project(":modules:data-streams"))
|
||||
}
|
||||
|
||||
tasks.named("testingConventions").configure {
|
||||
naming {
|
||||
IT {
|
||||
baseClass "org.elasticsearch.xpack.CcrIntegTestCase"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -294,6 +294,4 @@ addQaCheckDependencies(project)
|
|||
if (BuildParams.inFipsJvm) {
|
||||
// We don't support the IDP in FIPS-140 mode, so no need to run tests
|
||||
tasks.named("test").configure { enabled = false }
|
||||
// We run neither integTests nor unit tests in FIPS-140 mode
|
||||
tasks.named("testingConventions").configure { enabled = false }
|
||||
}
|
||||
|
|
|
@ -59,7 +59,4 @@ tasks.named("check").configure { dependsOn 'follow-cluster' }
|
|||
// Security is explicitly disabled for follow-cluster and leader-cluster, do not run these in FIPS mode
|
||||
tasks.withType(Test).configureEach {
|
||||
enabled = BuildParams.inFipsJvm == false
|
||||
}
|
||||
tasks.named("testingConventions").configure {
|
||||
enabled = BuildParams.inFipsJvm == false
|
||||
}
|
||||
}
|
|
@ -18,6 +18,10 @@ dependencies {
|
|||
internalClusterTestImplementation(testArtifact(project(xpackModule('core'))))
|
||||
}
|
||||
|
||||
tasks.named('internalClusterTestTestingConventions').configure {
|
||||
baseClass 'org.elasticsearch.index.mapper.MapperTestCase'
|
||||
}
|
||||
|
||||
if (BuildParams.isSnapshotBuild() == false) {
|
||||
tasks.named("internalClusterTest").configure {
|
||||
systemProperty 'es.index_mode_feature_flag_registered', 'true'
|
||||
|
|
|
@ -12,3 +12,8 @@ if (BuildParams.inFipsJvm){
|
|||
// Test clusters run with security disabled
|
||||
tasks.named("javaRestTest").configure{enabled = false }
|
||||
}
|
||||
|
||||
tasks.named('javaRestTestTestingConventions').configure {
|
||||
baseClass "org.elasticsearch.xpack.ml.integration.InferenceTestCase"
|
||||
baseClass "org.elasticsearch.test.rest.ESRestTestCase"
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.util.Set;
|
|||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class InferenceTestCase extends ESRestTestCase {
|
||||
public abstract class InferenceTestCase extends ESRestTestCase {
|
||||
|
||||
protected final Set<String> createdPipelines = new HashSet<>();
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import org.elasticsearch.common.unit.ByteSizeValue;
|
|||
import org.elasticsearch.common.unit.RatioValue;
|
||||
import org.elasticsearch.xpack.searchablesnapshots.cache.shared.FrozenCacheService;
|
||||
|
||||
public class BaseFrozenSearchableSnapshotsIntegTestCase extends BaseSearchableSnapshotsIntegTestCase {
|
||||
public abstract class BaseFrozenSearchableSnapshotsIntegTestCase extends BaseSearchableSnapshotsIntegTestCase {
|
||||
@Override
|
||||
protected boolean forceSingleDataPath() {
|
||||
return true;
|
||||
|
|
|
@ -49,9 +49,6 @@ if (BuildParams.inFipsJvm) {
|
|||
tasks.named("jarHell").configure {
|
||||
enabled = false
|
||||
}
|
||||
tasks.named("testingConventions").configure {
|
||||
enabled = false
|
||||
}
|
||||
// Forbiden APIs non-portable checks fail because bouncy castle classes being used from the FIPS JDK since those are
|
||||
// not part of the Java specification - all of this is as designed, so we have to relax this check for FIPS.
|
||||
tasks.withType(CheckForbiddenApis).configureEach {
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.security.operator;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
// This test class is really to pass the testingConventions test
|
||||
public class OperatorPrivilegesTestPluginTests extends ESTestCase {
|
||||
|
||||
public void testPluginWillInstantiate() {
|
||||
final OperatorPrivilegesTestPlugin operatorPrivilegesTestPlugin = new OperatorPrivilegesTestPlugin();
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,7 @@ import static org.hamcrest.Matchers.equalTo;
|
|||
// This test is to ensure that an operator can always successfully perform operator-only actions
|
||||
// even when operator privileges are partially enabled for the cluster. This could happen
|
||||
// for a cluster with operator privileges disabled wanting to enable operator privileges with rolling upgrade.
|
||||
public class OperatorPrivilegesPartiallyEnabledIntegTestCase extends SecurityIntegTestCase {
|
||||
public class OperatorPrivilegesPartiallyEnabledIntegTests extends SecurityIntegTestCase {
|
||||
|
||||
private static final String OPERATOR_USER_NAME = "test_operator";
|
||||
private static final String OPERATOR_AUTH_HEADER = "Basic "
|
|
@ -18,6 +18,11 @@ testClusters.matching { it.name == "yamlRestTest" }.configureEach {
|
|||
setting 'xpack.security.enabled', 'false'
|
||||
}
|
||||
|
||||
tasks.named('yamlRestTestTestingConventions').configure {
|
||||
baseClass 'org.elasticsearch.repositories.blobstore.testkit.AbstractSnapshotRepoTestKitRestTestCase'
|
||||
baseClass 'org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase'
|
||||
}
|
||||
|
||||
restResources {
|
||||
restApi {
|
||||
include 'indices', 'search', 'bulk', 'snapshot', 'nodes', '_common', 'snapshot_repo_test_kit'
|
||||
|
|
|
@ -16,5 +16,3 @@ dependencies {
|
|||
}
|
||||
|
||||
addQaCheckDependencies(project)
|
||||
|
||||
tasks.named("testingConventions").configure { enabled = false }
|
||||
|
|
|
@ -10,6 +10,11 @@ dependencies {
|
|||
yamlRestTestImplementation(project(":client:rest-high-level"))
|
||||
}
|
||||
|
||||
tasks.named('yamlRestTestTestingConventions').configure {
|
||||
baseClass 'org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase'
|
||||
baseClass 'org.elasticsearch.test.rest.ESRestTestCase'
|
||||
}
|
||||
|
||||
tasks.named("forbiddenPatterns").configure {
|
||||
exclude '**/*.key'
|
||||
exclude '**/*.pem'
|
||||
|
|
|
@ -49,7 +49,6 @@ restResources {
|
|||
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
logger.warn("Disabling repository-old-versions tests because we can't get the pid file on windows")
|
||||
tasks.named("testingConventions").configure { enabled = false }
|
||||
} else {
|
||||
/* Register a gradle artifact transformation to unpack resolved elasticsearch distributions. We only resolve
|
||||
* zip files here. Using artifact transforms allow a better caching of the downloaded distros as the
|
||||
|
|
|
@ -79,8 +79,4 @@ BuildParams.bwcVersions.withWireCompatible { bwcVersion, baseName ->
|
|||
// Security is explicitly disabled, do not run tests in FIPS mode
|
||||
tasks.withType(Test).configureEach {
|
||||
enabled = BuildParams.inFipsJvm == false
|
||||
}
|
||||
|
||||
tasks.named("testingConventions").configure {
|
||||
enabled = BuildParams.inFipsJvm == false
|
||||
}
|
||||
}
|
|
@ -92,3 +92,8 @@ tasks.named("processYamlRestTestResources").configure {
|
|||
filter("tokens" : expansions.collectEntries {k, v -> [k, v.toString()]} /* must be a map of strings */, ReplaceTokens.class)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('yamlRestTestTestingConventions').configure {
|
||||
baseClass 'org.elasticsearch.test.rest.ESRestTestCase'
|
||||
baseClass 'org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase'
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue