Rename new testing state and its flags.

Two flags control the use of testing state:

  --with-testing-state

    Enables the use of state. If state exists testing continues with
    either the failed tests or if no tests failed, with the remaining
    tests.

  --reset-testing-state

    Implies the use of --with-testing-state, but will first clear any
    existing state, thus always resulting in a new/clean test run.

The state is now stored in a directory based on the git branch
name. This CL also fixes a potential double writing issue for the
stdout/err files.

Bug: 186607794
Change-Id: Ifd3d7f3410104c5e6047d17900e7d87356cc9a89
diff --git a/build.gradle b/build.gradle
index 7eee894..5220ac2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,6 +13,7 @@
 import utils.Utils
 import org.gradle.api.tasks.testing.logging.TestExceptionFormat
 import org.gradle.api.tasks.testing.logging.TestLogEvent
+import org.gradle.api.tasks.testing.TestOutputEvent
 
 import java.nio.charset.StandardCharsets
 import java.nio.file.Files
@@ -1977,15 +1978,15 @@
     new FileWriter(file, append).withCloseable fn
 }
 
-def getGitBranchName() {
+static def getGitBranchName() {
     def out = new StringBuilder()
     def err = new StringBuilder()
     def proc = "git rev-parse --abbrev-ref HEAD".execute()
     proc.waitForProcessOutput(out, err)
-    return out
+    return out.toString().trim()
 }
 
-def getFreshTestReportIndex(File reportDir) {
+static def getFreshTestReportIndex(File reportDir) {
     def number = 0
     while (true) {
         def freshIndex = reportDir.toPath().resolve("index.${number++}.html").toFile()
@@ -2032,15 +2033,16 @@
     return forEachTestReportAlreadyX(reportDir, TestResult.ResultType.SKIPPED.name(), onSucceededTest)
 }
 
-def setUpNewTestReporting(Test task) {
+def setUpTestingState(Test task) {
     // Hide all test events from the console, they are written to the report.
     task.testLogging { events = [] }
 
-    def reportDir = project.hasProperty('test_report_output')
-            ? file(project.hasProperty('test_report_output'))
-            : file("${buildDir}/testreport")
+    def branch = getGitBranchName()
+    def reportDir = file("${buildDir}/test-state/${branch}")
     def index = reportDir.toPath().resolve("index.html").toFile()
-    def resuming = reportDir.exists()
+    def resetState = project.hasProperty('reset-testing-state')
+    def reportDirExists = reportDir.exists()
+    def resuming = !resetState && reportDirExists
 
     def hasFailingTests = false;
     if (resuming) {
@@ -2051,6 +2053,9 @@
         })
         // Otherwise exclude all of the test already marked as succeeding.
         if (!hasFailingTests) {
+            // Also allow the test to overall succeed if there are no remaining tests that match,
+            // which is natural if the state already succeeded in full.
+            task.filter.failOnNoMatchingTests = false
             forEachTestReportAlreadyPassing(reportDir, {
                 clazz, name -> task.filter.excludeTestsMatching("$clazz.$name")
             })
@@ -2063,6 +2068,9 @@
     task.beforeSuite { desc ->
         if (!desc.parent) {
             def parentReport = null
+            if (resetState && reportDirExists) {
+                delete reportDir
+            }
             if (resuming) {
                 if (index.exists()) {
                     parentReport = getFreshTestReportIndex(reportDir)
@@ -2071,7 +2079,6 @@
             } else {
                 reportDir.mkdirs()
             }
-            def branch = getGitBranchName()
             def runPrefix = resuming ? "Resuming" : "Starting"
             def title =  "${runPrefix} @ ${branch}"
             // Print a console link to the test report for easy access.
@@ -2104,7 +2111,7 @@
                     index << "<h2 style=\"background-color:#62D856\">GREEN BAR == YOU ROCK!</h2>"
                 }
             } else if (result.resultType == TestResult.ResultType.FAILURE) {
-                index << "<h2 style=\"background-color:#6D130Ared\">Some tests failed: ${result.resultType.name()}</h2><ul>"
+                index << "<h2 style=\"background-color:#6D130A\">Some tests failed: ${result.resultType.name()}</h2><ul>"
             } else {
                 index << "<h2>Tests finished: ${result.resultType.name()}</h2><ul>"
             }
@@ -2117,16 +2124,22 @@
     }
 
     // Events to stdout/err are appended to the files in the test directories.
-    // TODO(b/186607794): Doing so will cause resuming test runs for failing tests to append too!
-    //  consider writing the outputs to a .temp and moving that on test completion. This can still
-    //  be wrong if the tests are ctrl-c and output has been written, but that is likely a minor
-    //  concern.
     task.onOutput { desc, event ->
         withTestResultEntryWriter(reportDir, desc, event.getDestination().name(), true, {
             it.append(event.getMessage())
         })
     }
 
+    task.beforeTest { desc ->
+        // Remove any stale output files before running the test.
+        for (def destType : TestOutputEvent.Destination.values()) {
+            def destFile = getTestResultEntryOutputFile(reportDir, desc, destType.name())
+            if (destFile.exists()) {
+                delete destFile
+            }
+        }
+    }
+
     task.afterTest { desc, result ->
         if (result.getTestCount() != 1) {
             throw new IllegalStateException("Unexpected test with more than one result: ${desc}")
@@ -2182,9 +2195,9 @@
     // R8.jar is required for running bootstrap tests.
     dependsOn r8
 
-    def newTestingReport = project.hasProperty('testing-report')
-    if (newTestingReport) {
-        setUpNewTestReporting(task)
+    def useTestingState = project.hasProperty('testing-state')
+    if (useTestingState) {
+        setUpTestingState(task)
     }
 
     if (project.hasProperty('generate_golden_files_to')) {
@@ -2200,7 +2213,7 @@
     }
 
 
-    if (!newTestingReport) {
+    if (!useTestingState) {
         testLogging.exceptionFormat = 'full'
         if (project.hasProperty('print_test_stdout')) {
             testLogging.showStandardStreams = true
@@ -2229,7 +2242,7 @@
         systemProperty 'desugar_jdk_libs', project.property('desugar_jdk_libs')
     }
 
-    if (!newTestingReport) {
+    if (!useTestingState) {
         if (project.hasProperty('print_times') || project.hasProperty('one_line_per_test')) {
             afterTest { desc, result ->
                 def executionTime = (result.endTime - result.startTime) / 1000
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
index f40e8b3..4748dad 100644
--- a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
+++ b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
@@ -14,13 +14,11 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.VmTestRunner;
 import com.android.tools.r8.origin.Origin;
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.List;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 public class InterfaceRenamingTestRunner extends TestBase {
 
diff --git a/tools/test.py b/tools/test.py
index 685cbf2..157bd28 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -171,8 +171,12 @@
       help='Print the execution time of the slowest tests..',
       default=False, action='store_true')
   result.add_option(
-      '--testing-report',
-      help='Use the custom testing report output format',
+      '--with-testing-state',
+      help='Run/resume tests using testing state.',
+      default=False, action='store_true')
+  result.add_option(
+      '--reset-testing-state',
+      help='Clean the testing state and rerun tests (implies --with-testing-state).',
       default=False, action='store_true')
   result.add_option(
       '--stacktrace',
@@ -313,8 +317,11 @@
     gradle_args.append('-Pdesugar_jdk_json_dir=' + desugar_jdk_json_dir)
   if desugar_jdk_libs:
     gradle_args.append('-Pdesugar_jdk_libs=' + desugar_jdk_libs)
-  if options.testing_report:
-    gradle_args.append('-Ptesting-report')
+  if options.reset_testing_state:
+    gradle_args.append('-Ptesting-state')
+    gradle_args.append('-Preset-testing-state')
+  elif options.with_testing_state:
+    gradle_args.append('-Ptesting-state')
 
   # Build an R8 with dependencies for bootstrapping tests before adding test sources.
   gradle_args.append('r8WithDeps')