Bug 1230151 - Skip out of sync messages in Marionette client. r=automatedtester, a=test-only
authorAndreas Tolfsen <ato@mozilla.com>
Tue, 02 Aug 2016 15:04:44 +0100
changeset 347803 a38dbd94a596b8b7a987b1b134b0826639dd7311
parent 347802 3233a69a1ce81f612863338a1f8c598982e7df06
child 347804 a71ff2ff3271356643448c19e41b84acbd53f0e4
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersautomatedtester, test-only
bugs1230151, 1211489, 1207125
milestone50.0a2
Bug 1230151 - Skip out of sync messages in Marionette client. r=automatedtester, a=test-only The Marionette remote server sequences messages following bug 1211489. In the client we can make advantage of this by skipping out-of-sync messages. This helps avoid issues such as bug 1207125, where a Python process interrupt causes the receive() for the current command to be aborted, and a new command to be issued to retrieve some debug information. When the new command's receive() is called, it reads the response from the previous command. To get around this problem we must read through the data until we reach the response we are expecting. Only at that point should we return from receive() and give the user the expected response. MozReview-Commit-ID: 3qF9Xo3pb5W
testing/marionette/client/marionette_driver/transport.py
--- a/testing/marionette/client/marionette_driver/transport.py
+++ b/testing/marionette/client/marionette_driver/transport.py
@@ -24,16 +24,19 @@ class SocketTimeout(object):
 
 class Message(object):
     def __init__(self, msgid):
         self.id = msgid
 
     def __eq__(self, other):
         return self.id == other.id
 
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
 
 class Command(Message):
     TYPE = 0
 
     def __init__(self, msgid, name, params):
         Message.__init__(self, msgid)
         self.name = name
         self.params = params
@@ -123,17 +126,17 @@ class TcpTransport(object):
         """
         self.addr = addr
         self.port = port
         self.socket_timeout = socket_timeout
 
         self.protocol = 1
         self.application_type = None
         self.last_id = 0
-        self.expected_responses = []
+        self.expected_response = None
 
         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         self.sock.settimeout(self.socket_timeout)
 
     def _unmarshal(self, packet):
         msg = None
 
         # protocol 3 and above
@@ -178,23 +181,26 @@ class TcpTransport(object):
                 length = data[0:sep]
                 remaining = data[sep + 1:]
 
                 if len(remaining) == int(length):
                     if unmarshal:
                         msg = self._unmarshal(remaining)
                         self.last_id = msg.id
 
-                        if isinstance(msg, Response) and self.protocol >= 3:
-                            if msg not in self.expected_responses:
-                                raise Exception("Received unexpected response: %s" % msg)
-                            else:
-                                self.expected_responses.remove(msg)
+                        if self.protocol >= 3:
+                            self.last_id = msg.id
+
+                            # keep reading incoming responses until
+                            # we receive the user's expected response
+                            if isinstance(msg, Response) and msg != self.expected_response:
+                                return self.receive(unmarshal)
 
                         return msg
+
                     else:
                         return remaining
 
                 bytes_to_recv = int(length) - len(remaining)
 
         raise socket.timeout("Connection timed out after %ds" % self.socket_timeout)
 
     def connect(self):
@@ -225,17 +231,18 @@ class TcpTransport(object):
         """Send message to the remote server.  Allowed input is a
         ``Message`` instance or a JSON serialisable object.
         """
         if not self.sock:
             self.connect()
 
         if isinstance(obj, Message):
             data = obj.to_msg()
-            self.expected_responses.append(obj)
+            if isinstance(obj, Command):
+                self.expected_response = obj
         else:
             data = json.dumps(obj)
         payload = "%s:%s" % (len(data), data)
 
         totalsent = 0
         while totalsent < len(payload):
             sent = self.sock.send(payload[totalsent:])
             if sent == 0: