зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1194968: Support for Marionette protocol adjustments in Python client
Whilst it is not a goal for the Marionette protocol to be fully compatible with WebDriver due to the different transport mechanisms, bug 1153822 adjusts the Marionette protocol closer to what is prescribed in the WebDriver specification. This patch adds support for the new protocol (level or version 2) to the Python bindings with backwards compatibility for the earlier, existing protocol (1). r=jgriffin --HG-- extra : commitid : CiAKTE3h1nG extra : rebase_source : ea3ed4ec021febac7f3b2ae2c10ec583a3ddfcff
This commit is contained in:
Родитель
2211b5dea1
Коммит
9351a4ab23
|
@ -2,11 +2,22 @@
|
|||
# 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 itertools
|
||||
|
||||
from marionette_driver import errors
|
||||
from marionette import marionette_test
|
||||
from marionette.marionette_test import MarionetteTestCase as TC
|
||||
|
||||
|
||||
class TestHandleError(marionette_test.MarionetteTestCase):
|
||||
class TestProtocol1Errors(TC):
|
||||
def setUp(self):
|
||||
TC.setUp(self)
|
||||
self.op = self.marionette.protocol
|
||||
self.marionette.protocol = 1
|
||||
|
||||
def tearDown(self):
|
||||
self.marionette.protocol = self.op
|
||||
TC.tearDown(self)
|
||||
|
||||
def test_malformed_packet(self):
|
||||
for t in [{}, {"error": None}]:
|
||||
with self.assertRaisesRegexp(errors.MarionetteException, "Malformed packet"):
|
||||
|
@ -29,3 +40,51 @@ class TestHandleError(marionette_test.MarionetteTestCase):
|
|||
def test_unknown_error_status(self):
|
||||
with self.assertRaises(errors.MarionetteException):
|
||||
self.marionette._handle_error({"error": {"status": "barbera"}})
|
||||
|
||||
|
||||
class TestProtocol2Errors(TC):
|
||||
def setUp(self):
|
||||
TC.setUp(self)
|
||||
self.op = self.marionette.protocol
|
||||
self.marionette.protocol = 2
|
||||
|
||||
def tearDown(self):
|
||||
self.marionette.protocol = self.op
|
||||
TC.tearDown(self)
|
||||
|
||||
def test_malformed_packet(self):
|
||||
req = ["error", "message", "stacktrace"]
|
||||
ps = []
|
||||
for p in [p for i in range(0, len(req) + 1) for p in itertools.permutations(req, i)]:
|
||||
ps.append(dict((x, None) for x in p))
|
||||
|
||||
for p in filter(lambda p: len(p) < 3, ps):
|
||||
self.assertRaises(KeyError, self.marionette._handle_error, p)
|
||||
|
||||
def test_known_error_code(self):
|
||||
with self.assertRaises(errors.NoSuchElementException):
|
||||
self.marionette._handle_error(
|
||||
{"error": errors.NoSuchElementException.code[0],
|
||||
"message": None,
|
||||
"stacktrace": None})
|
||||
|
||||
def test_known_error_status(self):
|
||||
with self.assertRaises(errors.NoSuchElementException):
|
||||
self.marionette._handle_error(
|
||||
{"error": errors.NoSuchElementException.status,
|
||||
"message": None,
|
||||
"stacktrace": None})
|
||||
|
||||
def test_unknown_error_code(self):
|
||||
with self.assertRaises(errors.MarionetteException):
|
||||
self.marionette._handle_error(
|
||||
{"error": 123456,
|
||||
"message": None,
|
||||
"stacktrace": None})
|
||||
|
||||
def test_unknown_error_status(self):
|
||||
with self.assertRaises(errors.MarionetteException):
|
||||
self.marionette._handle_error(
|
||||
{"error": "barbera",
|
||||
"message": None,
|
||||
"stacktrace": None})
|
||||
|
|
|
@ -15,7 +15,7 @@ class TestTearDownContext(MarionetteTestCase):
|
|||
MarionetteTestCase.tearDown(self)
|
||||
|
||||
def get_context(self):
|
||||
return self.marionette._send_message('getContext', 'value')
|
||||
return self.marionette._send_message("getContext", key="value")
|
||||
|
||||
def test_skipped_teardown_ok(self):
|
||||
raise SkipTest("This should leave our teardown method in chrome context")
|
||||
|
|
|
@ -14,13 +14,13 @@ class TestSetContext(MarionetteTestCase):
|
|||
self.chrome = self.marionette.CONTEXT_CHROME
|
||||
self.content = self.marionette.CONTEXT_CONTENT
|
||||
|
||||
test_url = self.marionette.absolute_url('empty.html')
|
||||
test_url = self.marionette.absolute_url("empty.html")
|
||||
self.marionette.navigate(test_url)
|
||||
self.marionette.set_context(self.content)
|
||||
self.assertEquals(self.get_context(), self.content)
|
||||
|
||||
def get_context(self):
|
||||
return self.marionette._send_message('getContext', 'value')
|
||||
return self.marionette._send_message("getContext", key="value")
|
||||
|
||||
def test_set_different_context_using_with_block(self):
|
||||
with self.marionette.using_context(self.chrome):
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
"""
|
||||
Copyright 2011 Software Freedom Conservancy.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
# Copyright 2015 Mozilla Foundation
|
||||
# Copyright 2011 Software Freedom Conservancy.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
class ApplicationCache(object):
|
||||
|
||||
UNCACHED = 0
|
||||
IDLE = 1
|
||||
CHECKING = 2
|
||||
|
@ -28,4 +26,4 @@ class ApplicationCache(object):
|
|||
|
||||
@property
|
||||
def status(self):
|
||||
return self.driver._send_message('getAppCacheStatus', 'value')
|
||||
return self.driver._send_message("getAppCacheStatus", key="value")
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -10,11 +10,11 @@ import time
|
|||
|
||||
|
||||
class MarionetteTransport(object):
|
||||
""" The Marionette socket client. This speaks the same protocol
|
||||
as the remote debugger inside Gecko, in which messages are
|
||||
always preceded by the message length and a colon, e.g.,
|
||||
"""The Marionette socket client. This speaks the same protocol
|
||||
as the remote debugger inside Gecko, in which messages are always
|
||||
preceded by the message length and a colon, e.g.:
|
||||
|
||||
20:{'command': 'test'}
|
||||
20:{"command": "test"}
|
||||
"""
|
||||
|
||||
max_packet_length = 4096
|
||||
|
@ -25,26 +25,25 @@ class MarionetteTransport(object):
|
|||
self.port = port
|
||||
self.socket_timeout = socket_timeout
|
||||
self.sock = None
|
||||
self.traits = None
|
||||
self.applicationType = None
|
||||
self.actor = 'root'
|
||||
self.protocol = 1
|
||||
self.application_type = None
|
||||
|
||||
def _recv_n_bytes(self, n):
|
||||
""" Convenience method for receiving exactly n bytes from
|
||||
self.sock (assuming it's open and connected).
|
||||
"""Convenience method for receiving exactly n bytes from self.sock
|
||||
(assuming it's open and connected).
|
||||
"""
|
||||
data = ''
|
||||
data = ""
|
||||
while len(data) < n:
|
||||
chunk = self.sock.recv(n - len(data))
|
||||
if chunk == '':
|
||||
if chunk == "":
|
||||
break
|
||||
data += chunk
|
||||
return data
|
||||
|
||||
def receive(self):
|
||||
""" Receive the next complete response from the server, and return
|
||||
it as a dict. Each response from the server is prepended by
|
||||
len(message) + ':'.
|
||||
"""Receive the next complete response from the server, and
|
||||
return it as a JSON structure. Each response from the server
|
||||
is prepended by len(message) + ":".
|
||||
"""
|
||||
assert(self.sock)
|
||||
now = time.time()
|
||||
|
@ -69,8 +68,10 @@ class MarionetteTransport(object):
|
|||
raise socket.timeout('connection timed out after %d s' % self.socket_timeout)
|
||||
|
||||
def connect(self):
|
||||
""" Connect to the server and process the hello message we expect
|
||||
to receive in response.
|
||||
"""Connect to the server and process the hello message we expect
|
||||
to receive in response.
|
||||
|
||||
Return a tuple of the protocol level and the application type.
|
||||
"""
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.settimeout(self.socket_timeout)
|
||||
|
@ -82,27 +83,22 @@ class MarionetteTransport(object):
|
|||
self.sock = None
|
||||
raise
|
||||
self.sock.settimeout(2.0)
|
||||
|
||||
hello = self.receive()
|
||||
self.traits = hello.get('traits')
|
||||
self.applicationType = hello.get('applicationType')
|
||||
self.protocol = hello.get("marionetteProtocol", 1)
|
||||
self.application_type = hello.get("applicationType")
|
||||
|
||||
# get the marionette actor id
|
||||
response = self.send({'to': 'root', 'name': 'getMarionetteID'})
|
||||
self.actor = response['id']
|
||||
return (self.protocol, self.application_type)
|
||||
|
||||
def send(self, msg):
|
||||
""" Send a message on the socket, prepending it with len(msg) + ':'.
|
||||
"""
|
||||
def send(self, data):
|
||||
"""Send a message on the socket, prepending it with len(msg) + ":"."""
|
||||
if not self.sock:
|
||||
self.connect()
|
||||
if 'to' not in msg:
|
||||
msg['to'] = self.actor
|
||||
data = json.dumps(msg)
|
||||
data = '%s:%s' % (len(data), data)
|
||||
data = "%s:%s" % (len(data), data)
|
||||
|
||||
for packet in [data[i:i + self.max_packet_length] for i in
|
||||
range(0, len(data), self.max_packet_length)]:
|
||||
try:
|
||||
try:
|
||||
self.sock.send(packet)
|
||||
except IOError as e:
|
||||
if e.errno == errno.EPIPE:
|
||||
|
@ -110,12 +106,10 @@ class MarionetteTransport(object):
|
|||
else:
|
||||
raise e
|
||||
|
||||
response = self.receive()
|
||||
return response
|
||||
return self.receive()
|
||||
|
||||
def close(self):
|
||||
""" Close the socket.
|
||||
"""
|
||||
"""Close the socket."""
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
|
|
Загрузка…
Ссылка в новой задаче