From 0945dd6ba483d3ff2320dfe38cc54217e5487c95 Mon Sep 17 00:00:00 2001
From: John Richardson <john.richardson@mobica.com>
Date: Tue, 2 Aug 2016 16:21:47 +0100
Subject: [PATCH] Dump hierarchy view on error

Dump window hierarchy view from uiautomator to a file when WA fails
during execution. Note: the xml file are pre-formatted after dump.
Implementation specific to android.device.
---
 wlauto/common/android/device.py | 12 ++++++++++++
 wlauto/core/execution.py        |  9 +++++++++
 2 files changed, 21 insertions(+)

diff --git a/wlauto/common/android/device.py b/wlauto/common/android/device.py
index a269652a..c4e65382 100644
--- a/wlauto/common/android/device.py
+++ b/wlauto/common/android/device.py
@@ -22,6 +22,7 @@ import tempfile
 import shutil
 import threading
 import json
+import xml.dom.minidom
 from subprocess import CalledProcessError
 
 from wlauto.core.extension import Parameter
@@ -616,6 +617,17 @@ class AndroidDevice(BaseLinuxDevice):  # pylint: disable=W0223
         self.pull_file(on_device_file, filepath)
         self.delete_file(on_device_file)
 
+    def capture_view_hierachy(self, filepath):
+        """Captures the current view hierarchy into the specified file in a XML format."""
+        on_device_file = self.path.join(self.working_directory, 'screen_capture.xml')
+        self.execute('uiautomator dump {}'.format(on_device_file))
+        self.pull_file(on_device_file, filepath)
+        self.delete_file(on_device_file)
+
+        parsed_xml = xml.dom.minidom.parse(filepath)
+        with open(filepath, 'w') as f:
+            f.write(parsed_xml.toprettyxml())
+
     def is_screen_on(self):
         """Returns ``True`` if the device screen is currently on, ``False`` otherwise."""
         output = self.execute('dumpsys power')
diff --git a/wlauto/core/execution.py b/wlauto/core/execution.py
index 652816fc..659c424f 100644
--- a/wlauto/core/execution.py
+++ b/wlauto/core/execution.py
@@ -731,6 +731,13 @@ class Runner(object):
             filepath = os.path.join(settings.output_directory, filename)
         self.device.capture_screen(filepath)
 
+    def _take_uiautomator_dump(self, filename):
+        if self.context.output_directory:
+            filepath = os.path.join(self.context.output_directory, filename)
+        else:
+            filepath = os.path.join(settings.output_directory, filename)
+        self.device.capture_view_hierachy(filepath)
+
     @contextmanager
     def _handle_errors(self, action, on_error_status=IterationResult.FAILED):
         try:
@@ -746,6 +753,8 @@ class Runner(object):
                 self.current_job.result.add_event(str(we))
             try:
                 self._take_screenshot('error.png')
+                if self.device.platform == 'android':
+                    self._take_uiautomator_dump('error.xml')
             except Exception, e:  # pylint: disable=W0703
                 # We're already in error state, so the fact that taking a
                 # screenshot failed is not surprising...