Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 33 additions & 21 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ import org.yaml.snakeyaml.Yaml

import org.apache.tools.ant.taskdefs.condition.Os

import javax.inject.Inject
import org.gradle.process.ExecOperations

// Gives tasks access to an injected `ExecOperations` for running external processes from a `doLast` block.
interface InjectedExecOps {
@Inject
ExecOperations getExecOps()
}

buildscript {
repositories {
if (Boolean.parseBoolean(mavenLocalEnabled)) {
Expand Down Expand Up @@ -213,10 +222,8 @@ subprojects {
publishLibrary = true
}

artifacts {
add("archives", tasks.sourcesJar)
add("archives", tasks.javadocJar)
add("archives", tasks.testJar)
tasks.named('assemble') {
dependsOn tasks.sourcesJar, tasks.javadocJar, tasks.testJar
}

afterEvaluate { project ->
Expand Down Expand Up @@ -277,28 +284,33 @@ subprojects {
task updateYamsql(type: Task) {
mustRunAfter 'test'
mustRunAfter 'build'
def execOps = objects.newInstance(InjectedExecOps).execOps
def projectVersion = project.version
def yamsqlFiles = sourceSets["test"].resources.srcDirs.collect { resourceDir ->
fileTree(dir: resourceDir).matching {
include '**/*.yamsql'
exclude '/initial-version/*'
exclude '/supported-version/*'
}
}
doLast {
if (isReleaseBuild) {
// don't replace !current_version in the supported-version resources, as those are there to test
// supported_version, we want the !current_version to stick around
println("Replacing !current_version...")
sourceSets["test"].resources.srcDirs.stream().flatMap( resourceDir ->
fileTree(dir: resourceDir).matching {
include '**/*.yamsql'
exclude '/initial-version/*'
exclude '/supported-version/*'
}.stream()
).forEach { yamsql ->
def original = yamsql.text
def updated = original.replaceAll("(?<!\")!current_version(?!\")", project.version)
if (!original.equals(updated)) {
println("Replacing !current_version in " + yamsql)
// Replace !current_version in all yamsql, unless it is surrounded by ", allowing it to be used in
// comments, without issue, e.g. in showcasing-tests
yamsql.write(updated)
exec {
workingDir '.'
commandLine 'git', 'add', yamsql
yamsqlFiles.each { tree ->
tree.each { yamsql ->
def original = yamsql.text
def updated = original.replaceAll("(?<!\")!current_version(?!\")", projectVersion)
if (!original.equals(updated)) {
println("Replacing !current_version in " + yamsql)
// Replace !current_version in all yamsql, unless it is surrounded by ", allowing it to be used in
// comments, without issue, e.g. in showcasing-tests
yamsql.write(updated)
execOps.exec {
workingDir '.'
commandLine 'git', 'add', yamsql
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions gradle/check.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ tasks.withType(rootProject.SpotBugsTask) { task ->
if (project.hasProperty('spotbugsEnableHtmlReport')) {
// SpotBugs task can only have one report type enabled at a time
reports {
xml.enabled false
html.enabled true
xml.enabled = false
html.enabled = true
}
} else {
task.finalizedBy printInstructionsOnRunningWithHtmlOutput
Expand Down
11 changes: 9 additions & 2 deletions gradle/sphinx.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ var sphinxRoot = "${rootDir}/docs/sphinx"
var venvDir = "${sphinxRoot}/.venv"
var sphinxBuildDir = "${sphinxRoot}/.out"

// Gives tasks access to an injected `ExecOperations` for running external processes from a `doLast` block.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed this tip:

This is a good time to consider extracting the ad-hoc task into a proper class.

Not part of this PR, but something I will be thinking about

interface InjectedExecOps {
@javax.inject.Inject
org.gradle.process.ExecOperations getExecOps()
}

var documented_subprojects = [
"fdb-java-annotations",
"fdb-extensions",
Expand All @@ -40,11 +46,12 @@ task sphinxEnv {
inputs.file("${sphinxRoot}/requirements.txt")
outputs.dir(venvDir)

def execOps = objects.newInstance(InjectedExecOps).execOps
doLast {
exec {
execOps.exec {
commandLine 'python3', '-m', 'venv', venvDir
}
exec {
execOps.exec {
commandLine "${venvDir}/bin/pip", 'install', '-r', "${sphinxRoot}/requirements.txt"
}
}
Expand Down
82 changes: 48 additions & 34 deletions gradle/testing.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import java.util.regex.Matcher
import java.util.regex.Pattern

import org.gradle.api.tasks.testing.TestListener

/*
* testing.gradle
*
Expand Down Expand Up @@ -117,14 +119,19 @@ task mixedModeTest(type: Test) {
markdownReports.mkdirs()
}
}
afterTest { descriptor, result ->
def version = getMixedModeVersion(descriptor)
if (version == null) {
throw new RuntimeException("Could not find version " + getFullDisplayName(descriptor))
} else {
mixedModeResults.append("${result.resultType} ${version}\n")
addTestListener([
beforeSuite: { suite -> },
afterSuite: { suite, result -> },
beforeTest: { descriptor -> },
afterTest: { descriptor, result ->
def version = getMixedModeVersion(descriptor)
if (version == null) {
throw new RuntimeException("Could not find version " + getFullDisplayName(descriptor))
} else {
mixedModeResults.append("${result.resultType} ${version}\n")
}
}
}
] as TestListener)
}

def getFullDisplayName(descriptor) {
Expand Down Expand Up @@ -227,6 +234,10 @@ def betterException(String exception) {
}

tasks.withType(Test) { theTask ->
// Point the `Test` task at the compiled classes and the runtime classpath of the test source-set.
testClassesDirs = sourceSets.test.output.classesDirs
classpath = sourceSets.test.runtimeClasspath

configureTestTask('allTest', theTask)
configureTestTask(theTask.name, theTask)
testLogging {
Expand All @@ -251,35 +262,38 @@ tasks.withType(Test) { theTask ->
}
}

beforeTest { descriptor ->
println "${Instant.now()} ${getFullDisplayName(descriptor)} STARTED"
}
afterTest { descriptor, result ->
def duration = String.format(Locale.ROOT, "%,d", result.endTime - result.startTime)
println "${Instant.now()} ${getFullDisplayName(descriptor)} ${result.resultType} (${duration}ms)"
println()
if (result.resultType == TestResult.ResultType.FAILURE) {
failures.append(
"<details>\n" +
"<summary> ❌ ${getFullDisplayName(descriptor)}</summary>\n" +
"(${duration}ms)\n\n" + // blank line is required for ``` to work
"```\n${betterException(result.getException().asString())}\n```\n" +
"</details>\n")
}
}
afterSuite { descriptor, result ->
// Very few subprojects acutally have destructive or mixed-mode tests, so strip those out of the summary.
// We leave the ones that have nothing under test, because that would be suspicious if a subproject had
// no tests
if (descriptor.parent == null
&& !(descriptor.toString().endsWith(":destructiveTest") && result.getTestCount() == 0)
&& !(descriptor.toString().endsWith(":mixedModeTest") && result.getTestCount() == 0)) {
addTestListener([
beforeSuite: { suite -> },
beforeTest: { descriptor ->
println "${Instant.now()} ${getFullDisplayName(descriptor)} STARTED"
},
afterTest: { descriptor, result ->
def duration = String.format(Locale.ROOT, "%,d", result.endTime - result.startTime)
def description = descriptor.toString().replaceFirst("Gradle Test Run ", "")
summary.append(
"|${description}|${result.getFailedTestCount()}|${result.getSkippedTestCount()}|${result.getSuccessfulTestCount()}|${duration}ms|\n")
println "${Instant.now()} ${getFullDisplayName(descriptor)} ${result.resultType} (${duration}ms)"
println()
if (result.resultType == TestResult.ResultType.FAILURE) {
failures.append(
"<details>\n" +
"<summary> ❌ ${getFullDisplayName(descriptor)}</summary>\n" +
"(${duration}ms)\n\n" + // blank line is required for ``` to work
"```\n${betterException(result.getException().asString())}\n```\n" +
"</details>\n")
}
},
afterSuite: { descriptor, result ->
// Very few subprojects acutally have destructive or mixed-mode tests, so strip those out of the summary.
// We leave the ones that have nothing under test, because that would be suspicious if a subproject had
// no tests
if (descriptor.parent == null
&& !(descriptor.toString().endsWith(":destructiveTest") && result.getTestCount() == 0)
&& !(descriptor.toString().endsWith(":mixedModeTest") && result.getTestCount() == 0)) {
def duration = String.format(Locale.ROOT, "%,d", result.endTime - result.startTime)
def description = descriptor.toString().replaceFirst("Gradle Test Run ", "")
summary.append(
"|${description}|${result.getFailedTestCount()}|${result.getSkippedTestCount()}|${result.getSuccessfulTestCount()}|${duration}ms|\n")
}
}
}
] as TestListener)
reports {
junitXml.outputPerTestCase = true
}
Expand Down
25 changes: 11 additions & 14 deletions yaml-tests/yaml-tests.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,9 @@ ext.resolveOtherServer = { Set<String> rejectedVersions ->
// Every time we get a version that has `-SNAPSHOT` we add it to the `rejectedVersions` list, and recurse
// until we find a version that doesn't have a `-SNAPSHOT`, and that's what we use.
dependencies.add(configurationName,
['group': 'org.foundationdb',
'name': 'fdb-relational-server',
// the all classifier specifies that we want to fetch the shadow jar, which includes all the
// dependencies
'classifier': 'all'],
// the `all` classifier specifies that we want to fetch the shadow jar, which includes all the
// dependencies. The empty version slot is filled in by the `version { strictly '+' }` Action below.
'org.foundationdb:fdb-relational-server::all',
{
version {
strictly '+'
Expand Down Expand Up @@ -172,9 +170,7 @@ ext.resolveSpecificServer = { String version ->
}
})
dependencies.add(configurationName,
['group': 'org.foundationdb',
'name': 'fdb-relational-server',
'classifier': 'all'],
'org.foundationdb:fdb-relational-server::all',
{
it.version {
strictly version
Expand Down Expand Up @@ -224,14 +220,14 @@ task downloadManyExternalServers(type: Copy) {
// n defaults to 10 and can be overridden with -Ptests.listVersionsCount=<n>.
// Output file: <buildDir>/recentVersions.txt
task listRecentVersions {
def listVersionsCountProvider = providers.gradleProperty('tests.listVersionsCount')
def outputFileProvider = layout.buildDirectory.file('recentVersions.txt')
doLast {
int count = project.hasProperty('tests.listVersionsCount')
? Integer.parseInt(project.getProperty('tests.listVersionsCount'))
: 10
int count = listVersionsCountProvider.map { Integer.parseInt(it) }.getOrElse(10)
def versions = resolveRecentVersionStrings(count)
// we don't want the output to be a declared output, otherwise gradle will conclude that it doesn't need to
// rerun this when the property changes.
def outputFile = project.layout.buildDirectory.file('recentVersions.txt').get().asFile
def outputFile = outputFileProvider.get().asFile
outputFile.parentFile.mkdirs()
String jsonArray = '["' + versions.join('", "') + '"]'
outputFile.text = "versions=" + jsonArray
Expand All @@ -247,13 +243,14 @@ task listRecentVersions {
task downloadSpecificExternalServer(type: Copy) {
dependsOn "cleanExternalServerDirectory"
into project.layout.buildDirectory.dir('externalServer')
def mixedModeVersionProvider = providers.gradleProperty('tests.mixedModeVersion')
// Resolve which JAR to download only when the task is actually executed.
// Using from(Callable) defers evaluation to execution time, which avoids
// failing at configuration time when neither property is set and this task
// is not part of the requested task graph.
from({
if (project.hasProperty('tests.mixedModeVersion')) {
def exactVersion = project.getProperty('tests.mixedModeVersion')
def exactVersion = mixedModeVersionProvider.orNull
if (exactVersion != null) {
println("Using explicitly specified external server version: ${exactVersion}")
return resolveSpecificServer(exactVersion)
} else {
Expand Down
Loading