You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by th...@apache.org on 2020/09/14 16:38:28 UTC
[lucene-solr] 10/39: LUCENE-9491: Consolidate test options and improve support for dynamic… (#1807)
This is an automated email from the ASF dual-hosted git repository.
thelabdude pushed a commit to branch reference_impl_gradle_updates
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
commit 72bf564e35270fb4ebdaf1cc1e2d14fae1e4e74c
Author: Dawid Weiss <dw...@apache.org>
AuthorDate: Mon Aug 31 12:20:30 2020 +0200
LUCENE-9491: Consolidate test options and improve support for dynamic… (#1807)
* LUCENE-9491: Consolidate test options and improve support for dynamically computed values.
---
build.gradle | 1 +
gradle/defaults.gradle | 12 ++++
.../hashmapAssertions.gradle} | 40 ++++-------
gradle/testing/beasting.gradle | 12 +++-
gradle/testing/defaults-tests.gradle | 48 +++++++++++--
gradle/testing/profiling.gradle | 50 ++++++++------
gradle/testing/randomization.gradle | 80 ++++++++++------------
gradle/validation/config-file-sanity.gradle | 2 +-
8 files changed, 147 insertions(+), 98 deletions(-)
diff --git a/build.gradle b/build.gradle
index 9801e06..bbdc7bc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -175,3 +175,4 @@ apply from: file('gradle/documentation/render-javadoc.gradle')
apply from: file('gradle/hacks/findbugs.gradle')
apply from: file('gradle/hacks/gradle.gradle')
+apply from: file('gradle/hacks/hashmapAssertions.gradle')
diff --git a/gradle/defaults.gradle b/gradle/defaults.gradle
index bf64968..5f11803 100644
--- a/gradle/defaults.gradle
+++ b/gradle/defaults.gradle
@@ -40,10 +40,22 @@ allprojects {
result = project.getProperty(propName)
} else if (System.properties.containsKey(propName)) {
result = System.properties.get(propName)
+ } else if (defValue instanceof Closure) {
+ result = defValue.call()
} else {
result = defValue
}
return result
}
+
+ // System environment variable or default.
+ envOrDefault = { envName, defValue ->
+ return Objects.requireNonNullElse(System.getenv(envName), defValue);
+ }
+
+ // Either a project, system property, environment variable or default value.
+ propertyOrEnvOrDefault = { propName, envName, defValue ->
+ return propertyOrDefault(propName, envOrDefault(envName, defValue));
+ }
}
}
diff --git a/gradle/defaults.gradle b/gradle/hacks/hashmapAssertions.gradle
similarity index 50%
copy from gradle/defaults.gradle
copy to gradle/hacks/hashmapAssertions.gradle
index bf64968..095726c 100644
--- a/gradle/defaults.gradle
+++ b/gradle/hacks/hashmapAssertions.gradle
@@ -15,35 +15,19 @@
* limitations under the License.
*/
-allprojects {
- apply plugin: 'base'
-
- group "org.apache"
-
- // Repositories to fetch dependencies from.
- repositories {
- mavenCentral()
- maven {
- url "https://maven.restlet.com"
- }
- }
-
- // Artifacts will have names after full gradle project path
- // so :solr:core will have solr-core.jar, etc.
- project.archivesBaseName = project.path.replaceAll("^:", "").replace(':', '-')
-
- ext {
- // Utility method to support passing overrides via -P or -D.
- propertyOrDefault = { propName, defValue ->
- def result
- if (project.hasProperty(propName)) {
- result = project.getProperty(propName)
- } else if (System.properties.containsKey(propName)) {
- result = System.properties.get(propName)
- } else {
- result = defValue
+// 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.info("Enabling HashMap assertions.")
+ allprojects {
+ plugins.withType(JavaPlugin) {
+ tasks.withType(Test) { task ->
+ jvmArgs("-da:java.util.HashMap")
}
- return result
}
}
}
+
diff --git a/gradle/testing/beasting.gradle b/gradle/testing/beasting.gradle
index 1604cdb..c4b5303 100644
--- a/gradle/testing/beasting.gradle
+++ b/gradle/testing/beasting.gradle
@@ -25,6 +25,16 @@
def beastingMode = gradle.startParameter.taskNames.contains("beast");
+allprojects {
+ plugins.withType(JavaPlugin) {
+ ext {
+ testOptions += [
+ [propName: 'tests.dups', value: 0, description: "Reiterate runs of entire test suites ('beast' task)."]
+ ]
+ }
+ }
+}
+
if (beastingMode) {
if (rootProject.rootSeedUserProvided) {
logger.warn("Root randomization seed is externally provided, all duplicated runs will use the same starting seed.")
@@ -37,7 +47,7 @@ if (beastingMode) {
group "Verification"
}
- def dups = Integer.parseInt(propertyOrDefault("tests.dups", "0"))
+ def dups = Integer.parseInt(resolvedTestOption("tests.dups"))
if (dups <= 0) {
throw new GradleException("Specify -Ptests.dups=[count] for beast task.")
}
diff --git a/gradle/testing/defaults-tests.gradle b/gradle/testing/defaults-tests.gradle
index c4316d5..57b0c14 100644
--- a/gradle/testing/defaults-tests.gradle
+++ b/gradle/testing/defaults-tests.gradle
@@ -24,15 +24,49 @@ def verboseModeHookInstalled = false
allprojects {
plugins.withType(JavaPlugin) {
- def verboseMode = Boolean.parseBoolean(propertyOrDefault("tests.verbose", "false"))
-
project.ext {
+ // This array will collect all test options, including default values and option description.
+ // The actual values of these properties (defaults, project properties) are resolved lazily after evaluation
+ // completes.
+ // [propName: 'tests.foo', value: "bar", description: "Sets foo in tests."],
+ testOptions = [
+ // asserts, debug output.
+ [propName: 'tests.verbose', value: false, description: "Enables verbose mode (emits full test outputs immediately)."],
+ [propName: 'tests.workDir',
+ value: { -> file("${buildDir}/tmp/tests-tmp") },
+ description: "Working directory for forked test JVMs",
+ includeInReproLine: false
+ ],
+ // JVM settings
+ [propName: 'tests.minheapsize', value: "256m", description: "Minimum heap size for test JVMs"],
+ [propName: 'tests.heapsize', value: "512m", description: "Heap size for test JVMs"],
+ // Test forks
+ [propName: 'tests.jvms',
+ value: { -> ((int) Math.max(1, Math.min(Runtime.runtime.availableProcessors() / 2.0, 4.0))) },
+ description: "Number of forked test JVMs"],
+ [propName: 'tests.haltonfailure', value: true, description: "Halt processing on test failure."],
+ [propName: 'tests.jvmargs',
+ value: { -> propertyOrEnvOrDefault("tests.jvmargs", "TEST_JVM_ARGS", "-XX:TieredStopAtLevel=1") },
+ description: "Arguments passed to each forked JVM."],
+ ]
+
+ // Resolves test option's value.
+ resolvedTestOption = { propName ->
+ def option = testOptions.find { entry -> entry.propName == propName }
+ if (option == null) {
+ throw new GradleException("No such test option: " + propName)
+ }
+ return propertyOrDefault(option.propName, option.value)
+ }
+
testsCwd = file("${buildDir}/tmp/tests-cwd")
- testsTmpDir = file(propertyOrDefault("tests.workDir", "${buildDir}/tmp/tests-tmp"))
+ testsTmpDir = file(resolvedTestOption("tests.workDir"))
commonDir = project(":lucene").projectDir
commonSolrDir = project(":solr").projectDir
}
+ def verboseMode = resolvedTestOption("tests.verbose").toBoolean()
+
// If we're running in verbose mode and:
// 1) worker count > 1
// 2) number of 'test' tasks in the build is > 1
@@ -58,10 +92,10 @@ allprojects {
}
binaryResultsDirectory = file(propertyOrDefault("binaryResultsDirectory", binaryResultsDirectory))
- if (verboseMode) {
+ maxParallelForks = resolvedTestOption("tests.jvms") as Integer
+ if (verboseMode && maxParallelForks != 1) {
+ logger.lifecycle("tests.jvm forced to 1 in verbose mode.")
maxParallelForks = 1
- } else {
- maxParallelForks = propertyOrDefault("tests.jvms", (int) Math.max(1, Math.min(Runtime.runtime.availableProcessors() / 2.0, 4.0))) as Integer
}
workingDir testsCwd
@@ -72,6 +106,8 @@ allprojects {
jvmArgs Commandline.translateCommandline(propertyOrDefault("tests.jvmargs", "-XX:TieredStopAtLevel=1 -XX:+UseParallelGC -XX:-UseBiasedLocking -DconfigurationFile=log4j2.xml -Dorg.apache.xml.dtm.DTMManager=org.apache.xml.dtm.ref.DTMManagerDefault"));
+ ignoreFailures = resolvedTestOption("tests.haltonfailure").toBoolean() == false
+
systemProperty 'java.util.logging.config.file', rootProject.file("gradle/testing/defaults-tests/logging.properties")
systemProperty 'java.awt.headless', 'true'
systemProperty 'jdk.map.althashing.threshold', '0'
diff --git a/gradle/testing/profiling.gradle b/gradle/testing/profiling.gradle
index 01938f9..6655df7 100644
--- a/gradle/testing/profiling.gradle
+++ b/gradle/testing/profiling.gradle
@@ -19,29 +19,39 @@ import org.apache.lucene.gradle.ProfileResults;
def recordings = files()
-if (Boolean.parseBoolean(propertyOrDefault("tests.profile", "false"))) {
- allprojects {
- tasks.withType(Test) {
- jvmArgs("-XX:StartFlightRecording=dumponexit=true,maxsize=250M,settings=" + rootProject.file("gradle/testing/profiling.jfc"),
+allprojects {
+ plugins.withType(JavaPlugin) {
+ ext {
+ testOptions += [
+ [propName: 'tests.profile', value: false, description: "Enable java flight recorder profiling."]
+ ]
+ }
+
+ if (resolvedTestOption("tests.profile").toBoolean()) {
+ allprojects {
+ tasks.withType(Test) {
+ jvmArgs("-XX:StartFlightRecording=dumponexit=true,maxsize=250M,settings=" + rootProject.file("gradle/testing/profiling.jfc"),
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+DebugNonSafepoints")
- // delete any previous profile results
- doFirst {
- project.delete fileTree(dir: workingDir, include: '*.jfr')
- }
- doLast {
- recordings = recordings.plus fileTree(dir: workingDir, include: '*.jfr')
+ // delete any previous profile results
+ doFirst {
+ project.delete fileTree(dir: workingDir, include: '*.jfr')
+ }
+ doLast {
+ recordings = recordings.plus fileTree(dir: workingDir, include: '*.jfr')
+ }
+ }
}
- }
- }
- gradle.buildFinished {
- if (!recordings.isEmpty()) {
- ProfileResults.printReport(recordings.getFiles().collect { it.toString() },
- propertyOrDefault(ProfileResults.MODE_KEY, ProfileResults.MODE_DEFAULT) as String,
- Integer.parseInt(propertyOrDefault(ProfileResults.STACKSIZE_KEY, ProfileResults.STACKSIZE_DEFAULT)),
- Integer.parseInt(propertyOrDefault(ProfileResults.COUNT_KEY, ProfileResults.COUNT_DEFAULT)),
- Boolean.parseBoolean(propertyOrDefault(ProfileResults.LINENUMBERS_KEY, ProfileResults.LINENUMBERS_DEFAULT)))
+ gradle.buildFinished {
+ if (!recordings.isEmpty()) {
+ ProfileResults.printReport(recordings.getFiles().collect { it.toString() },
+ propertyOrDefault(ProfileResults.MODE_KEY, ProfileResults.MODE_DEFAULT) as String,
+ Integer.parseInt(propertyOrDefault(ProfileResults.STACKSIZE_KEY, ProfileResults.STACKSIZE_DEFAULT)),
+ Integer.parseInt(propertyOrDefault(ProfileResults.COUNT_KEY, ProfileResults.COUNT_DEFAULT)),
+ Boolean.parseBoolean(propertyOrDefault(ProfileResults.LINENUMBERS_KEY, ProfileResults.LINENUMBERS_DEFAULT)))
+ }
+ }
}
}
-}
+}
\ No newline at end of file
diff --git a/gradle/testing/randomization.gradle b/gradle/testing/randomization.gradle
index eb539c7..570ff6a 100644
--- a/gradle/testing/randomization.gradle
+++ b/gradle/testing/randomization.gradle
@@ -60,9 +60,9 @@ allprojects {
allprojects {
plugins.withType(JavaPlugin) {
ext {
- testOptions = [
+ testOptions += [
// seed, repetition and amplification.
- [propName: 'tests.seed', value: "random", description: "Sets the master randomization seed."],
+ [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: 1, description: "Value multiplier for randomized tests."],
[propName: 'tests.maxfailures', value: null, description: "Skip tests after a given number of failures."],
@@ -70,9 +70,8 @@ allprojects {
[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.verbose', value: false, description: "Emit verbose debug information from tests."],
[propName: 'tests.infostream', value: false, description: "Enables or disables infostream logs."],
- [propName: 'tests.leaveTemporary', value: null, description: "Leave temporary directories after tests complete."],
+ [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."],
@@ -88,7 +87,12 @@ allprojects {
[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.", buildOnly: true],
+ [propName: 'tests.badapples', value: null, description: "Enables or disables @BadApple 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
[propName: 'tests.linedocsfile', value: 'europarl.lines.txt.gz', description: "Test data file path."],
// miscellaneous; some of them very weird.
@@ -105,7 +109,11 @@ configure(allprojects.findAll {project -> project.path.startsWith(":solr") }) {
ext {
testOptions += [
[propName: 'tests.luceneMatchVersion', value: baseVersion, description: "Base Lucene version."],
- [propName: 'common-solr.dir', value: file("${commonDir}/../solr").path, description: "Solr base dir."],
+ [propName: 'common-solr.dir',
+ value: { -> file("${commonDir}/../solr").path },
+ description: "Solr base dir.",
+ includeInReproLine: false
+ ],
[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)."],
@@ -121,23 +129,15 @@ allprojects {
ext.testOptionsResolved = testOptions.findAll { opt ->
propertyOrDefault(opt.propName, opt.value) != null
}.collectEntries { opt ->
- [(opt.propName): Objects.toString(propertyOrDefault(opt.propName, opt.value))]
- }
-
- // These are not official options or dynamically seed-derived options.
- if (testOptionsResolved['tests.file.encoding'] == 'random') {
- testOptionsResolved['tests.file.encoding'] = RandomPicks.randomFrom(
- new Random(projectSeedLong), [
- "US-ASCII", "ISO-8859-1", "UTF-8"
- ])
- }
-
- if (testOptionsResolved['tests.seed'] == 'random') {
- testOptionsResolved['tests.seed'] = rootSeed
+ [(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
@@ -152,14 +152,19 @@ allprojects {
"tests.leavetmpdir",
"solr.test.leavetmpdir",
].find { prop ->
- Boolean.parseBoolean(propertyOrDefault(prop, "false"))
+ 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 opts with "buildOnly: true" (?)
+ // 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'])) {
@@ -215,33 +220,24 @@ allprojects {
println "Test options for project ${project.path} and seed \"${rootSeed}\":"
testOptions.sort { a, b -> a.propName.compareTo(b.propName) }.each { opt ->
- def defValue = Objects.toString(opt.value, null)
+ 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%-23s = %-8s # %s",
- (defValue != value ? "! " : " "),
+ "%s%-24s = %-8s # %s",
+ (defValue != value ? "! " : computedValue ? "C " : " "),
opt.propName,
value,
- (defValue != value ? "(!= default: ${defValue}) " : "") + opt.description)
+ (computedValue ? "(!= default: computed) " : (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) {
- tasks.withType(Test) { task ->
- jvmArgs("-da:java.util.HashMap")
- }
- }
- }
-}
-
diff --git a/gradle/validation/config-file-sanity.gradle b/gradle/validation/config-file-sanity.gradle
index e7ae048..30f6391 100644
--- a/gradle/validation/config-file-sanity.gradle
+++ b/gradle/validation/config-file-sanity.gradle
@@ -20,7 +20,7 @@
configure(project(":solr")) {
task validateConfigFileSanity() {
doFirst {
- def matchVersion = project(":solr:core").testOptionsResolved['tests.luceneMatchVersion']
+ def matchVersion = project(":solr:core").resolvedTestOption('tests.luceneMatchVersion')
if (!matchVersion) {
throw new GradleException("tests.luceneMatchVersion not defined?")
}