Create a task for finding status files
Change-Id: I78e523336f20a5140c6e063e28632f57a52f18e4
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/TestingState.kt b/d8_r8/commonBuildSrc/src/main/kotlin/TestingState.kt
index 2ef455f..1b89075 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/TestingState.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/TestingState.kt
@@ -8,8 +8,9 @@
import java.io.PrintStream
import java.net.URLEncoder
import java.nio.file.Path
-import java.util.concurrent.TimeUnit
import org.gradle.api.Project
+import org.gradle.api.logging.Logger
+import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.testing.Test
import org.gradle.api.tasks.testing.TestDescriptor
import org.gradle.api.tasks.testing.TestListener
@@ -17,6 +18,7 @@
import org.gradle.api.tasks.testing.TestOutputListener
import org.gradle.api.tasks.testing.TestResult
import org.gradle.api.tasks.testing.TestResult.ResultType
+import org.gradle.kotlin.dsl.register
// Utility to install tracking of test results in status files.
class TestingState {
@@ -29,7 +31,7 @@
enum class Mode { ALL, OUTSTANDING, FAILING, PAST_FAILING }
// These are the files that are allowed for tracking test status.
- enum class StatusFile { SUCCESS, FAILURE, PAST_FAILURE }
+ enum class StatusType { SUCCESS, FAILURE, PAST_FAILURE }
fun getRerunMode(project: Project) : Mode? {
val prop = project.findProperty(MODE_PROPERTY) ?: return null
@@ -54,30 +56,57 @@
val index = indexDir.resolve("index.html")
val resuming = reportDir.exists()
if (resuming) {
- applyTestFilters(testingStateMode, task, reportDir)
+ applyTestFilters(testingStateMode, task, reportDir, indexDir, projectName)
}
addTestHandler(task, projectName, index, reportDir)
}
- private fun applyTestFilters(mode: Mode, task: Test, reportDir: File) {
+ private fun applyTestFilters(
+ mode: Mode,
+ task: Test,
+ reportDir: File,
+ indexDir: File,
+ projectName: String,
+ ) {
if (mode == Mode.ALL) {
// Running without filters will (re)run all tests.
return
}
- if (mode == Mode.OUTSTANDING) {
- task.logger.lifecycle(
- "Note: the building of an exclude list often times out."
- + "You may need to simply rerun all tests.")
- forEachTestReportStatusMatching(task, reportDir, StatusFile.SUCCESS, { clazz, name ->
- task.filter.excludeTestsMatching("$clazz.$name")
- })
- return
+ val statusType = getStatusTypeForMode(mode)
+ val statusOutputFile = indexDir.resolve("${projectName}.${statusType.name}.txt")
+ val findStatusTask = task.project.tasks.register<Exec>("${projectName}-find-status-files")
+ {
+ inputs.dir(reportDir)
+ outputs.file(statusOutputFile)
+ workingDir(reportDir)
+ commandLine(
+ "find", ".", "-name", statusType.name
+ )
+ doFirst {
+ standardOutput = statusOutputFile.outputStream()
+ }
}
- assert(mode == Mode.FAILING || mode == Mode.PAST_FAILING)
- val result = if (mode == Mode.FAILING) StatusFile.FAILURE else StatusFile.PAST_FAILURE
- forEachTestReportStatusMatching(task, reportDir, result, { clazz, name ->
- task.filter.includeTestsMatching("$clazz.$name")
- })
+ task.dependsOn(findStatusTask)
+ task.doFirst {
+ if (mode == Mode.OUTSTANDING) {
+ forEachTestReportStatusMatching(
+ statusType,
+ findStatusTask.get().outputs.files.singleFile,
+ task.logger,
+ { clazz, name -> task.filter.excludeTestsMatching("${clazz}.${name}") })
+ } else {
+ val hasMatch = forEachTestReportStatusMatching(
+ statusType,
+ findStatusTask.get().outputs.files.singleFile,
+ task.logger,
+ { clazz, name -> task.filter.includeTestsMatching("${clazz}.${name}") })
+ if (!hasMatch) {
+ // Add a filter that does not match to ensure the test run is not "without filters"
+ // which would run all tests.
+ task.filter.includeTestsMatching("NON_MATCHING_TEST_FILTER")
+ }
+ }
+ }
}
private fun addTestHandler(
@@ -181,11 +210,20 @@
})
}
- private fun getStatusFile(result: ResultType) : StatusFile {
+ private fun getStatusTypeForMode(mode: Mode) : StatusType {
+ return when (mode) {
+ Mode.OUTSTANDING -> StatusType.SUCCESS
+ Mode.FAILING -> StatusType.FAILURE
+ Mode.PAST_FAILING -> StatusType.PAST_FAILURE
+ Mode.ALL -> throw RuntimeException("Unexpected mode 'all' in status determination")
+ }
+ }
+
+ private fun getStatusTypeForResult(result: ResultType) : StatusType {
return when (result) {
- ResultType.FAILURE -> StatusFile.FAILURE
- ResultType.SUCCESS -> StatusFile.SUCCESS
- ResultType.SKIPPED -> StatusFile.SUCCESS
+ ResultType.FAILURE -> StatusType.FAILURE
+ ResultType.SUCCESS -> StatusType.SUCCESS
+ ResultType.SKIPPED -> StatusType.SUCCESS
}
}
@@ -287,18 +325,18 @@
reportDir: File,
desc: TestDescriptor,
result: ResultType) {
- val statusFile = getStatusFile(result)
+ val statusFile = getStatusTypeForResult(result)
withTestResultEntryWriter(reportDir, desc, statusFile.name, false, {
it.append(statusFile.name)
})
- if (statusFile == StatusFile.FAILURE) {
- getTestResultEntryOutputFile(reportDir, desc, StatusFile.SUCCESS.name).delete()
- val pastFailure = StatusFile.PAST_FAILURE.name
+ if (statusFile == StatusType.FAILURE) {
+ getTestResultEntryOutputFile(reportDir, desc, StatusType.SUCCESS.name).delete()
+ val pastFailure = StatusType.PAST_FAILURE.name
withTestResultEntryWriter(reportDir, desc, pastFailure, false, {
it.append(pastFailure)
})
} else {
- getTestResultEntryOutputFile(reportDir, desc, StatusFile.FAILURE.name).delete()
+ getTestResultEntryOutputFile(reportDir, desc, StatusType.FAILURE.name).delete()
}
}
@@ -313,25 +351,13 @@
FileWriter(file, append).use(fn)
}
- fun forEachTestReportStatusMatching(
- test: Test,
- reportDir: File,
- statusFile: StatusFile,
- onTest: (String, String) -> Unit
- ): Boolean {
- val fileName = statusFile.name
- val logger = test.logger
- val proc = ProcessBuilder("find", ".", "-name", fileName)
- .directory(reportDir)
- .redirectOutput(ProcessBuilder.Redirect.PIPE)
- .start()
- val result = proc.waitFor(10, TimeUnit.SECONDS)
- if (!result) {
- throw RuntimeException("Unexpected failure to find reports within time limit")
- }
- var hadMatch = false
- for (rawLine in proc.inputStream.bufferedReader().lineSequence()) {
- // Lines are of the form: ./<class>/<name>/FAILURE
+ private fun forEachTestReportStatusMatching(
+ type: StatusType, file: File, logger: Logger, onTest: (String, String) -> Unit
+ ) : Boolean {
+ val fileName = type.name
+ var hasMatch = false
+ for (rawLine in file.bufferedReader().lineSequence()) {
+ // Lines are of the form: ./<class>/<name>/<mode>
try {
val trimmed = rawLine.trim()
val line = trimmed.substring(2)
@@ -339,12 +365,12 @@
val clazz = line.substring(0, sep)
val name = line.substring(sep + 1, line.length - fileName.length - 1)
onTest(clazz, name)
- hadMatch = true
+ hasMatch = true
} catch (e: Exception) {
logger.lifecycle("WARNING: failed attempt to read test description from: '${rawLine}'")
}
}
- return hadMatch
+ return hasMatch
}
}
}
\ No newline at end of file