From 06fdef41320c4af172459d0b5dd5bd92ea1ff666 Mon Sep 17 00:00:00 2001 From: Andrew Halberstadt Date: Thu, 27 Feb 2014 16:36:00 -0500 Subject: [PATCH] Bug 976120 - shell.js should respect crash reporting environment, r=fabrice --- .../marionette/client/marionette/__init__.py | 1 + .../client/marionette/decorators.py | 65 +++++++++++++++++++ .../marionette/client/marionette/emulator.py | 42 +++++++----- .../client/marionette/marionette.py | 15 ++--- 4 files changed, 98 insertions(+), 25 deletions(-) create mode 100644 testing/marionette/client/marionette/decorators.py diff --git a/testing/marionette/client/marionette/__init__.py b/testing/marionette/client/marionette/__init__.py index d30605c021e4..fabb1d04220e 100644 --- a/testing/marionette/client/marionette/__init__.py +++ b/testing/marionette/client/marionette/__init__.py @@ -11,3 +11,4 @@ from errors import * from runner import * from wait import Wait from date_time_value import DateTimeValue +import decorators diff --git a/testing/marionette/client/marionette/decorators.py b/testing/marionette/client/marionette/decorators.py new file mode 100644 index 000000000000..d5bc78056d25 --- /dev/null +++ b/testing/marionette/client/marionette/decorators.py @@ -0,0 +1,65 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from errors import MarionetteException +from functools import wraps +import sys +import traceback + +def _find_marionette_in_args(*args, **kwargs): + try: + m = [a for a in args + tuple(kwargs.values()) if hasattr(a, 'session')][0] + except IndexError: + print("Can only apply decorator to function using a marionette object") + raise + return m + +def do_crash_check(func, always=False): + """Decorator which checks for crashes after the function has run. + + :param always: If False, only checks for crashes if an exception + was raised. If True, always checks for crashes. + """ + @wraps(func) + def _(*args, **kwargs): + def check(): + m = _find_marionette_in_args(*args, **kwargs) + try: + m.check_for_crash() + except: + # don't want to lose the original exception + traceback.print_exc() + + try: + return func(*args, **kwargs) + except MarionetteException: + exc, val, tb = sys.exc_info() + if not always: + check() + raise exc, val, tb + finally: + if always: + check() + return _ + +def uses_marionette(func): + """Decorator which creates a marionette session and deletes it + afterwards if one doesn't already exist. + """ + @wraps(func) + def _(*args, **kwargs): + m = _find_marionette_in_args(*args, **kwargs) + delete_session = False + if not m.session: + delete_session = True + m.start_session() + + m.set_context(m.CONTEXT_CHROME) + ret = func(*args, **kwargs) + + if delete_session: + m.delete_session() + + return ret + return _ diff --git a/testing/marionette/client/marionette/emulator.py b/testing/marionette/client/marionette/emulator.py index b2fe9adcf443..3483180c38c1 100644 --- a/testing/marionette/client/marionette/emulator.py +++ b/testing/marionette/client/marionette/emulator.py @@ -4,7 +4,6 @@ from b2ginstance import B2GInstance import datetime -from errors import * from mozdevice import devicemanagerADB, DMError from mozprocess import ProcessHandlerMixin import os @@ -21,6 +20,15 @@ import traceback from emulator_battery import EmulatorBattery from emulator_geo import EmulatorGeo from emulator_screen import EmulatorScreen +from decorators import uses_marionette + +from errors import ( + InstallGeckoError, + InvalidResponseException, + MarionetteException, + ScriptTimeoutException, + TimeoutException +) class LogOutputProc(ProcessHandlerMixin): @@ -51,6 +59,9 @@ class Emulator(object): prefs = {'app.update.enabled': False, 'app.update.staging.enabled': False, 'app.update.service.enabled': False} + env = {'MOZ_CRASHREPORTER': '1', + 'MOZ_CRASHREPORTER_NO_REPORT': '1', + 'MOZ_CRASHREPORTER_SHUTDOWN': '1'} def __init__(self, homedir=None, noWindow=False, logcat_dir=None, arch="x86", emulatorBinary=None, res=None, sdcard=None, @@ -256,9 +267,8 @@ class Emulator(object): else: self._adb_started = False + @uses_marionette def wait_for_system_message(self, marionette): - marionette.start_session() - marionette.set_context(marionette.CONTEXT_CHROME) marionette.set_script_timeout(45000) # Telephony API's won't be available immediately upon emulator # boot; we have to wait for the syste-message-listener-ready @@ -282,9 +292,7 @@ waitFor( except InvalidResponseException: self.check_for_minidumps() raise - print 'done' - marionette.set_context(marionette.CONTEXT_CONTENT) - marionette.delete_session() + print '...done' def connect(self): self.adb = B2GInstance.check_adb(self.homedir, emulator=True) @@ -355,14 +363,10 @@ waitFor( # setup DNS fix for networking self._run_adb(['shell', 'setprop', 'net.dns1', '10.0.2.3']) + @uses_marionette def wait_for_homescreen(self, marionette): print 'waiting for homescreen...' - created_session = False - if not marionette.session: - marionette.start_session() - created_session = True - marionette.set_context(marionette.CONTEXT_CONTENT) marionette.execute_async_script(""" log('waiting for mozbrowserloadend'); @@ -374,10 +378,9 @@ window.addEventListener('mozbrowserloadend', function loaded(aEvent) { } });""", script_timeout=120000) print '...done' - if created_session: - marionette.delete_session() def setup(self, marionette, gecko_path=None, busybox=None): + self.set_environment(marionette) if busybox: self.install_busybox(busybox) @@ -387,9 +390,17 @@ window.addEventListener('mozbrowserloadend', function loaded(aEvent) { self.wait_for_system_message(marionette) self.set_prefs(marionette) + @uses_marionette + def set_environment(self, marionette): + for k, v in self.env.iteritems(): + marionette.execute_script(""" + let env = Cc["@mozilla.org/process/environment;1"]. + getService(Ci.nsIEnvironment); + env.set("%s", "%s"); + """ % (k, v)) + + @uses_marionette def set_prefs(self, marionette): - marionette.start_session() - marionette.set_context(marionette.CONTEXT_CHROME) for pref in self.prefs: marionette.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); @@ -405,7 +416,6 @@ window.addEventListener('mozbrowserloadend', function loaded(aEvent) { Services.prefs.setCharPref(arguments[0], arguments[1]); } """, [pref, self.prefs[pref]]) - marionette.delete_session() def restart_b2g(self): print 'restarting B2G' diff --git a/testing/marionette/client/marionette/marionette.py b/testing/marionette/client/marionette/marionette.py index 293abe42112b..1cd2db774c31 100644 --- a/testing/marionette/client/marionette/marionette.py +++ b/testing/marionette/client/marionette/marionette.py @@ -12,6 +12,7 @@ import traceback from application_cache import ApplicationCache from client import MarionetteClient +from decorators import do_crash_check from emulator import Emulator from emulator_screen import EmulatorScreen from errors import * @@ -420,6 +421,7 @@ class MultiActions(object): ''' return self.marionette._send_message('multiAction', 'ok', value=self.multi_actions, max_length=self.max_length) + class Marionette(object): """ Represents a Marionette connection to a browser or device. @@ -584,6 +586,7 @@ class Marionette(object): time.sleep(1) return False + @do_crash_check def _send_message(self, command, response_key="ok", **kwargs): if not self.session and command != "newSession": raise MarionetteException("Please start a session") @@ -596,7 +599,7 @@ class Marionette(object): try: response = self.client.send(message) - except socket.timeout as e: + except socket.timeout: self.session = None self.window = None self.client.close() @@ -714,14 +717,8 @@ class Marionette(object): """ - try: - # We are ignoring desired_capabilities, at least for now. - self.session = self._send_message('newSession', 'value') - except: - exc, val, tb = sys.exc_info() - self.check_for_crash() - raise exc, val, tb - + # We are ignoring desired_capabilities, at least for now. + self.session = self._send_message('newSession', 'value') self.b2g = 'b2g' in self.session return self.session