Bug 940954: Allow marionette to restart the browser and create a new session
☠☠ backed out by 4a8fb3fdb421 ☠ ☠
authorDavid Burns <dburns@mozilla.com>
Wed, 26 Nov 2014 00:33:53 +0000
changeset 218260 1b87e75119839c47d4d398cae04233e2b0927c34
parent 218259 6dea67b71cf25fbb367455f322e7c867daa03251
child 218261 e57c4cf44a6dab6e6b521c2ed4a1df2d9d003ca9
push id27925
push usercbook@mozilla.com
push dateWed, 03 Dec 2014 12:32:33 +0000
treeherdermozilla-central@59b7bf5d119d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs940954
milestone37.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 940954: Allow marionette to restart the browser and create a new session This gives us the ability to restart a session from the client side, say for testing Firefox updates, and then carry on the test. To do this it is just self.marionette.restart()
testing/marionette/client/marionette/geckoinstance.py
testing/marionette/client/marionette/marionette.py
testing/marionette/client/marionette/tests/unit/test_profile_management.py
--- a/testing/marionette/client/marionette/geckoinstance.py
+++ b/testing/marionette/client/marionette/geckoinstance.py
@@ -25,39 +25,46 @@ class GeckoInstance(object):
                       "browser.displayedE10SPrompt": 5,
                       "browser.displayedE10SPrompt.1": 5,
                       "browser.displayedE10SPrompt.2": 5,
                       "browser.displayedE10SPrompt.3": 5,
                       "browser.displayedE10SPrompt.4": 5,
                       "browser.tabs.remote.autostart.1": False,
                       "browser.tabs.remote.autostart.2": False}
 
-    def __init__(self, host, port, bin, profile, app_args=None, symbols_path=None,
-                  gecko_log=None, prefs=None):
+    def __init__(self, host, port, bin, profile=None, app_args=None, symbols_path=None,
+                  gecko_log=None, prefs=None, ):
         self.marionette_host = host
         self.marionette_port = port
         self.bin = bin
-        self.profile_path = profile
+        # Check if it is a Profile object or a path to profile
+        self.profile = None
+        if isinstance(profile, Profile):
+            self.profile = profile
+        else:
+            self.profile_path = profile
         self.prefs = prefs
         self.app_args = app_args or []
         self.runner = None
         self.symbols_path = symbols_path
         self.gecko_log = gecko_log
 
     def start(self):
         profile_args = {"preferences": deepcopy(self.required_prefs)}
         profile_args["preferences"]["marionette.defaultPrefs.port"] = self.marionette_port
         if self.prefs:
             profile_args["preferences"].update(self.prefs)
-        if not self.profile_path:
-            profile_args["restore"] = False
-            profile = Profile(**profile_args)
-        else:
-            profile_args["path_from"] = self.profile_path
-            profile = Profile.clone(**profile_args)
+
+        if hasattr(self, "profile_path") and self.profile is None:
+            if not self.profile_path:
+                profile_args["restore"] = False
+                self.profile = Profile(**profile_args)
+            else:
+                profile_args["path_from"] = self.profile_path
+                self.profile = Profile.clone(**profile_args)
 
         process_args = {
             'processOutputLine': [NullOutput()],
         }
 
         if self.gecko_log == '-':
             process_args['stream'] = sys.stdout
         else:
@@ -96,30 +103,38 @@ class GeckoInstance(object):
         env = os.environ.copy()
 
         # environment variables needed for crashreporting
         # https://developer.mozilla.org/docs/Environment_variables_affecting_crash_reporting
         env.update({ 'MOZ_CRASHREPORTER': '1',
                      'MOZ_CRASHREPORTER_NO_REPORT': '1', })
         self.runner = Runner(
             binary=self.bin,
-            profile=profile,
+            profile=self.profile,
             cmdargs=['-no-remote', '-marionette'] + self.app_args,
             env=env,
             symbols_path=self.symbols_path,
             process_args=process_args)
         self.runner.start()
 
-    def close(self):
+    def close(self, restart=False):
+        if not restart:
+            self.profile.cleanup()
+            self.profile = None
+
         if self.runner:
             self.runner.stop()
             self.runner.cleanup()
 
-    def restart(self, prefs=None):
-        self.close()
+    def restart(self, prefs=None, clean=True):
+        if clean:
+            self.profile.cleanup()
+            self.profile = None
+
+        self.close(restart=True)
         if prefs:
             self.prefs = prefs
         else:
             self.prefs = None
         self.start()
 
 class B2GDesktopInstance(GeckoInstance):
     required_prefs = {"focusmanager.testmode": True}
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -470,16 +470,17 @@ class Marionette(object):
                  profile=None, emulator=None, sdcard=None, emulator_img=None,
                  emulator_binary=None, emulator_res=None, connect_to_running_emulator=False,
                  gecko_log=None, homedir=None, baseurl=None, no_window=False, logdir=None,
                  busybox=None, symbols_path=None, timeout=None, socket_timeout=360,
                  device_serial=None, adb_path=None, process_args=None):
         self.host = host
         self.port = self.local_port = port
         self.bin = bin
+        self.profile = profile
         self.instance = None
         self.session = None
         self.session_id = None
         self.window = None
         self.runner = None
         self.emulator = None
         self.extra_emulators = []
         self.baseurl = baseurl
@@ -507,33 +508,33 @@ class Marionette(object):
                     config.read(os.path.join(os.path.dirname(bin), 'application.ini'))
                     app = config.get('App', 'Name')
                     instance_class = geckoinstance.apps[app.lower()]
                 except (ConfigParser.NoOptionError,
                         ConfigParser.NoSectionError,
                         KeyError):
                     instance_class = geckoinstance.GeckoInstance
             self.instance = instance_class(host=self.host, port=self.port,
-                                           bin=self.bin, profile=profile,
+                                           bin=self.bin, profile=self.profile,
                                            app_args=app_args, symbols_path=symbols_path,
                                            gecko_log=gecko_log)
             self.instance.start()
             assert(self.wait_for_port()), "Timed out waiting for port!"
 
         if emulator:
             self.runner = B2GEmulatorRunner(b2g_home=homedir,
                                             no_window=self.no_window,
                                             logdir=logdir,
                                             arch=emulator,
                                             sdcard=sdcard,
                                             symbols_path=symbols_path,
                                             binary=emulator_binary,
                                             userdata=emulator_img,
                                             resolution=emulator_res,
-                                            profile=profile,
+                                            profile=self.profile,
                                             adb_path=adb_path,
                                             process_args=process_args)
             self.emulator = self.runner.device
             self.emulator.start()
             self.port = self.emulator.setup_port_forwarding(remote_port=self.port)
             assert(self.emulator.wait_for_port(self.port)), "Timed out waiting for port!"
 
         if connect_to_running_emulator:
@@ -768,30 +769,30 @@ class Marionette(object):
         self.set_context(self.CONTEXT_CONTENT)
         if not pref_exists:
             self.delete_session()
             self.instance.restart(prefs)
             assert(self.wait_for_port()), "Timed out waiting for port!"
             self.start_session()
             self._reset_timeouts()
 
-    def restart_with_clean_profile(self):
+    def restart(self, clean=False):
         """
         This will terminate the currently running instance, and spawn a new instance
-        with a clean profile.
+        with the same profile and then reuse the session id when creating a session again.
 
         : param prefs: A dictionary whose keys are preference names.
         """
         if not self.instance:
-            raise errors.MarionetteException("enforce_gecko_prefs can only be called " \
+            raise errors.MarionetteException("restart can only be called " \
                                              "on gecko instances launched by Marionette")
         self.delete_session()
-        self.instance.restart()
+        self.instance.restart(clean=clean)
         assert(self.wait_for_port()), "Timed out waiting for port!"
-        self.start_session()
+        self.start_session(session_id=self.session_id)
         self._reset_timeouts()
 
     def absolute_url(self, relative_url):
         '''
         Returns an absolute url for files served from Marionette's www directory.
 
         :param relative_url: The url of a static file, relative to Marionette's www directory.
         '''
--- a/testing/marionette/client/marionette/tests/unit/test_profile_management.py
+++ b/testing/marionette/client/marionette/tests/unit/test_profile_management.py
@@ -22,11 +22,17 @@ class TestLog(MarionetteTestCase):
     def test_change_preset(self):
         bool_value = self.marionette.execute_script("return SpecialPowers.getBoolPref('marionette.test.bool');")
         self.assertTrue(bool_value)
         self.marionette.enforce_gecko_prefs({"marionette.test.bool": False})
         bool_value = self.marionette.execute_script("return SpecialPowers.getBoolPref('marionette.test.bool');")
         self.assertFalse(bool_value)
 
     def test_clean_profile(self):
-        self.marionette.restart_with_clean_profile()
+        self.marionette.restart(clean=True)
         with self.assertRaisesRegexp(JavascriptException, "Error getting pref"):
             bool_value = self.marionette.execute_script("return SpecialPowers.getBoolPref('marionette.test.bool');")
+
+    def test_can_restart_the_browser(self):
+        self.marionette.enforce_gecko_prefs({"marionette.test.restart": True})
+        self.marionette.restart()
+        bool_value = self.marionette.execute_script("return SpecialPowers.getBoolPref('marionette.test.restart');")
+        self.assertTrue(bool_value)
\ No newline at end of file