Bug 1073732: Allow Marionette to have sessions ids that are unique for that session; r=jgriffin
authorDavid Burns <dburns@mozilla.com>
Tue, 11 Nov 2014 15:55:23 +0000
changeset 215004 181e0dae85b93d027c9511abeafbadc3271911ed
parent 215003 f1ceb5df9d6640caf16189d00f08ae0952e676bf
child 215005 a19479860eb4e6a1e45ec288ccccaaba6131103b
push id51634
push userdburns@mozilla.com
push dateTue, 11 Nov 2014 16:07:52 +0000
treeherdermozilla-inbound@181e0dae85b9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgriffin
bugs1073732
milestone36.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 1073732: Allow Marionette to have sessions ids that are unique for that session; r=jgriffin
testing/marionette/client/marionette/marionette.py
testing/marionette/client/marionette/tests/unit/test_session.py
testing/marionette/marionette-server.js
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -474,16 +474,17 @@ class Marionette(object):
                  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.instance = None
         self.session = None
+        self.session_id = None
         self.window = None
         self.runner = None
         self.emulator = None
         self.extra_emulators = []
         self.baseurl = baseurl
         self.no_window = no_window
         self._test_name = None
         self.timeout = timeout
@@ -602,22 +603,22 @@ class Marionette(object):
             finally:
                 if sock:
                     sock.close()
             time.sleep(poll_interval)
         return False
 
     @do_crash_check
     def _send_message(self, command, response_key="ok", **kwargs):
-        if not self.session and command != "newSession":
+        if not self.session_id and command != "newSession":
             raise errors.MarionetteException("Please start a session")
 
         message = {"name": command}
-        if self.session:
-            message["sessionId"] = self.session
+        if self.session_id:
+            message["sessionId"] = self.session_id
         if kwargs:
             message["parameters"] = kwargs
 
         try:
             response = self.client.send(message)
         except socket.timeout:
             self.session = None
             self.window = None
@@ -632,16 +633,18 @@ class Marionette(object):
                 response = self._handle_emulator_cmd(response)
                 continue;
 
             if response.get("emulator_shell"):
                 response = self._handle_emulator_shell(response)
                 continue;
 
             break;
+        if not self.session_id:
+            self.session_id = response.get("sessionId", None)
 
         if response_key in response:
             return response[response_key]
         self._handle_error(response)
 
     def _handle_emulator_cmd(self, response):
         cmd = response.get("emulator_cmd")
         if not cmd or not self.emulator:
@@ -806,41 +809,42 @@ class Marionette(object):
     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.
         '''
         return "%s%s" % (self.baseurl, relative_url)
 
-    def start_session(self, desired_capabilities=None):
+    def start_session(self, desired_capabilities=None, session_id=None):
         """Create a new Marionette session.
 
         This method must be called before performing any other action.
 
         :params desired_capabilities: An optional dict of desired
             capabilities.  This is currently ignored.
 
         :returns: A dict of the capabilities offered."""
-        self.session = self._send_message('newSession', 'value', capabilities=desired_capabilities)
+        self.session = self._send_message('newSession', 'value', capabilities=desired_capabilities, session_id=session_id)
         self.b2g = 'b2g' in self.session
         return self.session
 
     @property
     def test_name(self):
         return self._test_name
 
     @test_name.setter
     def test_name(self, test_name):
         if self._send_message('setTestName', 'ok', value=test_name):
             self._test_name = test_name
 
     def delete_session(self):
         """Close the current session and disconnect from the server."""
         response = self._send_message('deleteSession', 'ok')
+        self.session_id = None
         self.session = None
         self.window = None
         self.client.close()
         return response
 
     @property
     def session_capabilities(self):
         '''
--- a/testing/marionette/client/marionette/tests/unit/test_session.py
+++ b/testing/marionette/client/marionette/tests/unit/test_session.py
@@ -24,9 +24,21 @@ class TestSession(marionette_test.Marion
 
         # Optional capabilities we want Marionette to support
         self.assertIn("device", caps)
         self.assertIn("handlesAlerts", caps)
         self.assertIn("rotatable", caps)
         self.assertIn("takesScreenshot", caps)
         self.assertIn("version", caps)
 
+    def test_we_can_get_the_session_id(self):
+        # Sends newSession
+        caps = self.marionette.start_session()
 
+        self.assertTrue(self.marionette.session_id is not None)
+        self.assertTrue(isinstance(self.marionette.session_id, unicode))
+
+    def test_we_can_set_the_session_id(self):
+        # Sends newSession
+        caps = self.marionette.start_session(session_id="ILoveCheese")
+
+        self.assertEqual(self.marionette.session_id, "ILoveCheese")
+        self.assertTrue(isinstance(self.marionette.session_id, unicode))
\ No newline at end of file
--- a/testing/marionette/marionette-server.js
+++ b/testing/marionette/marionette-server.js
@@ -122,16 +122,17 @@ function MarionetteServerConnection(aPre
   this.server = aServer;
   this.conn = aTransport;
   this.conn.hooks = this;
 
   // marionette uses a protocol based on the debugger server, which requires
   // passing back "actor ids" with responses. unlike the debugger server,
   // we don't have multiple actors, so just use a dummy value of "0" here
   this.actorID = "0";
+  this.sessionId = null;
 
   this.globalMessageManager = Cc["@mozilla.org/globalmessagemanager;1"]
                              .getService(Ci.nsIMessageBroadcaster);
   this.messageManager = this.globalMessageManager;
   this.browsers = {}; //holds list of BrowserObjs
   this.curBrowser = null; // points to current browser
   this.context = "content";
   this.scriptTimeout = null;
@@ -325,17 +326,19 @@ MarionetteServerConnection.prototype = {
    *        Value to send back to client
    * @param string command_id
    *        Unique identifier assigned to the client's request.
    *        Used to distinguish the asynchronous responses.
    */
   sendResponse: function MDA_sendResponse(value, command_id) {
     if (typeof(value) == 'undefined')
         value = null;
-    this.sendToClient({from:this.actorID, value: value}, command_id);
+    this.sendToClient({from:this.actorID,
+                       sessionId: this.sessionId,
+                       value: value}, command_id);
   },
 
   sayHello: function MDA_sayHello() {
     this.conn.send({ from: "root",
                      applicationType: "gecko",
                      traits: [] });
   },
 
@@ -548,16 +551,18 @@ MarionetteServerConnection.prototype = {
    */
   newSession: function MDA_newSession(aRequest) {
     logger.info("The newSession request is " + JSON.stringify(aRequest))
     this.command_id = this.getCommandId();
     this.newSessionCommandId = this.command_id;
 
     this.scriptTimeout = 10000;
     if (aRequest && aRequest.parameters) {
+      this.sessionId = aRequest.parameters.session_id ? aRequest.parameters.session_id : null;
+      logger.info("Session Id is set to: " + this.sessionId);
       this.setSessionCapabilities(aRequest.parameters.capabilities);
     }
 
     function waitForWindow() {
       let win = this.getCurrentWindow();
       if (!win) {
         // If the window isn't even created, just poll wait for it
         let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@@ -620,16 +625,20 @@ MarionetteServerConnection.prototype = {
    *
    * The return value is an immutable map of string keys
    * ("capabilities") to values, which may be of types boolean,
    * numerical or string.
    */
   getSessionCapabilities: function MDA_getSessionCapabilities() {
     this.command_id = this.getCommandId();
 
+    if (!this.sessionId) {
+      this.sessionId = this.uuidGen.generateUUID().toString();
+    }
+
     // eideticker (bug 965297) and mochitest (bug 965304)
     // compatibility.  They only check for the presence of this
     // property and should so not be in caps if not on a B2G device.
     if (appName == "B2G")
       this.sessionCapabilities.b2g = true;
 
     this.sendResponse(this.sessionCapabilities, this.command_id);
   },
@@ -2272,16 +2281,17 @@ MarionetteServerConnection.prototype = {
       this.curBrowser.frameManager.removeMessageManagerListeners(this.globalMessageManager);
     }
     this.switchToGlobalMessageManager();
     // reset frame to the top-most frame
     this.curFrame = null;
     if (this.mainFrame) {
       this.mainFrame.focus();
     }
+    this.sessionId = null;
     this.deleteFile('marionetteChromeScripts');
     this.deleteFile('marionetteContentScripts');
   },
 
   /**
    * Processes the 'deleteSession' request from the client by tearing down
    * the session and responding 'ok'.
    */