Make backup CTS tests more resilient for failing logcat --clear

Test: cts-tradefed run cts -m CtsBackupTestCases
Change-Id: I3202367fff7436da23b50ab09ab00b671fd635fe
diff --git a/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java b/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
index 4a70a67..9c42cc6 100644
--- a/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
+++ b/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
@@ -26,9 +26,8 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
+import java.util.UUID;
 import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * Base class for backup instrumentation tests.
@@ -36,7 +35,7 @@
  * Ensures that backup is enabled and local transport selected, and provides some utility methods.
  */
 public class BaseBackupCtsTest extends InstrumentationTestCase {
-    protected static final String APP_LOG_TAG = "BackupCTSApp";
+    private static final String APP_LOG_TAG = "BackupCTSApp";
 
     private static final String LOCAL_TRANSPORT =
             "android/com.android.internal.backup.LocalTransport";
@@ -55,6 +54,7 @@
         if (isBackupSupported) {
             assertTrue("Backup not enabled", isBackupEnabled());
             assertTrue("LocalTransport not selected", isLocalTransportSelected());
+            exec("setprop log.tag." + APP_LOG_TAG +" VERBOSE");
         }
     }
 
@@ -72,16 +72,44 @@
         return output.contains("* " + LOCAL_TRANSPORT);
     }
 
-    protected boolean waitForLogcat(String logcatString, int maxTimeoutInSeconds) throws Exception {
+    /**
+     * Attempts to clear logcat.
+     *
+     * Clearing logcat is known to be unreliable, so this methods also output a unique separator
+     * that can be used to find this point in the log even if clearing failed.
+     * @return a unique separator string
+     * @throws Exception
+     */
+    protected String clearLogcat() throws Exception {
+        exec("logcat -c");
+        String uniqueString = ":::" + UUID.randomUUID().toString();
+        exec("log -t " + APP_LOG_TAG + " " + uniqueString);
+        return uniqueString;
+    }
+
+    /**
+     * Wait for up to maxTimeoutInSeconds for the given strings to appear in the logcat in the given order.
+     * By passing the separator returned by {@link #clearLogcat} as the first string you can ensure that only
+     * logs emitted after that call to clearLogcat are found.
+     *
+     * @throws AssertionError if the strings are not found in the given time.
+     */
+    protected void waitForLogcat(int maxTimeoutInSeconds, String... logcatStrings)
+        throws Exception {
         long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(maxTimeoutInSeconds);
+        int stringIndex = 0;
         while (timeout >= System.currentTimeMillis()) {
             FileInputStream fis = executeStreamedShellCommand(getInstrumentation(),
                     "logcat -v brief -d " + APP_LOG_TAG + ":* *:S");
             BufferedReader log = new BufferedReader(new InputStreamReader(fis));
             String line;
+            stringIndex = 0;
             while ((line = log.readLine()) != null) {
-                if (line.contains(logcatString)) {
-                    return true;
+                if (line.contains(logcatStrings[stringIndex])) {
+                    stringIndex++;
+                    if (stringIndex >= logcatStrings.length) {
+                        return;
+                    }
                 }
             }
             closeQuietly(log);
@@ -89,7 +117,9 @@
             // performing the next search.
             Thread.sleep(SMALL_LOGCAT_DELAY);
         }
-        return false;
+        fail("Couldn't find " + logcatStrings[stringIndex] +
+            (stringIndex > 0 ? " after " + logcatStrings[stringIndex - 1] : "") +
+            " within " + maxTimeoutInSeconds + " seconds ");
     }
 
     protected void createTestFileOfSize(String packageName, int size) throws Exception {
@@ -97,14 +127,14 @@
             "-c android.intent.category.LAUNCHER " +
             "-n " + packageName + "/android.backup.app.MainActivity " +
             "-e file_size " + size);
-        assertTrue("File was not created", waitForLogcat("File created!", 30));
+        waitForLogcat(30, "File created!");
     }
 
     protected String exec(String command) throws Exception {
-        BufferedReader br = null;
         try (InputStream in = executeStreamedShellCommand(getInstrumentation(), command)) {
-            br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
-            String str = null;
+            BufferedReader br = new BufferedReader(
+                    new InputStreamReader(in, StandardCharsets.UTF_8));
+            String str;
             StringBuilder out = new StringBuilder();
             while ((str = br.readLine()) != null) {
                 out.append(str);
diff --git a/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java b/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
index 16ecb79..90dc5e8 100644
--- a/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
+++ b/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
@@ -25,50 +25,37 @@
 
     private static final int LOCAL_TRANSPORT_CONFORMING_FILE_SIZE = 5 * 1024;
 
+    private static final int TIMEOUT_SECONDS = 10;
+
     public void testExpectedMethodsCalledInOrder() throws Exception {
         if (!isBackupSupported()) {
             return;
         }
-        exec("logcat --clear");
-        exec("setprop log.tag." + APP_LOG_TAG +" VERBOSE");
+        String backupSeparator = clearLogcat();
 
         // Make sure there's something to backup
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_CONFORMING_FILE_SIZE);
 
         // Request backup and wait for it to complete
         exec("bmgr backupnow " + BACKUP_APP_NAME);
-        assertTrue("Backup agent not destroyed", waitForLogcat("onDestroy", 10));
 
-        verifyContainsInOrder(execLogcat(),
+        waitForLogcat(TIMEOUT_SECONDS,
+            backupSeparator,
             "onCreate",
             "Full backup requested",
             "onDestroy");
 
-        exec("logcat --clear");
+        String restoreSeparator = clearLogcat();
 
         // Now request restore and wait for it to complete
         exec("bmgr restore " + BACKUP_APP_NAME);
-        assertTrue("Backup agent not destroyed", waitForLogcat("onDestroy", 10));
 
-        verifyContainsInOrder(execLogcat(),
+        waitForLogcat(TIMEOUT_SECONDS,
+            restoreSeparator,
             "onCreate",
             "onRestoreFile",
             "onRestoreFinished",
             "onDestroy");
     }
 
-    private void verifyContainsInOrder(String message, String... substrings) {
-        int currentIndex = 0;
-        for (String substring : substrings) {
-            int substringIndex = message.indexOf(substring, currentIndex);
-            if (substringIndex < 0) {
-                fail("Didn't find '" + substring + "' in expected order");
-            }
-            currentIndex = substringIndex + substring.length();
-        }
-    };
-
-    private String execLogcat() throws Exception {
-        return exec("logcat -v brief -d " + APP_LOG_TAG + ":* *:S");
-    }
 }
diff --git a/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java b/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
index cfe007c..7315097 100644
--- a/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
+++ b/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
@@ -30,28 +30,30 @@
     private static final int LOCAL_TRANSPORT_BACKUP_QUOTA = 25 * 1024 * 1024;
     private static final int LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE = 30 * 1024 * 1024;
 
+    private static final int TIMEOUT_SECONDS = 10;
+
     public void testQuotaExceeded() throws Exception {
         if (!isBackupSupported()) {
             return;
         }
-        exec("logcat --clear");
-        exec("setprop log.tag." + APP_LOG_TAG +" VERBOSE");
+        String separator = clearLogcat();
         // Launch test app and create file exceeding limit for local transport
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE);
 
         // Request backup and wait for quota exceeded event in logcat
         exec("bmgr backupnow " + BACKUP_APP_NAME);
-        assertTrue("Quota exceeded event is not received", waitForLogcat("Quota exceeded!", 10));
+        waitForLogcat(TIMEOUT_SECONDS,separator,
+            "Quota exceeded!");
     }
 
     public void testQuotaReported() throws Exception {
         if (!isBackupSupported()) {
             return;
         }
-        exec("logcat --clear");
+        String separator = clearLogcat();
         exec("bmgr backupnow " + BACKUP_APP_NAME);
-        assertTrue("Quota not reported correctly",
-                waitForLogcat("quota is " + LOCAL_TRANSPORT_BACKUP_QUOTA, 10));
+        waitForLogcat(TIMEOUT_SECONDS,separator,
+            "quota is " + LOCAL_TRANSPORT_BACKUP_QUOTA);
     }
 
 }
diff --git a/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java b/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java
index 420c723..2802f81 100644
--- a/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java
+++ b/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java
@@ -25,50 +25,35 @@
 
     private static final int LOCAL_TRANSPORT_CONFORMING_FILE_SIZE = 5 * 1024;
 
+    private static final int TIMEOUT_SECONDS = 10;
+
     public void testExpectedMethodsCalledInOrder() throws Exception {
         if (!isBackupSupported()) {
             return;
         }
-        exec("logcat --clear");
-        exec("setprop log.tag." + APP_LOG_TAG +" VERBOSE");
+        String backupSeparator = clearLogcat();
 
         // Make sure there's something to backup
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_CONFORMING_FILE_SIZE);
 
         // Request backup and wait for it to complete
         exec("bmgr backupnow " + BACKUP_APP_NAME);
-        assertTrue("Backup agent not destroyed", waitForLogcat("onDestroy", 10));
 
-        verifyContainsInOrder(execLogcat(),
+        waitForLogcat(TIMEOUT_SECONDS,backupSeparator,
             "onCreate",
             "Backup requested",
             "onDestroy");
 
-        exec("logcat --clear");
+        String restoreSeparator = clearLogcat();
 
         // Now request restore and wait for it to complete
         exec("bmgr restore " + BACKUP_APP_NAME);
-        assertTrue("Backup agent not destroyed", waitForLogcat("onDestroy", 10));
 
-        verifyContainsInOrder(execLogcat(),
+        waitForLogcat(TIMEOUT_SECONDS, restoreSeparator,
             "onCreate",
             "Restore requested",
             "onRestoreFinished",
             "onDestroy");
     }
 
-    private void verifyContainsInOrder(String message, String... substrings) {
-        int currentIndex = 0;
-        for (String substring : substrings) {
-            int substringIndex = message.indexOf(substring, currentIndex);
-            if (substringIndex < 0) {
-                fail("Didn't find '" + substring + "' in expected order");
-            }
-            currentIndex = substringIndex + substring.length();
-        }
-    };
-
-    private String execLogcat() throws Exception {
-        return exec("logcat -v brief -d " + APP_LOG_TAG + ":* *:S");
-    }
 }
diff --git a/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java b/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
index 9647d07..6f78b09 100644
--- a/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
+++ b/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
@@ -30,28 +30,30 @@
     private static final int LOCAL_TRANSPORT_BACKUP_QUOTA = 5 * 1024 * 1024;
     private static final int LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE = 6 * 1024 * 1024;
 
+    private static final int TIMEOUT_SECONDS = 10;
+
     public void testQuotaExceeded() throws Exception {
         if (!isBackupSupported()) {
             return;
         }
-        exec("logcat --clear");
-        exec("setprop log.tag." + APP_LOG_TAG +" VERBOSE");
+        String separator = clearLogcat();
         // Launch test app and create file exceeding limit for local transport
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE);
 
         // Request backup and wait for quota exceeded event in logcat
         exec("bmgr backupnow " + BACKUP_APP_NAME);
-        assertTrue("Quota exceeded event is not received", waitForLogcat("Quota exceeded!", 10));
+        waitForLogcat(TIMEOUT_SECONDS, separator,
+            "Quota exceeded!");
     }
 
     public void testQuotaReported() throws Exception {
         if (!isBackupSupported()) {
             return;
         }
-        exec("logcat --clear");
+        String separator = clearLogcat();
         exec("bmgr backupnow " + BACKUP_APP_NAME);
-        assertTrue("Quota not reported correctly",
-                waitForLogcat("quota is " + LOCAL_TRANSPORT_BACKUP_QUOTA, 10));
+        waitForLogcat(TIMEOUT_SECONDS, separator,
+            "quota is " + LOCAL_TRANSPORT_BACKUP_QUOTA);
     }
 
 }