Bug 1137388 - Add a facility to restart firefox from marionette from within the browser for update tests.;r=automatedtester
authorChris Manchester <cmanchester@mozilla.com>
Fri, 06 Mar 2015 17:39:26 -0800
changeset 232342 56083b5a4473a5f4f8715c9d1a07b105abba04f6
parent 232341 efa7c6cf88fce711716619cb1b37035fa48387bc
child 232343 0d5b57b0063bc9cb4bd62d0db2397e525022c7d9
push id56521
push usercmanchester@mozilla.com
push dateSat, 07 Mar 2015 01:44:04 +0000
treeherdermozilla-inbound@56083b5a4473 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersautomatedtester
bugs1137388
milestone39.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 1137388 - Add a facility to restart firefox from marionette from within the browser for update tests.;r=automatedtester
testing/marionette/client/marionette/tests/unit/test_profile_management.py
testing/marionette/driver/marionette_driver/marionette.py
testing/marionette/marionette-server.js
--- a/testing/marionette/client/marionette/tests/unit/test_profile_management.py
+++ b/testing/marionette/client/marionette/tests/unit/test_profile_management.py
@@ -30,9 +30,35 @@ class TestLog(MarionetteTestCase):
         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
+        self.assertTrue(bool_value)
+
+    def test_in_app_restart_the_browser(self):
+        self.marionette.execute_script("SpecialPowers.setBoolPref('marionette.test.restart', true);")
+
+        # A "soft" restart initiated inside the application should keep track of this pref.
+        self.marionette.restart(in_app=True)
+        bool_value = self.marionette.execute_script("""
+          return SpecialPowers.getBoolPref('marionette.test.restart');
+        """)
+        self.assertTrue(bool_value)
+
+        bool_value = self.marionette.execute_script("""
+          SpecialPowers.setBoolPref('marionette.test.restart', false);
+          return SpecialPowers.getBoolPref('marionette.test.restart');
+        """)
+        self.assertFalse(bool_value)
+
+        # A "hard" restart is still possible (i.e., our instance is still able
+        # to kill the browser).
+        self.marionette.restart(in_app=False)
+
+        bool_value = self.marionette.execute_script("""
+          return SpecialPowers.getBoolPref('marionette.test.restart');
+        """)
+        # The "hard" restart blows away the pref we set.
+        self.assertTrue(bool_value)
--- a/testing/marionette/driver/marionette_driver/marionette.py
+++ b/testing/marionette/driver/marionette_driver/marionette.py
@@ -878,28 +878,48 @@ 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(self, clean=False):
+    def restart(self, clean=False, in_app=False):
         """
         This will terminate the currently running instance, and spawn a new instance
         with the same profile and then reuse the session id when creating a session again.
 
-        : param prefs: A dictionary whose keys are preference names.
+        : param clean: If False the same profile will be used after the restart. Note
+                       that the in app initiated restart always maintains the same
+                       profile.
+        : param in_app: If True, marionette will cause a restart from within the
+                        browser. Otherwise the browser will be restarted immediately
+                        by killing the process.
         """
         if not self.instance:
             raise errors.MarionetteException("restart can only be called " \
                                              "on gecko instances launched by Marionette")
-        self.delete_session()
-        self.instance.restart(clean=clean)
+
+        if in_app:
+            if clean:
+                raise ValueError
+            # Values here correspond to constants in nsIAppStartup.
+            # See https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIAppStartup
+            restart_flags = [
+                "eForceQuit",
+                "eRestart",
+            ]
+            try:
+                self._send_message('quitApplication', flags=restart_flags)
+            except IOError:
+                self.client.close()
+        else:
+            self.delete_session()
+            self.instance.restart(clean=clean)
         assert(self.wait_for_port()), "Timed out waiting for port!"
         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.
 
--- a/testing/marionette/marionette-server.js
+++ b/testing/marionette/marionette-server.js
@@ -2613,16 +2613,36 @@ MarionetteServerConnection.prototype = {
     catch (e) {
       this.sendError("Could not delete session", 500, e.name + ": " + e.message, command_id);
       return;
     }
     this.sendOk(command_id);
   },
 
   /**
+   * Quits the application with the provided flags and tears down the
+   * current session.
+   */
+  quitApplication: function MDA_quitApplication (aRequest) {
+    let command_id = this.getCommandId();
+    if (appName != "Firefox") {
+      this.sendError("In app initiated quit only supported on Firefox", 500, null, command_id);
+    }
+
+    let flagsArray = aRequest.parameters.flags;
+    let flags = Ci.nsIAppStartup.eAttemptQuit;
+    for (let k of flagsArray) {
+      flags |= Ci.nsIAppStartup[k];
+    }
+
+    this.sessionTearDown();
+    Services.startup.quit(flags);
+  },
+
+  /**
    * Returns the current status of the Application Cache
    */
   getAppCacheStatus: function MDA_getAppCacheStatus(aRequest) {
     this.command_id = this.getCommandId();
     this.sendAsync("getAppCacheStatus", {}, this.command_id);
   },
 
   _emu_cb_id: 0,
@@ -3294,16 +3314,17 @@ MarionetteServerConnection.prototype.req
   "getCurrentWindowHandles": MarionetteServerConnection.prototype.getWindowHandles,  // Selenium 2 compat
   "getWindows":  MarionetteServerConnection.prototype.getWindowHandles,  // deprecated
   "getWindowPosition": MarionetteServerConnection.prototype.getWindowPosition,
   "setWindowPosition": MarionetteServerConnection.prototype.setWindowPosition,
   "getActiveFrame": MarionetteServerConnection.prototype.getActiveFrame,
   "switchToFrame": MarionetteServerConnection.prototype.switchToFrame,
   "switchToWindow": MarionetteServerConnection.prototype.switchToWindow,
   "deleteSession": MarionetteServerConnection.prototype.deleteSession,
+  "quitApplication": MarionetteServerConnection.prototype.quitApplication,
   "emulatorCmdResult": MarionetteServerConnection.prototype.emulatorCmdResult,
   "importScript": MarionetteServerConnection.prototype.importScript,
   "clearImportedScripts": MarionetteServerConnection.prototype.clearImportedScripts,
   "getAppCacheStatus": MarionetteServerConnection.prototype.getAppCacheStatus,
   "close": MarionetteServerConnection.prototype.close,
   "closeWindow": MarionetteServerConnection.prototype.close,  // deprecated
   "closeChromeWindow": MarionetteServerConnection.prototype.closeChromeWindow,
   "setTestName": MarionetteServerConnection.prototype.setTestName,