diff --git a/build.gradle b/build.gradle index 4305712048b..ac6b859230c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,4 @@ + plugins { id "base" id "com.palantir.consistent-versions" version "1.12.4" @@ -27,7 +28,6 @@ apply from: file('gradle/ci/travis.gradle') apply from: file('gradle/defaults.gradle') apply from: file('gradle/defaults-java.gradle') apply from: file('gradle/testing/defaults-tests.gradle') -apply from: file('gradle/testing/defaults-tests-solr.gradle') apply from: file('gradle/testing/randomization.gradle') apply from: file('gradle/testing/fail-on-no-tests.gradle') diff --git a/gradle/generate-defaults.gradle b/gradle/generate-defaults.gradle index 72f0dafd89f..2e68cd112a7 100644 --- a/gradle/generate-defaults.gradle +++ b/gradle/generate-defaults.gradle @@ -62,7 +62,7 @@ if (!hasDefaults) { } } - // Make all tasks depend on local setup to make sure it'll run though. + // Make all tasks depend on local setup to make sure it'll run. allprojects { tasks.all { task -> if (task != rootProject.setupLocalDefaultsOnce) { diff --git a/gradle/testing/defaults-tests-solr.gradle b/gradle/testing/defaults-tests-solr.gradle deleted file mode 100644 index be84a59f6e4..00000000000 --- a/gradle/testing/defaults-tests-solr.gradle +++ /dev/null @@ -1,13 +0,0 @@ -import org.apache.tools.ant.taskdefs.condition.Os - -// Solr-specific test configs. - -configure(allprojects.findAll {project -> project.path.startsWith(":solr") }) { - plugins.withType(JavaPlugin) { - test { - systemProperty 'tests.disableHdfs', Os.isFamily(Os.FAMILY_WINDOWS) ? 'true' : 'false' - systemProperty 'jetty.testMode', '1' - systemProperty 'jetty.insecurerandom', '1' - } - } -} diff --git a/gradle/testing/defaults-tests.gradle b/gradle/testing/defaults-tests.gradle index d685328b763..8a6860cf258 100644 --- a/gradle/testing/defaults-tests.gradle +++ b/gradle/testing/defaults-tests.gradle @@ -4,6 +4,7 @@ import org.gradle.api.tasks.testing.logging.* allprojects { plugins.withType(JavaPlugin) { project.ext { + commonDir = project(":lucene").projectDir testsWorkDir = file("${buildDir}/tmp/tests-cwd") testsTmpDir = file("${buildDir}/tmp/tests-tmp") } @@ -15,9 +16,14 @@ allprojects { maxParallelForks = propertyOrDefault("tests.jvms", (int) Math.max(1, Math.min(Runtime.runtime.availableProcessors() / 2.0, 4.0))) + if (Boolean.parseBoolean(propertyOrDefault("tests.failfast", 'false'))) { + failFast true + } + minHeapSize = "256m" maxHeapSize = "512m" + systemProperty 'java.util.logging.config.file', file("${commonDir}/tools/junit4/logging.properties") systemProperty 'java.awt.headless', 'true' systemProperty 'jdk.map.althashing.threshold', '0' @@ -25,8 +31,16 @@ allprojects { systemProperty 'java.security.egd', 'file:/dev/./urandom' } + // jetty-related. + systemProperty 'jetty.testMode', '1' + systemProperty 'jetty.insecurerandom', '1' + + // Turn jenkins blood red for hashmap bugs, even on jdk7 + systemProperty 'jdk.map.althashing.threshold', '0' + // Set up cwd and temp locations. systemProperty("java.io.tmpdir", testsTmpDir) + systemProperty("tempDir", testsTmpDir) doFirst { testsWorkDir.mkdirs() testsTmpDir.mkdirs() diff --git a/gradle/testing/randomization.gradle b/gradle/testing/randomization.gradle index 6330777b1ed..6042676e081 100644 --- a/gradle/testing/randomization.gradle +++ b/gradle/testing/randomization.gradle @@ -1,10 +1,27 @@ - +// // Configure test randomization seeds and derived test properties. +// + +import org.apache.tools.ant.taskdefs.condition.Os +import com.carrotsearch.randomizedtesting.SeedUtils +import com.carrotsearch.randomizedtesting.generators.RandomPicks + +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath 'com.carrotsearch.randomizedtesting:randomizedtesting-runner:2.7.2' + } +} // Pick the "root" seed from which everything else is derived. configure(rootProject) { ext { rootSeed = propertyOrDefault('tests.seed', String.format("%08X", new Random().nextLong())) + rootSeedLong = SeedUtils.parseSeedChain(rootSeed)[0] + projectSeedLong = rootSeedLong ^ project.path.hashCode() } task randomizationInfo() { @@ -21,35 +38,167 @@ allprojects { } } -// Append randomization properties to tests, allow overriding these properties with -Pkey=value. +// Configure test property defaults and their descriptions. allprojects { - tasks.withType(Test) { task -> - [ - 'tests.seed': rootSeed, - 'tests.multiplier': '1', - 'tests.codec': 'random', - 'tests.postingsformat': 'random', - 'tests.docvaluesformat': 'random', - 'tests.locale': 'random', - 'tests.timezone': 'random', - 'tests.directory': 'random', - 'tests.nightly': 'false', - 'tests.weekly': 'false', - 'tests.monster': 'false', - 'tests.slow': 'true', - 'tests.verbose': 'false', - 'tests.filterstacks': 'true', - 'tests.asserts': 'true', - 'tests.iters': null, - 'tests.filter': null, - 'tests.linedocsfile': 'europarl.lines.txt.gz', - 'tests.cleanthreads.sysprop': 'perMethod' - ].each { propName, defValue -> - def value = propertyOrDefault(propName, defValue) - if (value != null) { - systemProperty propName, value + 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 N times."], + [propName: 'tests.multiplier', value: 1, 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."], + // asserts, debug output. + [propName: 'tests.asserts', value: "true", description: "Enables or disables assertions mode."], + [propName: 'tests.verbose', value: false, description: "Emit verbose debug information from tests."], + [propName: 'tests.infostream', value: null, description: "Enables or disables infostream logs."], + [propName: 'tests.leaveTemporary', value: null, description: "Leave temporary directories after tests complete."], + // 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.slow', value: true, description: "Enables or disables @Slow tests."], + [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.file.encoding', value: "random", description: "Sets the default file.encoding on test JVM."], + // test data + [propName: 'tests.linedocsfile', value: 'europarl.lines.txt.gz', description: "Test data file path."], + // miscellaneous; some of them very weird. + [propName: 'tests.LUCENE_VERSION', value: rootProject.version, description: "Base Lucene version."], + [propName: 'tests.bwcdir', value: null, description: "Data for backward-compatibility indexes."], + ] + } + } +} + +// Add Solr-specific test configs settings. +configure(allprojects.findAll {project -> project.path.startsWith(":solr") }) { + plugins.withType(JavaPlugin) { + ext { + testOptions += [ + [propName: 'tests.disableHdfs', value: Os.isFamily(Os.FAMILY_WINDOWS) ? 'true' : 'false', description: "Enables or disables @HDFS tests."], + [propName: 'tests.luceneMatchVersion', value: rootProject.version, description: "Base Lucene version."], + [propName: 'common-solr.dir', value: file("${commonDir}/../solr").path, description: "Solr base dir."], + [propName: 'solr.directoryFactory', value: "org.apache.solr.core.MockDirectoryFactory", description: "Solr directory factory."], + [propName: 'tests.src.home', value: null, description: "See SOLR-14023."], + [propName: 'solr.tests.use.numeric.points', value: null, description: "Point implementation to use (true=numerics, false=trie)."], + ] + } + } +} + +// 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): propertyOrDefault(opt.propName, opt.value)] + } + + // These are not official options or dynamically seed-derived options. + testOptionsResolved['tests.infostream'] = propertyOrDefault('tests.infostream', testOptionsResolved['tests.verbose']) + if (testOptionsResolved['tests.file.encoding'] == 'random') { + testOptionsResolved['tests.file.encoding'] = RandomPicks.randomFrom( + new Random(projectSeedLong), [ + "US-ASCII", "ISO-8859-1", "UTF-8" + ]) + } + + // leaving temporary folder option has multiple aliases... + if ([ + "tests.leaveTemporary", + "tests.leavetemporary", + "tests.leavetmpdir", + "solr.test.leavetmpdir", + ].find { prop -> + Boolean.parseBoolean(propertyOrDefault(prop, "false")) + }) { + testOptionsResolved['tests.leaveTemporary'] = true + } + + // Append resolved test properties to the test task. + test { + systemProperties testOptionsResolved + + if (Boolean.parseBoolean(testOptionsResolved['tests.asserts'])) { + jvmArgs("-ea", "-esa") + } else { + enableAssertions = false + } + + 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 helpTestOpts() { + 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 = opt.value + def value = testOptionsResolved[opt.propName] + println String.format(Locale.ROOT, + "%s%-23s = %-8s # %s", + (defValue != value ? "! " : " "), + opt.propName, + value, + (defValue != value ? "(!= default: ${defValue}) " : "") + opt.description) + } + } + } + } +} + +// Disable assertions for HashMap due to: LUCENE-8991 / JDK-8205399 +def vmName = System.getProperty("java.vm.name") +def spec = System.getProperty("java.specification.version") +if (vmName =~ /(?i)(hotspot|openjdk|jrockit)/ && + spec =~ /^(1\.8|9|10|11)$/ && + !Boolean.parseBoolean(propertyOrDefault('tests.asserts.hashmap', 'false'))) { + logger.debug("Enabling HashMap assertions.") + allprojects { + plugins.withType(JavaPlugin) { + test { + jvmArgs("-da:java.util.HashMap") + } + } + } +} + +/* + + + + + + + + + + + + + + + */