1#!/usr/bin/env python 2# Copyright (c) 2011 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import cStringIO 7import os 8import pickle 9import socket 10import sys 11 12import pyauto 13 14class RemoteHost(object): 15 """Class used as a host for tests that use the PyAuto RemoteProxy. 16 17 This class fires up a listener which waits for a connection from a RemoteProxy 18 and receives method call requests. Run python remote_host.py 19 remote_host.RemoteHost.RunHost to start up a PyAuto remote instance that you 20 can connect to and automate using pyauto.RemoteProxy. 21 """ 22 def __init__(self, host, *args, **kwargs): 23 self.StartSocketServer(host) 24 25 def StartSocketServer(self, host): 26 listening_socket = socket.socket() 27 listening_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 28 listening_socket.bind(host) 29 listening_socket.listen(1) 30 print 'Listening for incoming connections on port %d.' % host[1] 31 self._socket, address = listening_socket.accept() 32 print 'Accepted connection from %s:%d.' % address 33 34 while self.Connected(): 35 self._HandleRPC() 36 37 def StopSocketServer(self): 38 if self._socket: 39 try: 40 self._socket.shutdown(socket.SHUT_RDWR) 41 self._socket.close() 42 except socket.error: 43 pass 44 self._socket = None 45 46 def Connected(self): 47 return self._socket 48 49 def CreateTarget(self, target_class): 50 """Creates an instance of the specified class to serve as the RPC target. 51 52 RPC calls can be made on the target. 53 """ 54 self.target = target_class() 55 56 def _HandleRPC(self): 57 """Receives a method call request over the socket and executes the method. 58 59 This method captures stdout and stderr for the duration of the method call, 60 and sends those, the return value, and any thrown exceptions back to the 61 RemoteProxy. 62 """ 63 # Receive request. 64 request = self._socket.recv(4096) 65 if not request: 66 self.StopSocketServer() 67 return 68 request = pickle.loads(request) 69 70 # Redirect output to strings. 71 old_stdout = sys.stdout 72 old_stderr = sys.stderr 73 sys.stdout = stdout = cStringIO.StringIO() 74 sys.stderr = stderr = cStringIO.StringIO() 75 76 # Make requested method call. 77 result = None 78 exception = None 79 try: 80 if getattr(self, request[0], None): 81 result = getattr(self, request[0])(*request[1], **request[2]) 82 else: 83 result = getattr(self.target, request[0])(*request[1], **request[2]) 84 except BaseException, e: 85 exception = (e.__class__.__name__, str(e)) 86 87 # Put output back to the way it was before. 88 sys.stdout = old_stdout 89 sys.stderr = old_stderr 90 91 # Package up and send the result of the method call. 92 response = pickle.dumps((result, stdout.getvalue(), stderr.getvalue(), 93 exception)) 94 if self._socket.send(response) != len(response): 95 self.StopSocketServer() 96 97 98if __name__ == '__main__': 99 pyauto_suite = pyauto.PyUITestSuite(sys.argv) 100 RemoteHost(('', 7410)) 101 del pyauto_suite 102