Ejemplo n.º 1
0
    def handle_params(self):
        '''
        加载并设置参数,用例中可重写加载方法方式; 并显示的调用以适配不支持参数传递的方式
        正常模式:不设置参数
        设置数据驱动参数:只设置数据驱动参数
        设置全部参数:如果传入参数,以传入参数为准,否则设置数据驱动参数
        '''
        # 通过参数控制是否进行参数传递
        param_mode = settings.get("QTAF_PARAM_MODE", False)
        if not param_mode:
            param_mode = settings.get("QC_PARAM_MODE", False)

        if not param_mode:  # 正常模式
            return

        self.add_params()  # 添加参数定义
        self.assert_types()  # 参数校验

        temp_data = self.params

        if param_mode == "DATADRIVE":
            if isinstance(self.casedata, dict):
                temp_data.update(self.casedata)
        elif param_mode == "PARAM" or param_mode is True:
            if isinstance(self.casedata, dict):
                temp_data.update(self.casedata)

        if not temp_data or not isinstance(temp_data, dict):
            return
        for key, value in temp_data.items():
            if not isinstance(key, six.string_types):
                continue
            setattr(self, str(key), value)
Ejemplo n.º 2
0
 def _rewrite_assert(self, cls):
     if not settings.get("QTAF_REWRITE_ASSERT", True) or cls == TestCase:
         return
     rewriter = AssertionRewriter()
     for key in dir(cls):
         item = getattr(cls, key)
         if isinstance(item, (types.MethodType, types.FunctionType)):
             rewriter.rewrite(item)
Ejemplo n.º 3
0
 def _log_assert_failed(self, message, back_count=2):
     """记录断言失败的信息
     """
     stack = get_last_frame_stack(back_count)
     msg = "检查点不通过\n%s%s\n" % (smart_text(stack), smart_text(message))
     self.__testresult.log_record(EnumLogLevel.ASSERT, msg)
     if not settings.get("QTAF_ASSERT_CONTINUE", True):
         raise RuntimeError("testcase assert failed:%s" % message)
Ejemplo n.º 4
0
    def _thread_run(self):
        '''测试用例线程过程
        '''
        #                     函数时发生了死锁,故注释掉。观察一段时间,看修改是否会影响测试。
        try:
            try:
                self._check_testcase(self._testcase)
            except RuntimeError as e:
                self._testresult.error(e.args[0])
                return

            while True:
                with self._lock:
                    if len(self._subtasks) == 0 or self._stop_run:
                        break
                    it = self._subtasks.popleft()

                if isinstance(it, str):
                    try:
                        if it in ['init_test', 'initTest']:
                            getattr(self._testcase, it)(self._testresult)
                        else:
                            task_result = getattr(self._testcase, it)()
                            if task_result and isinstance(
                                    task_result, six.string_types):
                                valid_task_result = False
                                for name in TestResultType.__dict__:
                                    value = TestResultType.__dict__[name]
                                    if task_result == value:
                                        valid_task_result = True
                                        break
                                if not valid_task_result:
                                    self._testresult.warning(
                                        '指定的自定义状态不支持,用例将继续执行')
                                else:
                                    self._testresult.customize_result(
                                        task_result)
                                    while self._subtasks[0] not in [
                                            'post_test', 'postTest'
                                    ]:
                                        self._subtasks.popleft()
                    except:
                        self._testresult.exception('%s执行失败' % it)
                        if settings.get(
                                "QTAF_FAILED_SKIP_RUNTEST",
                                False) and it in ['pre_test', 'preTest']:
                            while self._subtasks[0] not in [
                                    'post_test', 'postTest'
                            ]:
                                self._subtasks.popleft()
                else:
                    it()

        except:
            self._error = traceback.format_exc()
Ejemplo n.º 5
0
 def _get_fbsimctl_agent_cmd_for_simulator(self, xcode_version):
     agent_dir = self._build_agent_for_simulator(xcode_version)
     xctest_path = os.path.join(
         agent_dir, 'XCTestAgent-Runner.app/PlugIns/XCTestAgent.xctest')
     attached_app = settings.get('QT4I_SIM_ATTACHED_APP',
                                 'com.apple.reminders')
     return ' '.join([
         dt.DT().fbsimctl, self.udid, 'launch_xctest', xctest_path,
         attached_app,
         '--port %s -- listen' % self._server_port
     ])
Ejemplo n.º 6
0
    def __record_assert_failed(self, message, actual, expect):
        '''记录Assert失败信息

        :param message: 提示信息
        :type message: string
        :param actual: 实际值
        :type actual: string
        :param expect: 期望值
        :type expect: string
        '''
        # 得到上一个函数调用帧所在的文件路径,行号,函数名
        stack = get_last_frame_stack(3)
        msg = "检查点不通过\n%s%s\n期望值:%s%s\n实际值:%s%s" % (
            smart_text(stack), smart_text(message), expect.__class__, expect,
            actual.__class__, actual)
        self.__testresult.log_record(EnumLogLevel.ASSERT, msg)
        if not settings.get("QTAF_ASSERT_CONTINUE", True):
            raise RuntimeError("testcase assert failed:%s" % message)
Ejemplo n.º 7
0
    def __init__(self,
                 addr,
                 port,
                 udid=None,
                 agent_port=DEFAULT_AGENT_PORT,
                 driver_type=settings.get('IOS_DRIVER', 'xctest'),
                 endpoint_clss=''):
        '''初始化

        :param addr: 设备主机ip
        :type addr: str
        :param port: 设备主机端口号
        :type port: int
        :param udid: 设备UDID
        :type udid: str or None
        :param agent_port: agent端口号
        :type agent_port: int
        :param driver_type: driver类型,xctest or instruments
        :type driver_type: str
        :param endpoint_clss: 注册远程调用类集,多个以";"间隔
        :type endpoint_clss: str
        '''
        unzip_path = pkg_resources.resource_filename(
            "qt4i", "driver")  # @UndefinedVariable
        self._script = os.path.join(unzip_path, 'driverserver.py')
        self._addr = addr
        self._port = port
        self._udid = udid
        self._agent_port = agent_port
        self._driver_type = driver_type
        self._endpoint_clss = endpoint_clss
        self._python = "python3" if PY3 else "python"
        if self._udid:
            self.pidfile = '/tmp/driverserver_%s.pid' % self._udid
        else:
            self.pidfile = '/tmp/driverserver.pid'
        if hasattr(settings, 'QT4i_ENDPOINT_CLSS'):  # 兼容小写配置项
            self._endpoint_clss = ','.join(
                [self._endpoint_clss, settings.QT4i_ENDPOINT_CLSS])
        elif hasattr(settings, 'QT4I_ENDPOINT_CLSS'):
            self._endpoint_clss = ','.join(
                [self._endpoint_clss, settings.QT4I_ENDPOINT_CLSS])
Ejemplo n.º 8
0
class XCUITestAgent(object):
    '''XCUITest Agent
    '''
    _process = None  # Agent进程
    _relay_thread = None  # 端口映射线程
    _agent_cmd = ''
    CommandTimeout = settings.get('QT4I_START_APP_TIMEOUT', 40)
    PythonPath = Task("which python").execute()

    LOG_FILTERED_METHODS = [
        Command.SCREENSHOT,
        Command.QTA_STOP_AGENT,
        Command.GET_PAGE_SOURCE,
        Command.GET_ELEMENT_TREE,
        Command.QTA_ELEMENT_TREE,
    ]

    def __init__(self,
                 device_id,
                 server_ip=DEFAULT_IP,
                 server_port=DEFAULT_PORT,
                 keep_alive=False,
                 retry=3,
                 timeout=60):
        self.log_name = "xctest_%s" % device_id
        self.log = logger.get_logger(self.log_name)

        self.version = '0.0.0'
        version_file = os.path.join(os.path.expanduser('~'), 'XCTestAgent',
                                    'version.txt')
        if os.path.exists(version_file):
            with open(version_file, 'r') as f:
                self.version = f.read()

        # XCTestAgent工程默认路径:~/XCTestAent/XCTestAgent.xcodeproj
        self.XCTestAgentPath = os.path.join(os.path.expanduser('~'),
                                            "XCTestAgent",
                                            "XCTestAgent.xcodeproj")
        if not os.path.exists(self.XCTestAgentPath):
            msg = 'XCTestAgent does not exist. Please use command line: "python manage.py qt4i.setup" to setup XCTestAgent.xcodeproj'
            self.log.error(msg)
            raise Exception(msg)

        self.udid = device_id
        self._server_ip = server_ip
        self._server_port = server_port
        self._server_url = 'http://%s:%d' % (server_ip, server_port)
        self.type = EnumDevice.Simulator if dt.DT().is_simulator(
            device_id) else EnumDevice.Device
        self._is_remote = True
        self.session_id = None
        self.crash_flag = False
        self.capabilities = {}
        self.mjpeg_port = 9100 + self._server_port - 8100
        self.stub_remote_port = 18123
        self.stub_port = 18123 + self._server_port - 8100
        self.stub_server_url = 'http://%s:%d' % (DEFAULT_IP, self.stub_port)
        self.stub_client = RemoteConnection(self.stub_server_url,
                                            keep_alive=False,
                                            logger_name=self.log_name)
        self.stub_client.set_timeout(0.01)
        self.error_handler = ErrorHandler()
        self._command_executor = RemoteConnection(self._server_url,
                                                  keep_alive=keep_alive,
                                                  logger_name=self.log_name)
        self._is_relay_quit = threading.Event()
        self.start(retry, timeout)

    def _tcp_relay(self):
        '''将设备8100端口映射到本地8100端口,虚拟机不需要映射
        '''
        if self.type == EnumDevice.Simulator:
            return
        if not self._relay_thread and self._server_ip == DEFAULT_IP:
            self._relay_error = None
            self._relay_thread = threading.Thread(target=self._forward_ports)
            self._relay_thread.daemon = True
            self.log.info("Start TCPRelay Thread")
            self._relay_thread.start()
            time.sleep(1)
            if self._relay_error:
                raise Exception(self._relay_error)
            if not self._relay_thread.is_alive():
                raise RuntimeError('_relay_thread failed to start')

    def _forward_ports(self):
        '''iOS真机设备的端口转发
        '''
        try:
            agent_port_pair = '%d:%d' % (8100, self._server_port)
            stub_port_pair = '%d:%d' % (self.stub_remote_port, self.stub_port)
            mjpeg_port_pair = '%d:%d' % (9100, self.mjpeg_port)
            pair_ports = [
                agent_port_pair, stub_port_pair, mjpeg_port_pair
            ]  # 端口对的数组,每对端口中前一个代表远程端口,后一个代表本地端口,例如:["8100:8100", "8200:8200"]
            self._tcp_servers = []
            serverclass = ThreadedTCPServer
            for pair_port in pair_ports:
                rport, lport = pair_port.split(":")
                rport = int(rport)
                lport = int(lport)
                self.log.info("Forwarding local port %d to remote port %d" %
                              (lport, rport))
                server = serverclass(("localhost", lport), TCPRelay)
                server.rport = rport
                server.bufsize = 128
                server.udid = self.udid
                self._tcp_servers.append(server)

            self._quit_relay_thread = False  # 退出端口转发线程的标识位
            self._is_relay_quit.clear()
            while not self._quit_relay_thread:
                try:
                    rl, wl, xl = select.select(self._tcp_servers, [],
                                               [])  #@UnusedVariable
                    for server in rl:
                        server.handle_request()
                except:
                    self.log.exception('Forward ports')
                    self._quit_relay_thread = True
            self.log.info("Forward ports: successfully quit select")
            for server in self._tcp_servers:
                server.server_close()
            self._is_relay_quit.set()
            self.log.info("Forward ports: successfully close socket")

        except Exception:
            self._relay_error = traceback.format_exc()
            self.log.exception('forward ports')

    def _build_agent_for_simulator(self, xcode_version):
        agent_dir = os.path.join(AGENT_STORE_PATH, xcode_version)
        agent_file = os.path.join(agent_dir, 'XCTestAgent-Runner.app')
        if not os.path.exists(agent_file):
            if not os.path.exists(agent_dir):
                os.makedirs(agent_dir)
            xctest_root = pkg_resources.resource_filename(
                "qt4i", "driver/xctest/xctestproj")  #@UndefinedVariable
            xctestproj = os.path.join(xctest_root, 'XCTestAgent',
                                      'XCTestAgent.xcodeproj')
            build_for_test_cmd = ' '.join([
                'xcodebuild', 'build-for-testing', '-project',
                '"%s"' % xctestproj, '-scheme', 'XCTestAgent', '-destination',
                '"platform=%s,id=%s"' % (self.type, self.udid),
                'CONFIGURATION_BUILD_DIR=%s' % agent_dir
            ])
            subprocess.call(build_for_test_cmd, shell=True)
        return agent_dir

    def _get_xcodebuild_agent_cmd_for_simulator(self, xcode_version):
        template_root = pkg_resources.resource_filename(
            "qt4i", "driver/xctest/bin")  #@UndefinedVariable
        xctestfile_template = os.path.join(
            template_root, "iphonesimulator-%s.xctestrun" % xcode_version)
        agent_dir = self._build_agent_for_simulator(xcode_version)
        xctestfile = os.path.join(agent_dir, "%s.xctestrun" % self.udid)
        if not os.path.isfile(xctestfile):
            shutil.copyfile(xctestfile_template, xctestfile)
        cmd = "/usr/libexec/PlistBuddy -c 'Set :XCTestAgent:EnvironmentVariables:USE_PORT %s' %s" % (
            self._server_port, xctestfile)
        subprocess.call(cmd, shell=True)
        derived_data_path = '/tmp/xctlog/%s' % self.udid
        if not os.path.exists(derived_data_path):
            os.makedirs(derived_data_path)
        return ' '.join([
            'xcodebuild', 'test-without-building', '-xctestrun', xctestfile,
            '-destination',
            '"platform=%s,id=%s"' % (self.type, self.udid), '-derivedDataPath',
            derived_data_path
        ])

    def _get_fbsimctl_agent_cmd_for_simulator(self, xcode_version):
        agent_dir = self._build_agent_for_simulator(xcode_version)
        xctest_path = os.path.join(
            agent_dir, 'XCTestAgent-Runner.app/PlugIns/XCTestAgent.xctest')
        attached_app = settings.get('QT4I_SIM_ATTACHED_APP',
                                    'com.apple.reminders')
        return ' '.join([
            dt.DT().fbsimctl, self.udid, 'launch_xctest', xctest_path,
            attached_app,
            '--port %s -- listen' % self._server_port
        ])

    def _stdout_line_callback(self, line):
        '''标准输出回调函数
        '''
        self.log.debug(line.strip(' '))

    def _stderr_line_callback(self, line):
        '''标准错误回调函数
        '''
        self.log.error(line.strip(' '))

    def start(self, retry=1, timeout=CommandTimeout):
        '''启动Agent,类初始化即调用
        
        :param retry: 启动尝试次数
        :type retry: int
        :param timeout: 单次启动超时 (秒)
        :type timeout: int
        '''
        if self.type == EnumDevice.Simulator:
            dt.DT().reboot(self.udid)
            xcode_version = dt.DT.get_xcode_version().split(".")[0]
            if int(xcode_version) >= 9:
                self._agent_cmd = self._get_xcodebuild_agent_cmd_for_simulator(
                    xcode_version)
            else:
                self._agent_cmd = self._get_fbsimctl_agent_cmd_for_simulator(
                    xcode_version)
        else:
            self._agent_cmd = ' '.join([
                'xcodebuild',
                '-project %s' % self.XCTestAgentPath,
                '-scheme %s' % 'XCTestAgent',
                '-destination "platform=%s,id=%s"' % (self.type, self.udid),
                'test'
            ])
        # 清理遗留的xcodebuild进程
        print self._agent_cmd
        Process().kill_process_by_name(self._agent_cmd.replace('"', ''))
        Process().kill_process_by_port(self._server_port)
        start_time = time.time()
        # 启动端口转发线程
        self._tcp_relay()
        for _ in range(retry):
            self.log.info("Start XCTestAgent: %s" % self._agent_cmd)
            self.log.info("XCTestAgent Version: %s" % self.version)
            self._process = ThreadTask(
                command=self._agent_cmd,
                stdout_line_callback=self._stdout_line_callback,
                stderr_line_callback=self._stderr_line_callback)

            time0 = time.time()
            while time.time() - time0 < timeout:
                if self.is_working():
                    exec_time = (time.time() - start_time) * 1000
                    self.log.info('[ %s ] consumed [ %dms ]' %
                                  ('Start XCTestAgent', exec_time))
                    return
                time.sleep(3)

            _dt = dt.DT()
            self.log.info("Uninstall com.apple.test.XCTestAgent-Runner")
            result = _dt.uninstall("com.apple.test.XCTestAgent-Runner",
                                   self.udid)
            if not result:
                self.log.error(_dt.uninstall_error)

        self.stop()
        error_log_name = 'xctest_%s' % self.udid
        agent_error_log = logger.get_agent_error_log(error_log_name,
                                                     start_time)
        raise AgentStartError("Failed to start XCTestAgent.\nDetails:%s" %
                              agent_error_log)

    def stop(self, is_timeout=False):
        '''关闭Agent
        '''
        if not is_timeout:
            # 停止手机上Agent进程
            try:
                self._command_executor.set_timeout(2)
                self.execute(Command.QTA_STOP_AGENT)
            except:
                pass
        # 停止PC上端口转发线程
        if self._relay_thread:
            self._quit_relay_thread = True
            try:
                self._execute(Command.HEALTH)  # 发送请求,退出select阻塞
            except:
                pass
            self._is_relay_quit.wait()  # 等待端口转发线程退出
            self._relay_thread = None
        self._command_executor.reset_timeout()
        # 等待PC上xcodebuild进程停止
        time.sleep(10)
        self._process = None
        self.session_id = None
        self.log.info("Stop XCTestAgent successfully")

    def is_working(self):
        '''检查当前Agent是否在工作,如果Agent无法退到后台,重启手机
        
        :returns: boolean
        '''
        try:
            response = self._execute(Command.HEALTH)
            return response['value'] == 'XCTestAgent is ready'

        except XCTestAgentDeadException:
            self.log.exception('XCTestAgentDead')
            # Agent无法退到后台,重启手机
            try:
                self.log.info('Reboot device %s' % self.udid)
                dt.DT().reboot(self.udid)
                for _ in xrange(20):
                    device = dt.DT().get_device_by_udid(self.udid)
                    if device is not None:
                        break
                else:
                    raise RuntimeError("after reboot, device disconnected!")
            except Exception:
                self.log.exception('device reboot error')

        except XCTestAgentTimeoutException:
            if self._relay_error:
                self.log.error("TcpRelay error:%s" % self._relay_error)
            self.stop(True)

        return False

    def has_session(self):
        '''检查是否存在会话
        
        :returns: boolean
        '''
        return self.session_id is not None

    def start_session(self, desired_capabilities, timeout=CommandTimeout):
        '''创建新会话
        
        :param desired_capabilities: 
         - bundleId - APP的bundle_id,例如:com.tencent.qq.dailybuild.test
         - app - APP路径,用于运行前安装APP.
        :type desired_capabilities: dict
        :param timeout: 检测启动app超时时间,启动超时则重启agent,单位:秒
        :type timeout: int
        :returns: 返回结果,dict
        '''
        while not self.is_working():
            self.start()

        while 1:
            time0 = time.time()
            response = self._execute(
                Command.SESSION, {
                    'desiredCapabilities': desired_capabilities,
                })
            exec_time = int((time.time() - time0) * 1000)  # 单位:毫秒
            if exec_time < (timeout * 1000):
                break
            self.log.error('Restart XCTestAgent: [ %s ] consumed [ %dms ]' %
                           ('start app', exec_time))
            self.stop()
            self.start()

        self.session_id = response['sessionId']
        self.capabilities = response['value']['capabilities']
        return response

    def _execute(self, command, params={}):
        '''执行指令
        '''
        response = self._command_executor.execute(command, params)

        if response:
            if not command in self.LOG_FILTERED_METHODS:
                self.log.debug(response)
            # 检查返回结果
            try:
                self.error_handler.check_response(response)
            except ApplicationCrashedException:
                self.crash_flag = True
                raise
            return response
        # 如果服务器没有发送响应,认为指令执行成功
        return {'success': 0, 'value': None, 'sessionId': self.session_id}

    def capture_screen(self):
        try:
            return self.stub_client.execute(Command.SCREENSHOT, {})
        except XCTestAgentTimeoutException:
            return None

    def execute(self, command, params={}, timeout=CommandTimeout):
        '''执行指令

        :param command: 指令名称
        :type command: str
        :param params: 指令参数集
        :type params: dict 例如 {bundleId: com.tencent.qq.dailybuild.test}
        :param timeout: 指令执行超时等待时常
        :type timeout: int
        :returns: 返回结果,dict
        '''
        time0 = time.time()

        if not self.has_session():
            response = self._execute(Command.STATUS, {})
            self.session_id = response['sessionId']

        if self.has_session():
            if not params:
                params = {'sessionId': self.session_id}
            elif 'sessionId' not in params:
                params['sessionId'] = self.session_id

        if command == Command.START:
            if params is None:
                raise WebDriverException("Desired Capabilities can't be None")
            if not isinstance(params, dict):
                raise WebDriverException(
                    "Desired Capabilities must be a dictionary")
            response = self.start_session(params, timeout)

        elif command == Command.QUIT:
            if params.has_key('sessionId'):
                response = self._execute(command, params)
            else:
                response = {}
            self.session_id = None

        elif command == Command.STATUS:
            response = self._execute(command, params)
            if self.session_id is None:
                self.session_id = response['sessionId']
        else:
            response = self._execute(command, params)

        exec_time = int((time.time() - time0) * 1000)
        self.log.info('[ %s ] consumed [ %dms ]' % (command, exec_time))
        return response

    def get_crash_flag(self):
        '''获取app是否crash的标识
        
        :returns : boolean -- True if app crashed, False if not
        '''
        return self.crash_flag

    def get_driver_log(self):
        '''获取driver日志路径
        '''
        driver_log_path = None
        for h in self.log.handlers:
            attr_name = 'baseFilename'
            if hasattr(h, attr_name):
                driver_log_path = getattr(h, attr_name)
                break
        return driver_log_path
Ejemplo n.º 9
0
 def test_get(self):
     '''get settings
     '''
     self.assertEqual(settings.DEBUG, False)
     self.assertEqual(settings.get('DEBUG'), False)
     self.assertEqual(settings.get('NOT_EXIST', False), False)
Ejemplo n.º 10
0
from qt4i.driver.tools import logger
from qt4i.driver.xctest.webdriverclient.exceptions import NoSuchElementException
from qt4i.driver.xctest.webdriverclient.exceptions import XCTestAgentTimeoutException
from qt4i.driver.util.uimap import UIA_XCT_MAPS
from qt4i.driver.xctest.webdriverclient.errorhandler import ErrorCode
from qt4i.driver.util.modalmap import DeviceProperty
from testbase.conf import settings
from tuia.qpathparser import QPathParser
from pymobiledevice import lockdown


DEFAULT_IP = '127.0.0.1'
DEFAULT_PORT = 8100
TMP_LOAD_FILE = '/tmp/tmpFile'

TMP_DIR_PATH = settings.get('QT4I_TMP_DIR_PATH', '/tmp')

def convert_to_qpath(element):
    '''转换成qpath
    '''
    if 'type' in element:
        element['classname'] = element['type']
        element.pop('type')
    if 'isEnabled' in element:
        element['enabled'] = (element['isEnabled'] == '1')
        element.pop('isEnabled')
    if 'isVisible' in element:
        element['visible'] = (element['isVisible'] == '1')
        element.pop('isVisible')
    if 'rect' in element:
        element['rect']= {
Ejemplo n.º 11
0
    def __init__(self, attrs={}, devicemanager=None):
        '''Device构造函数

        :param attrs: 设备UDID字符串|设备属性字典
                                    keys: udid            - 设备UDID
                                          host            - 设备主机ip
                                          is_simulator    - 是否为模拟器
        :type attrs: str|dict
        :param devicemanager: 设备管理类
        :type devicemanager: DeviceManager
        '''
        cond = {}
        if isinstance(attrs, six.string_types):
            cond['udid'] = attrs
        elif isinstance(attrs, dict):
            cond = attrs.copy()
        else:
            raise Exception('Device attributes type error: %s' % type(attrs))
        ct = None
        if devicemanager:
            self._device_resource = devicemanager.acquire_device(cond)
        else:
            ct = context.current_testcase()
            if ct is None:
                self._test_resources = TestResourceManager(
                    LocalResourceManagerBackend()).create_session()
            else:
                self._test_resources = ct.test_resources
            self._device_resource = self._test_resources.acquire_resource(
                "ios", condition=cond)

        if self._device_resource is None:
            raise Exception('无可用的真机和模拟器: %s' % str(cond))

        props = self._device_resource
        if 'properties' in self._device_resource:  # online mode
            devprops = self._device_resource['properties']
            props = {
                p['name'].encode(Encoding): p['value'].encode(Encoding)
                for p in devprops
            }
            props['id'] = self._device_resource['id']

        if 'csst_uri' not in props or props['csst_uri'] == 'None':
            props['csst_uri'] = None

        self._device_resource = DeviceResource(props['host'], int(
            props['port']), props['udid'], props['is_simulator'],
                                               props['name'], props['version'],
                                               props['csst_uri'], props['id'])

        self._base_url = self._device_resource.driver_url
        self._ws_uri = self._device_resource.ws_uri
        self._host = RPCClientProxy('/'.join([self._base_url, 'host/']),
                                    self._ws_uri,
                                    allow_none=True,
                                    encoding=Encoding)
        self._device_udid = self._device_resource.udid
        self._device_name = self._device_resource.name
        self._device_ios = self._device_resource.version
        if isinstance(self._device_resource.is_simulator, bool):
            self._device_simulator = self._device_resource.is_simulator
        else:
            self._device_simulator = self._device_resource.is_simulator == str(
                True)

        if self._device_simulator:
            self._host.start_simulator(self._device_udid)
        self._app_started = False
        Device.Devices.append(self)
        url = '/'.join([self._base_url, 'device', '%s/' % self._device_udid])
        self._driver = RPCClientProxy(url,
                                      self._ws_uri,
                                      allow_none=True,
                                      encoding=Encoding)
        self._keyboard = Keyboard(self)
        logger.info('[%s] Device - Connect - %s - %s (%s)' %
                    (datetime.datetime.fromtimestamp(
                        time.time()), self.name, self.udid, self.ios_version))
        # 申请设备成功后,对弹窗进行处理
        rule = settings.get('QT4I_ALERT_DISMISS', DEFAULT_ALERT_RULE)
        if rule:
            try:
                self._dismiss_alert(rule)
            except:
                logger.exception('dismiss alert %s' % rule)
Ejemplo n.º 12
0
from testbase import logger
from qt4i.util import EnumDirect, Rectangle

from qt4i.driver.rpc import RPCClientProxy
from qt4i.driver.util import Process

QT4i_LOGS_PATH = os.path.abspath('_attachments')
if not os.path.exists(QT4i_LOGS_PATH):
    os.makedirs(QT4i_LOGS_PATH)

Encoding = 'UTF-8'
DEFAULT_ADDR = '127.0.0.1'
DEFAULT_PORT = 12306
DEFAULT_AGENT_PORT = 8100
DEFAULT_ALERT_RULE = settings.get(
    'QT4I_ALERT_RULES', [{
        'button_text': '^确定$|^好$|^允许$|^OK$|^Allow$'
    }])


class DeviceServer(object):
    '''设备服务器
    '''
    def __init__(self,
                 addr,
                 port,
                 udid=None,
                 agent_port=DEFAULT_AGENT_PORT,
                 driver_type=settings.get('IOS_DRIVER', 'xctest'),
                 endpoint_clss=''):
        '''初始化
Ejemplo n.º 13
0
import time
import traceback
import uuid
import xmlrpclib
import hashlib

from testbase.conf import settings
from testbase.util import LazyInit
from qt4i.exceptions import ControlAmbiguousError
from qt4i.exceptions import ControlNotFoundError
from qt4i.app import App
from qt4i.device import QT4i_LOGS_PATH
from qt4i.qpath import QPath
from qt4i.util import Rectangle, EnumDirect, Timeout

INS_IOS_DRIVER = True if settings.get('IOS_DRIVER',
                                      'xctest') == 'instruments' else False
QTA_AI_SWITCH = settings.get('QT4I_AI_SWITCH', False)


class ControlContainer(object):
    '''控件集合接口
    '''
    def __init__(self):
        self._locators = {}  # 对象定义

    def __getitem__(self, key):
        '''操作符"[]"重载
        
        :param key: 控件名
        :type key: str
        :rtype: object
Ejemplo n.º 14
0
# OF ANY KIND, either express or implied. See the License for the specific language
# governing permissions and limitations under the License.
#
'''logging日志模块的封装
'''

import os
import logging
import datetime
import time
import re

from logging.handlers import BaseRotatingHandler
from testbase.conf import settings

TMP_DIR_PATH = settings.get('QT4I_TMP_DIR_PATH', '/tmp')
LOG_MAX_BTYES = 1024 * 1024 * 10  #单份日志最大容量(byte)
LOG_BACKUP_COUNT = settings.get('QT4I_LOG_BACKUP_COUNT',
                                10)  #setting获取count时默认值
LOG_DELAY_DAY = 2  #日志保留天数0.0015625
TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
USER_KEY_WORDS = settings.get('QT4I_AGENT_ERROR_KEY_WORDS', [])
DEFAULT_KEY_WORDS = [
    'requires a development team', 'invalid code signature', 'expired',
    'Connection peer refused channel request'
]


class RotatingFileHandler(BaseRotatingHandler):
    '''文件滚动日志
    '''
Ejemplo n.º 15
0
class RemoteConnection(object):
    """A connection with the Remote WebDriver server.

    Communicates with the server using the WebDriver wire protocol:
    https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol"""

    _default_timeout = settings.get('QT4I_XCTAGENT_CMD_TIMEOUT', 90)
    if _default_timeout == -1:
        _default_timeout = socket._GLOBAL_DEFAULT_TIMEOUT
    _timeout = _default_timeout
#     _timeout = socket._GLOBAL_DEFAULT_TIMEOUT
    

    @classmethod
    def get_timeout(cls):
        """
        
        :returns: Timeout value in seconds for all http requests made to the Remote Connection
        """
        return None if cls._timeout == socket._GLOBAL_DEFAULT_TIMEOUT else cls._timeout

    @classmethod
    def set_global_timeout(cls, timeout):
        """Override the default timeout
 
        :param timeout: timeout value for http requests in seconds
        :type timeout: int
        """
        cls._timeout = timeout
        
    @classmethod
    def reset_global_timeout(cls):
        """Reset the http request timeout to socket._GLOBAL_DEFAULT_TIMEOUT
        """
#         cls._timeout = socket._GLOBAL_DEFAULT_TIMEOUT
        cls._timeout = cls._default_timeout
    
    def set_timeout(self, timeout):
        self._timeout = timeout
        
    def reset_timeout(self):
        self._timeout = self._default_timeout

    def __init__(self, remote_server_addr, keep_alive=False, resolve_ip=True, logger_name='RemoteConnection'):
        # Attempt to resolve the hostname and get an IP address.
        self.logger = logging.get_logger(logger_name)
        self.keep_alive = keep_alive
        parsed_url = parse.urlparse(remote_server_addr)
        addr = ""
        if parsed_url.hostname and resolve_ip:
            try:
                netloc = socket.gethostbyname(parsed_url.hostname)
                addr = netloc
                if parsed_url.port:
                    netloc += ':%d' % parsed_url.port
                if parsed_url.username:
                    auth = parsed_url.username
                    if parsed_url.password:
                        auth += ':%s' % parsed_url.password
                    netloc = '%s@%s' % (auth, netloc)
                remote_server_addr = parse.urlunparse(
                    (parsed_url.scheme, netloc, parsed_url.path,
                     parsed_url.params, parsed_url.query, parsed_url.fragment))
            except socket.gaierror:
                self.logger.info('Could not get IP address for host: %s' % parsed_url.hostname)

        self._url = remote_server_addr
        if keep_alive:
            self._conn = httplib.HTTPConnection(
                str(addr), str(parsed_url.port), timeout=self._timeout)

        self._commands = {
            Command.HEALTH: ('GET', '/health'),
            Command.DEACTIVATE_APP: ('POST', '/session/$sessionId/wda/deactivateApp'),
            Command.STATUS: ('GET', '/status'),
            Command.SESSION: ('POST', '/session'),
            Command.GET_ALL_SESSIONS: ('GET', '/sessions'),
            Command.QUIT: ('DELETE', '/session/$sessionId'),
            Command.GET: ('POST', '/session/$sessionId/url'),
            Command.EXECUTE_SCRIPT: ('POST', '/session/$sessionId/execute'),
            Command.GET_CURRENT_URL: ('GET', '/session/$sessionId/url'),
            Command.GET_TITLE: ('GET', '/session/$sessionId/title'),
            Command.GET_PAGE_SOURCE: ('GET', '/session/$sessionId/source'),
            Command.SCREENSHOT: ('GET', '/screenshot'),
            Command.ELEMENT_SCREENSHOT: ('GET', '/session/$sessionId/screenshot/$id'),
            Command.FIND_ELEMENT: ('POST', '/session/$sessionId/element'),
            Command.FIND_ELEMENTS: ('POST', '/session/$sessionId/elements'),
            Command.GET_ACTIVE_ELEMENT:
                ('POST', '/session/$sessionId/element/active'),
            Command.FIND_CHILD_ELEMENT:
                ('POST', '/session/$sessionId/element/$id/element'),
            Command.FIND_CHILD_ELEMENTS:
                ('POST', '/session/$sessionId/element/$id/elements'),
            Command.CLICK_ELEMENT: ('POST', '/session/$sessionId/element/$id/click'),
            Command.CLEAR_ELEMENT: ('POST', '/session/$sessionId/element/$id/clear'),
            Command.SUBMIT_ELEMENT: ('POST', '/session/$sessionId/element/$id/submit'),
            Command.GET_ELEMENT_TREE: ('GET', '/session/$sessionId/tree'),
            Command.GET_ELEMENT_TEXT: ('GET', '/session/$sessionId/element/$id/text'),
            Command.SEND_KEYS_TO_ELEMENT:
                ('POST', '/session/$sessionId/element/$id/value'),
            Command.SEND_KEYS_TO_ACTIVE_ELEMENT:
                ('POST', '/session/$sessionId/keys'),
            Command.GET_ELEMENT_VALUE:
                ('GET', '/session/$sessionId/element/$id/value'),
            Command.GET_ELEMENT_TAG_NAME:
                ('GET', '/session/$sessionId/element/$id/name'),
            Command.IS_ELEMENT_SELECTED:
                ('GET', '/session/$sessionId/element/$id/selected'),
            Command.SET_ELEMENT_SELECTED:
                ('POST', '/session/$sessionId/element/$id/selected'),
            Command.IS_ELEMENT_ENABLED:
                ('GET', '/session/$sessionId/element/$id/enabled'),
            Command.IS_ELEMENT_DISPLAYED:
                ('GET', '/session/$sessionId/element/$id/displayed'),
            Command.GET_ELEMENT_LOCATION:
                ('GET', '/session/$sessionId/element/$id/location'),
            Command.GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW:
                ('GET', '/session/$sessionId/element/$id/location_in_view'),
            Command.GET_ELEMENT_SIZE:
                ('GET', '/session/$sessionId/element/$id/size'),
            Command.GET_ELEMENT_RECT:
                ('GET', '/session/$sessionId/element/$id/rect'),
            Command.GET_ELEMENT_ATTRIBUTE:
                ('GET', '/session/$sessionId/element/$id/attribute/$name'),
            Command.ELEMENT_EQUALS:
                ('GET', '/session/$sessionId/element/$id/equals/$other'),
            Command.SWITCH_TO_FRAME: ('POST', '/session/$sessionId/frame'),
            Command.SWITCH_TO_PARENT_FRAME: ('POST', '/session/$sessionId/frame/parent'),
            Command.SWITCH_TO_WINDOW: ('POST', '/session/$sessionId/window'),
            Command.CLOSE: ('DELETE', '/session/$sessionId/window'),
            Command.IMPLICIT_WAIT:
                ('POST', '/session/$sessionId/timeouts/implicit_wait'),
            Command.EXECUTE_ASYNC_SCRIPT: ('POST', '/session/$sessionId/execute_async'),
            Command.SET_SCRIPT_TIMEOUT:
                ('POST', '/session/$sessionId/timeouts/async_script'),
            Command.SET_TIMEOUTS:
                ('POST', '/session/$sessionId/timeouts'),
            Command.DISMISS_ALERT:
                ('POST', '/session/$sessionId/dismiss_alert'),
            Command.ACCEPT_ALERT:
                ('POST', '/session/$sessionId/accept_alert'),
            Command.SET_ALERT_VALUE:
                ('POST', '/session/$sessionId/alert_text'),
            Command.GET_ALERT_TEXT:
                ('GET', '/session/$sessionId/alert_text'),
            Command.SET_ALERT_CREDENTIALS:
                ('POST', '/session/$sessionId/alert/credentials'),
            Command.CLICK:
                ('POST', '/session/$sessionId/click'),
            Command.DOUBLE_CLICK:
                ('POST', '/session/$sessionId/doubleclick'),
            Command.MOUSE_DOWN:
                ('POST', '/session/$sessionId/buttondown'),
            Command.MOUSE_UP:
                ('POST', '/session/$sessionId/buttonup'),
            Command.MOVE_TO:
                ('POST', '/session/$sessionId/moveto'),
            Command.GET_WINDOW_SIZE:
                ('GET', '/session/$sessionId/window/size'),
            Command.SET_WINDOW_SIZE:
                ('POST', '/session/$sessionId/window/$windowHandle/size'),
            Command.GET_WINDOW_POSITION:
                ('GET', '/session/$sessionId/window/$windowHandle/position'),
            Command.SET_WINDOW_POSITION:
                ('POST', '/session/$sessionId/window/$windowHandle/position'),
            Command.MAXIMIZE_WINDOW:
                ('POST', '/session/$sessionId/window/$windowHandle/maximize'),
            Command.SET_SCREEN_ORIENTATION:
                ('POST', '/session/$sessionId/orientation'),
            Command.GET_SCREEN_ORIENTATION:
                ('GET', '/session/$sessionId/orientation'),
            Command.SINGLE_TAP:
                ('POST', '/session/$sessionId/touch/click'),
            Command.TOUCH_DOWN:
                ('POST', '/session/$sessionId/touch/down'),
            Command.TOUCH_UP:
                ('POST', '/session/$sessionId/touch/up'),
            Command.TOUCH_MOVE:
                ('POST', '/session/$sessionId/touch/move'),
            Command.TOUCH_SCROLL:
                ('POST', '/session/$sessionId/touch/scroll'),
            Command.DOUBLE_TAP:
                ('POST', '/session/$sessionId/touch/doubleclick'),
            Command.LONG_PRESS:
                ('POST', '/session/$sessionId/touch/longclick'),
            Command.FLICK:
                ('POST', '/session/$sessionId/touch/flick'),
            Command.EXECUTE_SQL:
                ('POST', '/session/$sessionId/execute_sql'),
            Command.GET_LOCATION:
                ('GET', '/session/$sessionId/location'),
            Command.SET_LOCATION:
                ('POST', '/session/$sessionId/location'),
            Command.GET_APP_CACHE:
                ('GET', '/session/$sessionId/application_cache'),
            Command.GET_APP_CACHE_STATUS:
                ('GET', '/session/$sessionId/application_cache/status'),
            Command.CLEAR_APP_CACHE:
                ('DELETE', '/session/$sessionId/application_cache/clear'),
            Command.GET_NETWORK_CONNECTION:
                ('GET', '/session/$sessionId/network_connection'),
            Command.SET_NETWORK_CONNECTION:
                ('POST', '/session/$sessionId/network_connection'),
            Command.GET_LOCAL_STORAGE_ITEM:
                ('GET', '/session/$sessionId/local_storage/key/$key'),
            Command.REMOVE_LOCAL_STORAGE_ITEM:
                ('DELETE', '/session/$sessionId/local_storage/key/$key'),
            Command.GET_LOCAL_STORAGE_KEYS:
                ('GET', '/session/$sessionId/local_storage'),
            Command.SET_LOCAL_STORAGE_ITEM:
                ('POST', '/session/$sessionId/local_storage'),
            Command.CLEAR_LOCAL_STORAGE:
                ('DELETE', '/session/$sessionId/local_storage'),
            Command.GET_LOCAL_STORAGE_SIZE:
                ('GET', '/session/$sessionId/local_storage/size'),
            Command.GET_SESSION_STORAGE_ITEM:
                ('GET', '/session/$sessionId/session_storage/key/$key'),
            Command.REMOVE_SESSION_STORAGE_ITEM:
                ('DELETE', '/session/$sessionId/session_storage/key/$key'),
            Command.GET_SESSION_STORAGE_KEYS:
                ('GET', '/session/$sessionId/session_storage'),
            Command.SET_SESSION_STORAGE_ITEM:
                ('POST', '/session/$sessionId/session_storage'),
            Command.CLEAR_SESSION_STORAGE:
                ('DELETE', '/session/$sessionId/session_storage'),
            Command.GET_SESSION_STORAGE_SIZE:
                ('GET', '/session/$sessionId/session_storage/size'),
            Command.CURRENT_CONTEXT_HANDLE:
                ('GET', '/session/$sessionId/context'),
            Command.CONTEXT_HANDLES:
                ('GET', '/session/$sessionId/contexts'),
            Command.SWITCH_TO_CONTEXT:
                ('POST', '/session/$sessionId/context'),
            Command.QTA_FIND_ELEMENTS:
                ('POST', '/session/$sessionId/qta/element/$id/elements'),
            Command.QTA_DEVICE_CLICK:
                ('POST', '/session/$sessionId/qta/click'),
            Command.QTA_ELEMENT_CLICK:
                ('POST', '/session/$sessionId/qta/element/$id/click'),
            Command.QTA_DEVICE_DOUBLE_CLICK:
                ('POST', '/session/$sessionId/qta/doubleclick'),
            Command.QTA_ELEMENT_DOUBLE_CLICK:
                ('POST', '/session/$sessionId/wda/element/$id/doubleTap'),
            Command.QTA_DEVICE_LONG_CLICK:
                ('POST', '/session/$sessionId/qta/longclick'),
            Command.QTA_ELEMENT_LONG_CLICK:
                ('POST', '/session/$sessionId/wda/element/$id/touchAndHold'),
            Command.QTA_DEVICE_SENDKEYS:
                ('POST', '/session/$sessionId/qta/sendkeys'),
            Command.QTA_ELEMENT_SENDKEYS:
                ('POST', '/session/$sessionId/qta/element/$id/sendkeys'),
            Command.QTA_DEVICE_DRAG:
                ('POST', '/session/$sessionId/qta/drag'),
            Command.QTA_ELEMENT_DRAG:
                ('POST', '/session/$sessionId/qta/element/$id/drag'),
            Command.QTA_GET_PARENT_ELEMENT:
                ('POST', '/session/$sessionId/qta/element/$id/parent'),
            Command.QTA_GET_CHILDREN_ELEMENTS:
                ('POST', '/session/$sessionId/qta/element/$id/children'),
            Command.QTA_SCROLL_TO_VISIBLE:
                ('POST', '/session/$sessionId/qta/element/$id/scroll'),
            Command.QTA_DRAG_TO_VALUE:
                ('POST', '/session/$sessionId/qta/element/$id/slider'),
            Command.QTA_GET_ELEMENT_ATTRS:
                ('POST', '/session/$sessionId/qta/element/$id/attrs'),
            Command.QTA_ALERT_RULES_UPDATE:
                ('POST', '/qta/alertrules/update'),
            Command.QTA_ELEMENT_SETVALUE:
                ('POST', '/session/$sessionId/qta/element/$id/value'),
            Command.QTA_ELEMENT_TREE:
                ('POST', '/session/$sessionId/qta/element/$id/tree'),
            Command.QTA_STOP_AGENT:
                ('POST', '/qta/stop'),
            Command.QTA_GET_FOREGROUND_APP_NAME:
                ('GET', '/qta/appName'),
            Command.QTA_GET_FOREGROUND_APP_PID:
                ('GET', '/qta/appPid'),
            Command.QTA_GET_ELEMENT_WIN_INFO:
                ('GET', '/session/$sessionId/qta/element/$id/wininfo'),
            Command.QTA_ALERT_DISMISS:
                ('POST', '/qta/alert/dismiss'),
            Command.QTA_DEVICE_LOCK:
                ('POST', '/qta/device/lock'),    
            Command.QTA_DEVICE_UNLOCK:
                ('POST', '/qta/device/unlock'),  
            Command.QTA_SANDBOX_LIST:
                ('POST', '/qta/sandbox/list'),
            Command.QTA_SANDBOX_REMOVE:
                ('POST', '/qta/sandbox/remove'),
            Command.QTA_ALBUM_UPLOAD:
                ('POST', '/qta/album/upload'),
            Command.QTA_STUB_CALL:
                ('POST', '/qta/stub'),
            Command.QTA_DEVICE_VOLUME:
                ('POST', '/qta/device/volume'),     
            Command.QTA_DEVICE_SIRI:
                ('POST', '/qta/device/siri'),       
            Command.QTA_DEVICE_SCREEN_DIRECTION:
                ('POST', '/qta/device/screenDirection'), 
            Command.QTA_DEVICE_DETAIL_INFO:
                ('POST', '/qta/device/detailInfo'),
            Command.QTA_WHEEL_SELECT:
                ('POST', '/session/$sessionId/qta/element/$id/wheel/select'),
        }

    def execute(self, command, params):
        """Send a command to the remote server.
        Any path subtitutions required for the URL mapped to the command should be
        included in the command parameters.

        :param command: A string specifying the command to execute.
        :type command: str
        :param params: A dictionary of named parameters to send with the command as its JSON payload.
        :type params: dict
        :returns: dict 
        """
        command_info = self._commands[command]
        assert command_info is not None, 'Unrecognised command %s' % command
        data = json.dumps(params)
        path = string.Template(command_info[1]).substitute(params)
        url = '%s%s' % (self._url, path)
        return self._request(command_info[0], url, body=data)

    def _request(self, method, url, body=None):
        """Send an HTTP request to the remote server.

        :param method: A string for the HTTP method to send the request with.
        :type method: str
        :param url: A string for the URL to send the request to.
        :type url: str
        :param body: A string for request body. Ignored unless method is POST or PUT.
        :type body: str
        :returns: A dictionary with the server's parsed JSON response.
        """
        self.logger.debug('%s %s %s' % (method, url, body))
        
        for _ in range(3):
            try:
                parsed_url = parse.urlparse(url)
        
                if self.keep_alive:
                    headers = {"Connection": 'keep-alive', method: parsed_url.path,
                               "User-Agent": "Python http auth",
                               "Content-type": "application/json;charset=\"UTF-8\"",
                               "Accept": "application/json"}
                    if parsed_url.username:
                        auth = base64.standard_b64encode(('%s:%s' %
                               (parsed_url.username, parsed_url.password)).encode('ascii')).decode('ascii').replace('\n', '')
                        headers["Authorization"] = "Basic %s" % auth
                    if body and method != 'POST' and method != 'PUT':
                        body = None
                    try:
                        self._conn.request(method, parsed_url.path, body, headers)
                        resp = self._conn.getresponse()
                    except (httplib.HTTPException, socket.error):
                        self._conn.close()
                        raise
        
                    statuscode = resp.status
                else:
                    password_manager = None
                    if parsed_url.username:
                        netloc = parsed_url.hostname
                        if parsed_url.port:
                            netloc += ":%s" % parsed_url.port
                        cleaned_url = parse.urlunparse((parsed_url.scheme,
                                                           netloc,
                                                           parsed_url.path,
                                                           parsed_url.params,
                                                           parsed_url.query,
                                                           parsed_url.fragment))
                        password_manager = url_request.HTTPPasswordMgrWithDefaultRealm()
                        password_manager.add_password(None,
                                                      "%s://%s" % (parsed_url.scheme, netloc),
                                                      parsed_url.username,
                                                      parsed_url.password)
                        request = Request(cleaned_url, data=body.encode('utf-8'), method=method)
                    else:
                        request = Request(url, data=body.encode('utf-8'), method=method)
        
                    request.add_header('Accept', 'application/json')
                    request.add_header('Content-Type', 'application/json;charset=UTF-8')
        
                    if password_manager:
                        opener = url_request.build_opener(url_request.HTTPRedirectHandler(),
                                                          HttpErrorHandler(),
                                                          url_request.HTTPBasicAuthHandler(password_manager),
                                                          url_request.ProxyHandler({}))
                    else:
                        opener = url_request.build_opener(url_request.HTTPRedirectHandler(),
                                                          HttpErrorHandler(),
                                                          url_request.ProxyHandler({}))
                    resp = opener.open(request, timeout=self._timeout)
                    statuscode = resp.code
                    if not hasattr(resp, 'getheader'):
                        if hasattr(resp.headers, 'getheader'):
                            resp.getheader = lambda x: resp.headers.getheader(x)
                        elif hasattr(resp.headers, 'get'):
                            resp.getheader = lambda x: resp.headers.get(x)
        
                data = resp.read()
                try:
                    if 300 <= statuscode < 304:
                        return self._request('GET', resp.getheader('location'))
                    body = data.decode('utf-8').replace('\x00', '').strip()
                    if 399 < statuscode < 500:
                        return {'status': statuscode, 'value': body}
                    content_type = []
                    if resp.getheader('Content-Type') is not None:
                        content_type = resp.getheader('Content-Type').split(';')
                    if not any([x.startswith('image/png') for x in content_type]):
                        try:
                            data = json.loads(body.strip())
                        except ValueError:
                            if 199 < statuscode < 300:
                                status = ErrorCode.SUCCESS
                            else:
                                status = ErrorCode.UNKNOWN_ERROR
                            return {'status': status, 'value': body.strip()}
        
                        assert type(data) is dict, (
                            'Invalid server response body: %s' % body)
                        # Some of the drivers incorrectly return a response
                        # with no 'value' field when they should return null.
                        if 'value' not in data:
                            data['value'] = None
                        return data
                    else:
                        data = {'status': 0, 'value': body.strip()}
                        return data
                finally:
                    self.logger.debug("Finished Request")
                    resp.close()
                
            except socket.timeout:
                self.logger.exception('Remote Connection timeout')
                raise XCTestAgentTimeoutException('XCTestAgent response is timed out')
            except Exception, e:
                self.logger.error('Remote Connection:%s' % str(e))