Merge "Improving hierarchy dump for test failure" into sc-dev
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 4c47947..dc59bdd 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -2,6 +2,8 @@
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
 import android.util.Log;
 
 import androidx.test.uiautomator.UiDevice;
@@ -12,9 +14,12 @@
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
 
-import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 public class FailureWatcher extends TestWatcher {
     private static final String TAG = "FailureWatcher";
@@ -26,20 +31,6 @@
         mLauncher = launcher;
     }
 
-    private static void dumpViewHierarchy(UiDevice device) {
-        final ByteArrayOutputStream stream = new ByteArrayOutputStream();
-        try {
-            device.dumpWindowHierarchy(stream);
-            stream.flush();
-            stream.close();
-            for (String line : stream.toString().split("\\r?\\n")) {
-                Log.e(TAG, line.trim());
-            }
-        } catch (IOException e) {
-            Log.e(TAG, "error dumping XML to logcat", e);
-        }
-    }
-
     @Override
     protected void succeeded(Description description) {
         super.succeeded(description);
@@ -53,22 +44,41 @@
 
     public static void onError(UiDevice device, Description description, Throwable e) {
         if (device == null) return;
-        final String pathname = getInstrumentation().getTargetContext().
-                getFilesDir().getPath() + "/TestScreenshot-" + description.getMethodName()
-                + ".png";
-        Log.e(TAG, "Failed test " + description.getMethodName() +
-                ", screenshot will be saved to " + pathname +
-                ", track trace is below, UI object dump is further below:\n" +
-                Log.getStackTraceString(e));
-        dumpViewHierarchy(device);
+        final File parentFile = getInstrumentation().getTargetContext().getFilesDir();
+        final File sceenshot = new File(parentFile,
+                "TestScreenshot-" + description.getMethodName() + ".png");
+        final File hierarchy = new File(parentFile,
+                "Hierarchy-" + description.getMethodName() + ".zip");
 
-        try {
-            final String dumpsysResult = device.executeShellCommand(
-                    "dumpsys activity service TouchInteractionService");
-            Log.d(TAG, "TouchInteractionService: " + dumpsysResult);
-        } catch (IOException ex) {
+        // Dump window hierarchy
+        try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(hierarchy))) {
+            out.putNextEntry(new ZipEntry("bugreport.txt"));
+            dumpStringCommand("dumpsys window windows", out);
+            dumpStringCommand("dumpsys package", out);
+            dumpStringCommand("dumpsys activity service TouchInteractionService", out);
+            out.closeEntry();
+
+            out.putNextEntry(new ZipEntry("visible_windows.zip"));
+            dumpCommand("cmd window dump-visible-window-views", out);
+            out.closeEntry();
+        } catch (IOException ex) { }
+
+        Log.e(TAG, "Failed test " + description.getMethodName()
+                + ",\nscreenshot will be saved to " + sceenshot
+                + ",\nUI dump at: " + hierarchy
+                + " (use go/web-hv to open the dump file)", e);
+        device.takeScreenshot(sceenshot);
+    }
+
+    private static void dumpStringCommand(String cmd, OutputStream out) throws IOException {
+        out.write(("\n\n" + cmd + "\n").getBytes());
+        dumpCommand(cmd, out);
+    }
+
+    private static void dumpCommand(String cmd, OutputStream out) throws IOException {
+        try (AutoCloseInputStream in = new AutoCloseInputStream(getInstrumentation()
+                .getUiAutomation().executeShellCommand(cmd))) {
+            FileUtils.copy(in, out);
         }
-
-        device.takeScreenshot(new File(pathname));
     }
 }