This is an automated email from the ASF dual-hosted git repository. upthewaterspout pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push: new 082f1d5 GEODE-5388: Adding a gradle task to repeat a test 082f1d5 is described below commit 082f1d5619acf5fab93017cc200d58162c7c3021 Author: Dan Smith <upthewatersp...@apache.org> AuthorDate: Mon Jul 9 13:21:56 2018 -0700 GEODE-5388: Adding a gradle task to repeat a test Example ./gradlew repeatTest --tests PartitionedRegionHADUnitTest.testBucketFailOverDuringCacheClose -Prepeat=100 --- .../org/apache/geode/gradle/RepeatTest.groovy | 70 +++++++++++ .../geode/gradle/OverriddenTestExecutor.java | 134 +++++++++++++++++++++ gradle.properties | 3 + gradle/docker.gradle | 1 + gradle/test.gradle | 13 +- 5 files changed, 219 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/groovy/org/apache/geode/gradle/RepeatTest.groovy b/buildSrc/src/main/groovy/org/apache/geode/gradle/RepeatTest.groovy new file mode 100644 index 0000000..9109f96 --- /dev/null +++ b/buildSrc/src/main/groovy/org/apache/geode/gradle/RepeatTest.groovy @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.gradle; + +import org.gradle.StartParameter; +import org.gradle.api.file.FileTree; +import org.gradle.api.internal.DocumentationRegistry; +import org.gradle.api.internal.tasks.testing.JvmTestExecutionSpec; +import org.gradle.api.internal.tasks.testing.TestExecuter +import org.gradle.api.internal.tasks.testing.detection.DefaultTestExecuter; +import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter; +import org.gradle.api.tasks.testing.Test; +import org.gradle.internal.operations.BuildOperationExecutor; +import org.gradle.internal.time.Clock; +import org.gradle.internal.work.WorkerLeaseRegistry +import org.gradle.process.internal.worker.WorkerProcessFactory; + +class RepeatTest extends Test { + int times = 1; + + @Override + FileTree getCandidateClassFiles() { + FileTree candidates = super.getCandidateClassFiles(); + for (int i = 0; i < times; i++) { + candidates = candidates.plus(super.getCandidateClassFiles()); + } + + return candidates; + } + + /* + * We have to override gradles default test executor, because that uses {@link RunPreviousFailedFirstTestClassProcessor} + * Which deduplicates the test specs we're passing in + */ + @Override + protected TestExecuter<JvmTestExecutionSpec> createTestExecuter() { + def oldExecutor = super.createTestExecuter() + + def workerProcessFactory = getProcessBuilderFactory() + + //Use the previously set worker process factory. If the test is + //being run using the parallel docker plugin, this will be a docker + //process factory + if(oldExecutor instanceof DefaultTestExecuter) { + workerProcessFactory = oldExecutor.workerFactory + } + + return new OverriddenTestExecutor(workerProcessFactory, getActorFactory(), + getModuleRegistry(), + getServices().get(WorkerLeaseRegistry.class), + getServices().get(BuildOperationExecutor.class), + getServices().get(StartParameter.class).getMaxWorkerCount(), + getServices().get(Clock.class), + getServices().get(DocumentationRegistry.class), + (DefaultTestFilter) getFilter()); + } + +} diff --git a/buildSrc/src/main/java/org/apache/geode/gradle/OverriddenTestExecutor.java b/buildSrc/src/main/java/org/apache/geode/gradle/OverriddenTestExecutor.java new file mode 100644 index 0000000..4984bb4 --- /dev/null +++ b/buildSrc/src/main/java/org/apache/geode/gradle/OverriddenTestExecutor.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.gradle; + +import java.io.File; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; +import org.gradle.api.file.FileTree; +import org.gradle.api.internal.DocumentationRegistry; +import org.gradle.api.internal.classpath.ModuleRegistry; +import org.gradle.api.internal.tasks.testing.JvmTestExecutionSpec; +import org.gradle.api.internal.tasks.testing.TestClassProcessor; +import org.gradle.api.internal.tasks.testing.TestExecuter; +import org.gradle.api.internal.tasks.testing.TestFramework; +import org.gradle.api.internal.tasks.testing.TestResultProcessor; +import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory; +import org.gradle.api.internal.tasks.testing.detection.DefaultTestClassScanner; +import org.gradle.api.internal.tasks.testing.detection.TestFrameworkDetector; +import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter; +import org.gradle.api.internal.tasks.testing.processors.MaxNParallelTestClassProcessor; +import org.gradle.api.internal.tasks.testing.processors.PatternMatchTestClassProcessor; +import org.gradle.api.internal.tasks.testing.processors.RestartEveryNTestClassProcessor; +import org.gradle.api.internal.tasks.testing.processors.RunPreviousFailedFirstTestClassProcessor; +import org.gradle.api.internal.tasks.testing.processors.TestMainAction; +import org.gradle.api.internal.tasks.testing.worker.ForkingTestClassProcessor; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.internal.Factory; +import org.gradle.internal.actor.ActorFactory; +import org.gradle.internal.operations.BuildOperationExecutor; +import org.gradle.internal.time.Clock; +import org.gradle.internal.work.WorkerLeaseRegistry; +import org.gradle.process.internal.worker.WorkerProcessFactory; + +/** + * Test executor that is used to replace gradles DefaultTestExecutor and does + * not include a {@link RunPreviousFailedFirstTestClassProcessor} in the processor + * chain. This is used by the RepeatTest task. + */ +class OverriddenTestExecutor implements TestExecuter<JvmTestExecutionSpec> { + private static final Logger LOGGER = Logging.getLogger(OverriddenTestExecutor.class); + + private final WorkerProcessFactory workerFactory; + private final ActorFactory actorFactory; + private final ModuleRegistry moduleRegistry; + private final WorkerLeaseRegistry workerLeaseRegistry; + private final BuildOperationExecutor buildOperationExecutor; + private final int maxWorkerCount; + private final Clock clock; + private final DocumentationRegistry documentationRegistry; + private final DefaultTestFilter testFilter; + private TestClassProcessor processor; + + public OverriddenTestExecutor(WorkerProcessFactory workerFactory, ActorFactory actorFactory, ModuleRegistry moduleRegistry, + WorkerLeaseRegistry workerLeaseRegistry, BuildOperationExecutor buildOperationExecutor, int maxWorkerCount, + Clock clock, DocumentationRegistry documentationRegistry, DefaultTestFilter testFilter) { + this.workerFactory = workerFactory; + this.actorFactory = actorFactory; + this.moduleRegistry = moduleRegistry; + this.workerLeaseRegistry = workerLeaseRegistry; + this.buildOperationExecutor = buildOperationExecutor; + this.maxWorkerCount = maxWorkerCount; + this.clock = clock; + this.documentationRegistry = documentationRegistry; + this.testFilter = testFilter; + } + + @Override + public void execute(final JvmTestExecutionSpec testExecutionSpec, TestResultProcessor testResultProcessor) { + final TestFramework testFramework = testExecutionSpec.getTestFramework(); + final WorkerTestClassProcessorFactory testInstanceFactory = testFramework.getProcessorFactory(); + final WorkerLeaseRegistry.WorkerLease currentWorkerLease = workerLeaseRegistry.getCurrentWorkerLease(); + final Set<File> classpath = ImmutableSet.copyOf(testExecutionSpec.getClasspath()); + final Factory<TestClassProcessor> forkingProcessorFactory = new Factory<TestClassProcessor>() { + public TestClassProcessor create() { + return new ForkingTestClassProcessor(currentWorkerLease, workerFactory, testInstanceFactory, testExecutionSpec.getJavaForkOptions(), + classpath, testFramework.getWorkerConfigurationAction(), moduleRegistry, documentationRegistry); + } + }; + final Factory<TestClassProcessor> reforkingProcessorFactory = new Factory<TestClassProcessor>() { + public TestClassProcessor create() { + return new RestartEveryNTestClassProcessor(forkingProcessorFactory, testExecutionSpec.getForkEvery()); + } + }; + processor = + new PatternMatchTestClassProcessor(testFilter, + new MaxNParallelTestClassProcessor(getMaxParallelForks(testExecutionSpec), reforkingProcessorFactory, actorFactory)); + + final FileTree testClassFiles = testExecutionSpec.getCandidateClassFiles(); + + Runnable detector; + if (testExecutionSpec.isScanForTestClasses() && testFramework.getDetector() != null) { + TestFrameworkDetector testFrameworkDetector = testFramework.getDetector(); + testFrameworkDetector.setTestClasses(testExecutionSpec.getTestClassesDirs().getFiles()); + testFrameworkDetector.setTestClasspath(classpath); + detector = new DefaultTestClassScanner(testClassFiles, testFrameworkDetector, processor); + } else { + detector = new DefaultTestClassScanner(testClassFiles, null, processor); + } + + final Object testTaskOperationId = buildOperationExecutor.getCurrentOperation().getParentId(); + + new TestMainAction(detector, processor, testResultProcessor, clock, testTaskOperationId, testExecutionSpec.getPath(), "Gradle Test Run " + testExecutionSpec.getIdentityPath()).run(); + } + + @Override + public void stopNow() { + if (processor != null) { + processor.stopNow(); + } + } + + private int getMaxParallelForks(JvmTestExecutionSpec testExecutionSpec) { + int maxParallelForks = testExecutionSpec.getMaxParallelForks(); + if (maxParallelForks > maxWorkerCount) { + LOGGER.info("{}.maxParallelForks ({}) is larger than max-workers ({}), forcing it to {}", testExecutionSpec.getPath(), maxParallelForks, maxWorkerCount, maxWorkerCount); + maxParallelForks = maxWorkerCount; + } + return maxParallelForks; + } +} diff --git a/gradle.properties b/gradle.properties index f449867..dc7e5c0 100755 --- a/gradle.properties +++ b/gradle.properties @@ -60,6 +60,9 @@ dunitDockerUser = root #JVM to be used by tests testJVM= +repeat = 100 + org.gradle.caching = true org.gradle.parallel = false org.gradle.configureondemand = false + diff --git a/gradle/docker.gradle b/gradle/docker.gradle index 96de69d..4cafbf2 100644 --- a/gradle/docker.gradle +++ b/gradle/docker.gradle @@ -108,5 +108,6 @@ subprojects { if (project.hasProperty('parallelDunit')) { distributedTest.configure(dockerConfig) + repeatTest.configure(dockerConfig) } } diff --git a/gradle/test.gradle b/gradle/test.gradle index 18ce11b..0987c02 100644 --- a/gradle/test.gradle +++ b/gradle/test.gradle @@ -1,4 +1,5 @@ import org.apache.geode.gradle.TestPropertiesWriter +import org.apache.geode.gradle.RepeatTest /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -133,6 +134,14 @@ subprojects { outputs.upToDateWhen{false} } + task repeatTest(type:RepeatTest) { + times= Integer.parseInt(repeat) + + useJUnit {} + + outputs.upToDateWhen{false} + } + task flakyTest(type:Test) { useJUnit { includeCategories 'org.apache.geode.test.junit.categories.FlakyTest' @@ -417,6 +426,6 @@ subprojects { check.dependsOn checkMissedTests - combineReports.mustRunAfter check, test, integrationTest, distributedTest, flakyTest, checkMissedTests, acceptanceTest - [build, check, test, integrationTest, distributedTest, flakyTest, checkMissedTests, acceptanceTest].each {it.finalizedBy combineReports} + combineReports.mustRunAfter check, test, integrationTest, distributedTest, flakyTest, checkMissedTests, acceptanceTest, repeatTest + [build, check, test, integrationTest, distributedTest, flakyTest, checkMissedTests, acceptanceTest, repeatTest].each {it.finalizedBy combineReports} }