/* * 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. */ // // Configure test randomization seeds and derived test properties. // import java.nio.file.* import com.carrotsearch.randomizedtesting.SeedUtils import com.carrotsearch.randomizedtesting.generators.RandomPicks import org.apache.tools.ant.types.Commandline buildscript { repositories { mavenCentral() } dependencies { classpath 'com.carrotsearch.randomizedtesting:randomizedtesting-runner:2.7.2' } } def resources = scriptResources(buildscript) // Pick the "root" seed from which everything else is derived. configure(rootProject) { ext { rootSeed = propertyOrDefault('tests.seed', String.format("%08X", new Random().nextLong())) rootSeedUserProvided = (propertyOrDefault('tests.seed', null) != null) rootSeedLong = SeedUtils.parseSeedChain(rootSeed)[0] projectSeedLong = rootSeedLong ^ project.path.hashCode() } task randomizationInfo() { doFirst { logger.lifecycle("Running tests with randomization seed: tests.seed=${rootSeed}") } } } // Any test task will trigger display of randomization settings. allprojects { tasks.withType(Test) { task -> task.dependsOn rootProject.randomizationInfo } } // Configure test property defaults and their descriptions. allprojects { plugins.withType(JavaPlugin) { ext { testOptions += [ // seed, repetition and amplification. [propName: 'tests.seed', value: { -> rootSeed }, description: "Sets the master randomization seed."], [propName: 'tests.iters', value: null, description: "Duplicate (re-run) each test case N times."], [propName: 'tests.multiplier', value: null, description: "Value multiplier for randomized tests."], [propName: 'tests.maxfailures', value: null, description: "Skip tests after a given number of failures."], [propName: 'tests.timeoutSuite', value: null, description: "Timeout (in millis) for an entire suite."], [propName: 'tests.failfast', value: "false", description: "Stop the build early on failure.", buildOnly: true], // asserts, debug output. [propName: 'tests.asserts', value: "true", description: "Enables or disables assertions mode."], [propName: 'tests.infostream', value: false, description: "Enables or disables infostream logs."], [propName: 'tests.leaveTemporary', value: false, description: "Leave temporary directories after tests complete."], [propName: 'tests.useSecurityManager', value: true, description: "Control security manager in tests.", buildOnly: true], // component randomization [propName: 'tests.codec', value: "random", description: "Sets the codec tests should run with."], [propName: 'tests.directory', value: "random", description: "Sets the Directory implementation tests should run with."], [propName: 'tests.postingsformat', value: "random", description: "Sets the postings format tests should run with."], [propName: 'tests.docvaluesformat', value: "random", description: "Sets the doc values format tests should run with."], [propName: 'tests.locale', value: "random", description: "Sets the default locale tests should run with."], [propName: 'tests.timezone', value: "random", description: "Sets the default time zone tests should run with."], // filtering [propName: 'tests.filter', value: null, description: "Applies a test filter (see :helpTests)."], [propName: 'tests.nightly', value: false, description: "Enables or disables @Nightly tests."], [propName: 'tests.weekly', value: false, description: "Enables or disables @Weekly tests."], [propName: 'tests.monster', value: false, description: "Enables or disables @Monster tests."], [propName: 'tests.awaitsfix', value: null, description: "Enables or disables @AwaitsFix tests."], [propName: 'tests.gui', value: { -> return rootProject.ext.isCIBuild }, description: "Enables or disables @RequiresGUI tests."], [propName: 'tests.file.encoding', value: { -> RandomPicks.randomFrom(new Random(projectSeedLong), ["US-ASCII", "ISO-8859-1", "UTF-8"]) }, description: "Sets the default file.encoding on test JVM.", buildOnly: true], // Test data file used. [propName: 'tests.linedocsfile', value: 'europarl.lines.txt.gz', description: "Test data file path."], // miscellaneous; some of them very weird. [propName: 'tests.LUCENE_VERSION', value: baseVersion, description: "Base Lucene version."], [propName: 'tests.bwcdir', value: null, description: "Data for backward-compatibility indexes."], // vectorization related [propName: 'tests.vectorsize', value: { -> RandomPicks.randomFrom(new Random(projectSeedLong), ["128", "256", "512"]) }, description: "Sets preferred vector size in bits."], [propName: 'tests.forceintegervectors', value: "true", description: "Forces use of integer vectors even when slow."], ] } } } // Resolve test option values after all evaluation is complete. allprojects { plugins.withType(JavaPlugin) { afterEvaluate { ext.testOptionsResolved = testOptions.findAll { opt -> propertyOrDefault(opt.propName, opt.value) != null }.collectEntries { opt -> [(opt.propName): Objects.toString(resolvedTestOption(opt.propName))] } // Compute the "reproduce with" string. ext.testOptionsForReproduceLine = testOptions.findAll { opt -> if (opt["includeInReproLine"] == false) { return false } def defValue = Objects.toString(opt.value, null) def value = testOptionsResolved[opt.propName] return defValue != value }.collect { opt -> Commandline.quoteArgument("-P" + opt.propName + "=" + testOptionsResolved[opt.propName]) }.join(" ") // leaving temporary folder option has multiple aliases... if ([ "tests.leaveTemporary", "tests.leavetemporary", "tests.leavetmpdir", "solr.test.leavetmpdir", ].find { prop -> def v = Boolean.parseBoolean(propertyOrDefault(prop, "false")) if (v) { logger.lifecycle("Update your code to use the official 'tests.leaveTemporary' option (you used '${prop}').") } return v }) { testOptionsResolved['tests.leaveTemporary'] = "true" } // Append resolved test properties to the test task. tasks.withType(Test) { task -> // TODO: we could remove some options that are only relevant to the build environment // and not the test JVM itself. systemProperties testOptionsResolved if (Boolean.parseBoolean(testOptionsResolved['tests.asserts'])) { jvmArgs("-ea", "-esa") } else { enableAssertions = false } if (Boolean.parseBoolean(testOptionsResolved["tests.failfast"])) { failFast true } // Enable security manager, if requested. We could move the selection of security manager and security policy // to each project's build/ configuration but it seems compact enough to keep it here for now. def securityArgumentProvider = new SecurityArgumentProvider(commonDir: project(":lucene").layout.projectDirectory) if (Boolean.parseBoolean(testOptionsResolved["tests.useSecurityManager"])) { if (project.path.endsWith(".tests")) { // LUCENE-10301: for now, do not use the security manager for modular tests (test framework is not available). } else if (project.path == ":lucene:replicator") { systemProperty 'java.security.manager', "org.apache.lucene.tests.util.TestSecurityManager" securityArgumentProvider.javaSecurityPolicy = layout.projectDirectory.file("${resources}/policies/replicator-tests.policy") } else if (project.path.startsWith(":lucene")) { systemProperty 'java.security.manager', "org.apache.lucene.tests.util.TestSecurityManager" securityArgumentProvider.javaSecurityPolicy = layout.projectDirectory.file("${resources}/policies/tests.policy") } jvmArgumentProviders.add(securityArgumentProvider) def gradleUserHome = project.gradle.getGradleUserHomeDir() systemProperty 'gradle.lib.dir', Paths.get(project.class.location.toURI()).parent.toAbsolutePath().toString().replace('\\', '/') systemProperty 'gradle.worker.jar', Paths.get("${gradleUserHome}/caches/${gradle.gradleVersion}/workerMain/gradle-worker.jar").toAbsolutePath().toString() systemProperty 'gradle.user.home', gradleUserHome.toPath().toAbsolutePath().toString() } doFirst { logger.debug("Will use test opts:\n" + testOptionsResolved.collect {k,v -> "${k}: ${v}"}.sort().join("\n")) } } } } } // Add a helper task to display resolved test property values with their defaults // and descriptions. allprojects { plugins.withType(JavaPlugin) { task testOpts() { group = 'Help (developer guides and hints)' description = "Display values of randomization settings for a given seed" doFirst { println "Test options for project ${project.path} and seed \"${rootSeed}\":" testOptions.sort { a, b -> a.propName.compareTo(b.propName) }.each { opt -> def defValue def computedValue = false if (opt.value instanceof Closure) { defValue = Objects.toString(opt.value(), null) computedValue = true } else { defValue = Objects.toString(opt.value, null) } def value = testOptionsResolved[opt.propName] println String.format(Locale.ROOT, "%s%-24s = %-8s # %s", (defValue != value ? "! " : computedValue ? "C " : " "), opt.propName, value, (computedValue ? "(!= default: computed) " : (defValue != value ? "(!= default: ${defValue}) " : "")) + opt.description) } } } } } class SecurityArgumentProvider implements CommandLineArgumentProvider { @Internal Directory commonDir @InputFile @Optional @PathSensitive(PathSensitivity.RELATIVE) RegularFile javaSecurityPolicy @Override Iterable asArguments() { def args = ["-Dcommon.dir=${commonDir.getAsFile()}"] if (javaSecurityPolicy) { args.add("-Djava.security.policy=${javaSecurityPolicy.getAsFile()}") } return args } }