Bug 1482878 - Use screencap to capture on physical android device screenshots, r=gbrown.
authorBob Clary <bclary@bclary.com>
Sat, 18 Aug 2018 10:47:12 -0700
changeset 432283 74595d74ea3e25df0b5e49c1f7f1c61ab8d006ed
parent 432282 4956d2326228f24cbaf6dfebb2362aab983ae7e3
child 432284 65682bbb4b81ccb612c067aab9601a3b047d3a86
push id34466
push userdluca@mozilla.com
push dateSat, 18 Aug 2018 21:36:39 +0000
treeherdermozilla-central@6190326e1b38 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgbrown
bugs1482878
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1482878 - Use screencap to capture on physical android device screenshots, r=gbrown.
build/mobile/remoteautomation.py
testing/mochitest/runtestsremote.py
testing/mozbase/mozscreenshot/mozscreenshot/__init__.py
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -10,17 +10,17 @@ import os
 import posixpath
 import tempfile
 import shutil
 import sys
 
 from automation import Automation
 from mozdevice import ADBTimeoutError
 from mozlog import get_default_logger
-from mozscreenshot import dump_screen
+from mozscreenshot import dump_screen, dump_device_screen
 import mozcrash
 
 # signatures for logcat messages that we don't care about much
 fennecLogcatFilters = ["The character encoding of the HTML document was not declared",
                        "Use of Mutation Events is deprecated. Use MutationObserver instead.",
                        "Unexpected value from nativeGetEnabledTags: 0"]
 
 
@@ -405,21 +405,24 @@ class RemoteAutomation(Automation):
                         print("Failed to get top activity, retrying, once...")
                         top = self.device.get_top_activity(timeout=60)
             # Flush anything added to stdout during the sleep
             self.read_stdout()
             print("wait for %s complete; top activity=%s" % (self.procName, top))
             return status
 
         def kill(self, stagedShutdown=False):
-            if self.utilityPath:
-                # Take a screenshot to capture the screen state just before
-                # the application is killed. There are on-device screenshot
-                # options but they rarely work well with Firefox on the
-                # Android emulator. dump_screen provides an effective
+            # Take a screenshot to capture the screen state just before
+            # the application is killed.
+            if not self.device._device_serial.startswith('emulator-'):
+                dump_device_screen(self.device, get_default_logger())
+            elif self.utilityPath:
+                # Do not use the on-device screenshot options since
+                # they rarely work well with Firefox on the Android
+                # emulator. dump_screen provides an effective
                 # screenshot of the emulator and its host desktop.
                 dump_screen(self.utilityPath, get_default_logger())
             if stagedShutdown:
                 # Trigger an ANR report with "kill -3" (SIGQUIT)
                 try:
                     self.device.pkill(self.procName, sig=3, attempts=1)
                 except ADBTimeoutError:
                     raise
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -11,18 +11,18 @@ sys.path.insert(
     0, os.path.abspath(
         os.path.realpath(
             os.path.dirname(__file__))))
 
 from automation import Automation
 from remoteautomation import RemoteAutomation, fennecLogcatFilters
 from runtests import MochitestDesktop, MessageLogger
 from mochitest_options import MochitestArgumentParser
-
 from mozdevice import ADBAndroid, ADBTimeoutError
+from mozscreenshot import dump_screen, dump_device_screen
 import mozinfo
 
 SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
 
 
 class MochiRemote(MochitestDesktop):
     localProfile = None
     logMessages = []
@@ -111,16 +111,27 @@ class MochiRemote(MochitestDesktop):
             if uploadDir and self.device.is_dir(self.remoteMozLog):
                 self.device.pull(self.remoteMozLog, uploadDir)
         self.device.rm(self.remoteLogFile, force=True)
         self.device.rm(self.remoteProfile, force=True, recursive=True)
         self.device.rm(self.remoteCache, force=True, recursive=True)
         MochitestDesktop.cleanup(self, options, final)
         self.localProfile = None
 
+    def dumpScreen(self, utilityPath):
+        if self.haveDumpedScreen:
+            self.log.info(
+                "Not taking screenshot here: see the one that was previously logged")
+            return
+        self.haveDumpedScreen = True
+        if self.device._device_serial.startswith('emulator-'):
+            dump_screen(utilityPath, self.log)
+        else:
+            dump_device_screen(self.device, self.log)
+
     def findPath(self, paths, filename=None):
         for path in paths:
             p = path
             if filename:
                 p = os.path.join(p, filename)
             if os.path.exists(self.getFullPath(p)):
                 return path
         return None
--- a/testing/mozbase/mozscreenshot/mozscreenshot/__init__.py
+++ b/testing/mozbase/mozscreenshot/mozscreenshot/__init__.py
@@ -18,17 +18,22 @@ def printstatus(name, returncode):
     Note that mozlog structured action "process_exit" should be used
     instead of that in new code.
     """
     print("TEST-INFO | %s: %s" % (name, strstatus(returncode)))
 
 
 def dump_screen(utilityPath, log):
     """dumps a screenshot of the entire screen to a directory specified by
-    the MOZ_UPLOAD_DIR environment variable"""
+    the MOZ_UPLOAD_DIR environment variable.
+
+    :param utilityPath: Path of utility programs. This is typically a path
+        to either the objdir's bin directory or a path to the host utilities.
+    :param log: Reference to logger.
+    """
 
     is_structured_log = hasattr(log, 'process_exit')
 
     # Need to figure out which OS-dependent tool to use
     if mozinfo.isUnix:
         utility = [os.path.join(utilityPath, "screentopng")]
         utilityname = "screentopng"
     elif mozinfo.isMac:
@@ -56,8 +61,53 @@ def dump_screen(utilityPath, log):
         returncode = subprocess.call(utility + [imgfilename])
         if is_structured_log:
             log.process_exit(utilityname, returncode)
         else:
             printstatus(utilityname, returncode)
     except OSError as err:
         log.info("Failed to start %s for screenshot: %s"
                  % (utility[0], err.strerror))
+
+
+def dump_device_screen(device, log):
+    """dumps a screenshot of a real device's entire screen to a directory
+    specified by the MOZ_UPLOAD_DIR environment variable. Cloned from
+    mozscreenshot.dump_screen.
+
+    :param device: Reference to an ADBAndroid object which provides the
+        interface to interact with Android devices.
+    :param log: Reference to logger.
+    """
+
+    utilityname = 'screencap'
+    is_structured_log = hasattr(log, 'process_exit')
+
+    # Get dir where to write the screenshot file
+    parent_dir = os.environ.get('MOZ_UPLOAD_DIR', None)
+    if not parent_dir:
+        log.info('Failed to retrieve MOZ_UPLOAD_DIR env var')
+        return
+
+    # Run the capture
+    try:
+        # Android 6.0 and later support mktemp.  See
+        # https://android.googlesource.com/platform/system/core/
+        # +/master/shell_and_utilities/README.md#android-6_0-marshmallow
+        # We can use mktemp on real devices since we do not test on
+        # real devices older than Android 6.0. Note we must create the
+        # file without an extension due to limitations in mktemp.
+        filename = device.shell_output('mktemp -p %s mozilla-test-fail-screenshot_XXXXXX' %
+                                       device.test_root)
+        pngfilename = filename + '.png'
+        device.mv(filename, pngfilename)
+        if is_structured_log:
+            log.process_start(utilityname)
+        device.shell_output('%s -p %s' % (utilityname, pngfilename))
+        if is_structured_log:
+            log.process_exit(utilityname, 0)
+        else:
+            printstatus(utilityname, 0)
+        device.pull(pngfilename, parent_dir)
+        device.rm(pngfilename)
+    except Exception as err:
+        log.info("Failed to start %s for screenshot: %s"
+                 % (utilityname, str(err)))