diff --git a/testing/marionette/client/marionette/marionette.py b/testing/marionette/client/marionette/marionette.py index 51727a3ed429..602db2d9c61d 100644 --- a/testing/marionette/client/marionette/marionette.py +++ b/testing/marionette/client/marionette/marionette.py @@ -584,39 +584,39 @@ class Marionette(object): time.sleep(1) return False - def _send_message(self, command, response_key, **kwargs): - if not self.session and command not in ('newSession', 'getStatus'): - raise MarionetteException(message="Please start a session") + def _send_message(self, command, response_key="ok", **kwargs): + if not self.session and command not in ("newSession", "getStatus"): + raise MarionetteException("Please start a session") - message = { 'name': command } + message = {"name": command} if self.session: - message['sessionId'] = self.session + message["sessionId"] = self.session if kwargs: - message['parameters'] = kwargs + message["parameters"] = kwargs try: response = self.client.send(message) - except socket.timeout: + except socket.timeout as e: self.session = None self.window = None self.client.close() - raise TimeoutException(message='socket.timeout', status=ErrorCodes.TIMEOUT, stacktrace=None) + raise TimeoutException( + "Connection timed out", status=ErrorCodes.TIMEOUT) # Process any emulator commands that are sent from a script # while it's executing. while response.get("emulator_cmd"): response = self._handle_emulator_cmd(response) - if (response_key == 'ok' and response.get('ok') == True) or response_key in response: + if response_key in response: return response[response_key] - else: - self._handle_error(response) + self._handle_error(response) def _handle_emulator_cmd(self, response): cmd = response.get("emulator_cmd") if not cmd or not self.emulator: - raise MarionetteException(message="No emulator in this test to run " - "command against.") + raise MarionetteException( + "No emulator in this test to run command against") cmd = cmd.encode("ascii") result = self.emulator._run_telnet(cmd) return self.client.send({"name": "emulatorCmdResult", @@ -706,11 +706,17 @@ class Marionette(object): return self._send_message('getStatus', 'value') def start_session(self, desired_capabilities=None): - ''' - Creates a new Marionette session. + """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. + + """ - You must call this method before performing any other action. - ''' try: # We are ignoring desired_capabilities, at least for now. self.session = self._send_message('newSession', 'value') diff --git a/testing/marionette/client/marionette/tests/unit/test_session.py b/testing/marionette/client/marionette/tests/unit/test_session.py new file mode 100644 index 000000000000..d5f78782ded4 --- /dev/null +++ b/testing/marionette/client/marionette/tests/unit/test_session.py @@ -0,0 +1,32 @@ +# 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/. + +import marionette_test + +class TestSession(marionette_test.MarionetteTestCase): + def setUp(self): + super(TestSession, self).setUp() + self.marionette.delete_session() + + def test_new_session_returns_capabilities(self): + # Sends newSession + caps = self.marionette.start_session() + + # Check that session was created. This implies the server + # sent us the sessionId and status fields. + self.assertIsNotNone(self.marionette.session) + + # Required capabilities mandated by WebDriver spec + self.assertIn("browserName", caps) + self.assertIn("platformName", caps) + self.assertIn("platformVersion", caps) + + # Optional capabilities we want Marionette to support + self.assertIn("cssSelectorsEnabled", caps) + self.assertIn("device", caps) + self.assertIn("handlesAlerts", caps) + self.assertIn("javascriptEnabled", caps) + self.assertIn("rotatable", caps) + self.assertIn("takesScreenshot", caps) + self.assertIn("version", caps) diff --git a/testing/marionette/client/marionette/tests/unit/unit-tests.ini b/testing/marionette/client/marionette/tests/unit/unit-tests.ini index 045447a97dc0..9f43328a6108 100644 --- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini +++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini @@ -11,6 +11,8 @@ b2g = true ; true if the test should be skipped skip = false +[test_session.py] + [test_expectedfail.py] expected = fail [test_getstatus.py] diff --git a/testing/marionette/marionette-server.js b/testing/marionette/marionette-server.js index 8905044bee6e..8c1904ef4406 100644 --- a/testing/marionette/marionette-server.js +++ b/testing/marionette/marionette-server.js @@ -489,11 +489,14 @@ MarionetteServerConnection.prototype = { */ /** - * Create a new session. This creates a BrowserObj. + * Create a new session. This creates a new BrowserObj. * - * In a desktop environment, this opens a new 'about:blank' tab for - * the client to test in. + * In a desktop environment, this opens a new browser with + * "about:blank" which subsequent commands will be sent to. * + * This will send a hash map of supported capabilities to the client + * as part of the Marionette:register IPC command in the + * receiveMessage callback when a new browser is created. */ newSession: function MDA_newSession() { this.command_id = this.getCommandId(); @@ -507,25 +510,27 @@ MarionetteServerConnection.prototype = { if (!win || (appName == "Firefox" && !win.gBrowser) || (appName == "Fennec" && !win.BrowserApp)) { - checkTimer.initWithCallback(waitForWindow.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT); + checkTimer.initWithCallback(waitForWindow.bind(this), 100, + Ci.nsITimer.TYPE_ONE_SHOT); } else { this.startBrowser(win, true); } } - if (!Services.prefs.getBoolPref("marionette.contentListener")) { waitForWindow.call(this); } else if ((appName != "Firefox") && (this.curBrowser == null)) { - //if there is a content listener, then we just wake it up + // If there is a content listener, then we just wake it up this.addBrowser(this.getCurrentWindow()); - this.curBrowser.startSession(false, this.getCurrentWindow(), this.whenBrowserStarted); + this.curBrowser.startSession(false, this.getCurrentWindow(), + this.whenBrowserStarted); this.messageManager.broadcastAsyncMessage("Marionette:restart", {}); } else { - this.sendError("Session already running", 500, null, this.command_id); + this.sendError("Session already running", 500, null, + this.command_id); } this.switchToGlobalMessageManager(); }, @@ -536,23 +541,29 @@ MarionetteServerConnection.prototype = { let rotatable = appName == "B2G" ? true : false; let value = { - 'appBuildId' : Services.appinfo.appBuildID, - 'XULappId' : Services.appinfo.ID, - 'cssSelectorsEnabled': true, - 'browserName': appName, - 'handlesAlerts': false, - 'javascriptEnabled': true, - 'nativeEvents': false, - 'platformName': Services.appinfo.OS, - 'platformVersion': Services.appinfo.platformVersion, - 'secureSsl': false, - 'device': qemu == "1" ? "qemu" : (!device ? "desktop" : device), - 'rotatable': rotatable, - 'takesScreenshot': true, - 'takesElementScreenshot': true, - 'version': Services.appinfo.version + 'appBuildId' : Services.appinfo.appBuildID, + 'XULappId' : Services.appinfo.ID, + 'cssSelectorsEnabled': true, + 'browserName': appName, + 'handlesAlerts': false, + 'javascriptEnabled': true, + 'nativeEvents': false, + 'platform': Services.appinfo.OS, + 'platformName': Services.appinfo.OS, + 'platformVersion': Services.appinfo.platformVersion, + 'secureSsl': false, + 'device': qemu == "1" ? "qemu" : (!device ? "desktop" : device), + 'rotatable': rotatable, + 'takesScreenshot': true, + 'takesElementScreenshot': true, + 'version': Services.appinfo.version }; + // eideticker (bug 965297) and mochitest (bug 965304) + // compatibility + if (appName == "B2G") + value.b2g = true; + this.sendResponse(value, this.command_id); }, @@ -2387,7 +2398,7 @@ MarionetteServerConnection.prototype = { return; } if (this.curBrowser.newSession) { - this.sendResponse(reg.id, this.newSessionCommandId); + this.getSessionCapabilities(); this.newSessionCommandId = null; } }