예제 #1
0
파일: androidapp.py 프로젝트: wolf937/QT4A
    def __init__(self, process_name, device=None):
        if isinstance(device, (AndroidDevice, Device)):
            self._device = device
        elif isinstance(device, (str, unicode)):
            self._device = AndroidDevice(device)
        else:
            self._device = Device()

        if not self._device.is_app_installed(self.package_name):
            time.sleep(60)  # 这段时间手Q测试中会出现设备死机的问题,此时会报应用未安装的错误,通过sleep来减少失败用例
            raise RuntimeError('应用:%s 尚未安装' % self.package_name)

        self._drivers = {}  # 如果应用存在多个被测进程,就会有多个driver
        self._process_name = process_name  # 主程序名
        
        self._monitor_task_list = []
        self._monitor_run = True
        
        self._app_crashed = False
        self._device.wake_screen()  # 唤醒屏幕
        self._device.unlock_keyguard()  # 解屏幕锁
        self._close_other_window()
        self._main_thread_id = threading.current_thread().ident  # 创建该对象的线程一定是主线程
        self._monitor_thread = None
        self.start_monitor()
        self.add_monitor_task(self._detect_crash_window)
예제 #2
0
 def post_test(self):
     '''清理测试用例
     '''
     from qt4a.androiddriver.util import logger
     logger.info('post_test run')
     super(DemoTestBase, self).post_test()
     Device.release_all_device()  # 释放所有设备
     logger.info('postTest complete')
예제 #3
0
    def post_test(self):
        '''清理测试用例
        '''

        logger.info('开始执行测试清理。。。')
        super(GYTestBase, self).post_test()
        Device.release_all_device()  # 释放所有设备
        logger.info('测试清理执行完毕。')
예제 #4
0
    def _record_screen_thread(self, device):
        '''录屏线程
        '''
        from qt4a.androiddriver.devicedriver import qt4a_path
        record_time = 4 * 1000  # 每次录制的时间
        framerate = 8
        quality = 20
        remote_tmp_path_tmpl = '%s/screen.record.%%d' % qt4a_path
        max_record_file_count = 4  # 最大临时存储的录屏文件数目
        index = 0
        device.delete_file('%s/screen.record.*' % qt4a_path)

        while not hasattr(
                self,
                '_run_test_complete') or self._run_test_complete == False:
            # 尚未结束
            remote_tmp_path = remote_tmp_path_tmpl % (index %
                                                      max_record_file_count)
            device.run_shell_cmd(
                '%s/screenshot record -p %s -t %d -f %d -q %d' %
                (qt4a_path, remote_tmp_path, record_time, framerate, quality))
            index += 1
            if index >= max_record_file_count: index -= max_record_file_count

        if not self.test_result.passed:
            merge_file_list = []
            for i in range(max_record_file_count):
                merge_file_list.append(remote_tmp_path_tmpl %
                                       ((i + index) % max_record_file_count))
            device.run_shell_cmd(
                'cat %s > %s' % (' '.join(merge_file_list),
                                 remote_tmp_path_tmpl % max_record_file_count))
            local_tmp_path = tempfile.mktemp('.record')
            device.pull_file(remote_tmp_path_tmpl % max_record_file_count,
                             local_tmp_path)
            save_dir = tempfile.mkdtemp('.screenshot')
            frame_list = Device.extract_record_frame(local_tmp_path, save_dir)
            video_path = self.__class__.__name__ + '_' + get_valid_file_name(
                device.device_id) + '_' + str(int(time.time())) + '.mp4'
            result = Device.screen_frame_to_video(frame_list, framerate,
                                                  video_path)
            if result == None:
                qta_logger.warn('opencv not installed')
            else:
                self.test_result.info('最近15秒录屏',
                                      attachments={video_path: video_path})
            shutil.rmtree(save_dir)

        self._record_thread_status_dict[device.device_id] = True
예제 #5
0
 def _get_app(self):
     ADB.is_rooted = mock.Mock(return_value=False)
     ADB.run_shell_cmd = mock.Mock(side_effect=mock_run_shell_cmd)
     adb_backend = LocalADBBackend('127.0.0.1', 'test')
     adb = ADB(adb_backend)
     device = Device(adb)
     return AndroidDemoApp(device)
예제 #6
0
 def __init__(self, device=None):
     if not device:
         from testbase import context
         tc = context.current_testcase()
         if tc:
             device = tc.acquire_device()
         else:
             # 使用本地设备
             device = Device()
     super(QT4ABrowser, self).__init__(self.process_name, device)
예제 #7
0
def open_mp_demo(wxid, password):
    device = Device()
    wxacc = WeiXinAccount(wxid, password)
    wxapp = WeiXinApp(device)
    wxapp.login(wxacc)
    page = wxapp.open_mini_program('小程序示例', MiniProgramComponentPage)
    page.open_component_page('基础内容', 'text')
    time.sleep(2)

    page = MiniProgramTextPage(wxapp)
    page.add_line()
    page.add_line()
    page.add_line()
    page.add_line()
    print(page.control('文本区域').inner_text)
예제 #8
0
파일: androidapp.py 프로젝트: wolf937/QT4A
class AndroidApp(object):
    '''Android App基类
    '''
    package_name = ''

    def __init__(self, process_name, device=None):
        if isinstance(device, (AndroidDevice, Device)):
            self._device = device
        elif isinstance(device, (str, unicode)):
            self._device = AndroidDevice(device)
        else:
            self._device = Device()

        if not self._device.is_app_installed(self.package_name):
            time.sleep(60)  # 这段时间手Q测试中会出现设备死机的问题,此时会报应用未安装的错误,通过sleep来减少失败用例
            raise RuntimeError('应用:%s 尚未安装' % self.package_name)

        self._drivers = {}  # 如果应用存在多个被测进程,就会有多个driver
        self._process_name = process_name  # 主程序名
        
        self._monitor_task_list = []
        self._monitor_run = True
        
        self._app_crashed = False
        self._device.wake_screen()  # 唤醒屏幕
        self._device.unlock_keyguard()  # 解屏幕锁
        self._close_other_window()
        self._main_thread_id = threading.current_thread().ident  # 创建该对象的线程一定是主线程
        self._monitor_thread = None
        self.start_monitor()
        self.add_monitor_task(self._detect_crash_window)

    @property
    def device(self):
        '''返回所在的设备
        '''
        return self._device

    @property
    def process_name(self):
        '''应用所在的主进程名
        '''
        return self._process_name

    @process_name.setter
    def process_name(self, name):
        self._process_name = name

    @property
    def crashed(self):
        return self._app_crashed

    def get_driver(self, process_name=''):
        '''根据进程名获取driver对象,默认为主程序driver
        '''
        if not process_name:
            if not self._process_name: raise RuntimeError('主程序名为空')
            process_name = self._process_name
        if not self._drivers.has_key(process_name):
            # 只有尚未创建的时候才需要互斥
            # logger.debug('wait for %s create driver' % process_name)
            # 创建driver
            driver = AndroidDriver.create(process_name, self._device)
            self._drivers[process_name] = driver
            # logger.debug('%s create driver success' % process_name)
        driver = self._drivers[process_name]
        return driver
    
    def close_driver(self, process_name):
        '''关闭测试桩
        '''
        self._drivers[process_name].close()
        del self._drivers[process_name]

    def wait_for_activity(self, activity, timeout=5, interval=0.5):
        '''等待Activity打开
        
        :param activity: Activtiy名称
        :type activity:  string
        :param timeout:  超时时间,单位:S
        :type timeout:   int/float
        :param interval: 检查间隔时间,单位:S
        :type interval:  int/float
        '''
        time0 = time.time()
        while time.time() - time0 < timeout:
            if self.crashed:
                raise RuntimeError('%s Crashed' % self.__class__.__name__)
            current_activity = self.device.get_current_activity()
            # print 'current_activity', current_activity
            if current_activity == activity: return True
            time.sleep(interval)
        raise ControlNotFoundError('等待Activity:%s 超时,当前Activity: %s' % (activity, current_activity))

    def drag(self, direction='right', count=1):
        '''滑动屏幕,适合一大块区域(如引导屏)的滑动,无需关心具体的控件
        :param direction: 方向,right、left、top、bottom
        :type direction:  string
        :param count: 次数
        :type count:  int
        '''
        width, height = self.device.screen_size
        mid_width = width / 2
        mid_height = height / 2
        x1 = x2 = mid_width
        y1 = y2 = mid_height
        if direction == 'right':
            x1 -= 50
            x2 += 50
        elif direction == 'left':
            x1 += 50
            x2 -= 50
        elif direction == 'top':
            y1 += 50
            y2 -= 50
        elif direction == 'bottom':
            y1 -= 50
            y2 += 50
        else:
            raise RuntimeError('不支持的方向:%s' % direction)

        for _ in range(count):
            self.get_driver().drag(x1, y1, x2, y2)
            time.sleep(0.5)

    def shake(self, process_name='', duration=4, interval=0.05):
        '''模拟摇一摇功能
        
        :param process_name: 要模拟的进程名,默认为主进程
        :type process_name:  string
        :param duration:     持续时间,单位:秒
        :type duration:      int
        :param interval:     发送的时间间隔
        :type interval:      float
        '''
        return self.get_driver(process_name).shake(duration, interval)

    def send_back_key(self):
        '''发送返回按键
        '''
        self._device.send_key(4)
    
    def send_key(self, key):
        '''发送单个按键
        :param key: 发送的按键字符串
        :type key:  string
        '''
        self.get_driver().send_key(key)
        
    def send_home_key(self):
        '''发送Home键
        '''
        self._device.send_key(3)
        
    def send_menu_key(self):
        '''发送菜单键
        '''
        self.get_driver().send_key('{MENU}')

    def close(self):
        '''关闭所有打开的driver
        '''
        # self.remove_monitor_task(self._detect_crash_window)
        self.remove_all_task()
        self.stop_monitor()
        for key in self._drivers.keys():
            self._drivers[key].close()
        self._drivers = {}

    def _close_other_window(self):
        '''关闭会影响用例执行的窗口
        '''
        import re
        from systemui import CrashWindow, AppNoResponseWindow, AppResolverPanel, StatusBar
        current_activity = self.device.get_current_activity()
        logger.debug('current_activity: %s' % current_activity)
        pattern = re.compile(AppNoResponseWindow.Activity)
        if current_activity == StatusBar.Activity:
            self.send_back_key()
        elif current_activity == CrashWindow.Activity or pattern.match(current_activity):
            # 如果是Crash窗口
            self.device.send_key(66)
            timeout = 10
            time0 = time.time()
            while time.time() - time0 < timeout:  # 等待窗口消失
                current_activity = self.device.get_current_activity()
                if current_activity == CrashWindow.Activity or pattern.match(current_activity):
                    time.sleep(0.5)
                    self.device.send_key(66)
                else:
                    return
            self.device.send_key(4)
        elif current_activity == AppResolverPanel.Activity:
            self.device.send_key(4)
            
    def _handle_system_crash_window(self):
        '''处理系统Crash窗口
        '''
        from systemui import CrashWindow
        crash_window = CrashWindow.findCrashWindow(self)
        if not crash_window: return False
        logger.error('detect crash window')
        crash_window.close()
        return True

    def _detect_crash_window(self):
        '''检测Crash窗口
        '''
        from systemui import CrashWindow
        current_activity = self.device.get_current_activity()
        # print current_activity
        if current_activity == CrashWindow.Activity:
            # TODO:Crash时测试桩无法获取当前进程名
            # if self._handle_system_crash_window(): return True
            self.on_crashed()
            return True

    def on_crashed(self):
        '''发生Crash之后的处理
        '''
        logger.error('detect crash')
        self._app_crashed = True
        self._monitor_run = False

    def add_monitor_task(self, task, last_time=0):
        '''添加监控任务
        '''
        if last_time > 0: last_time += time.time()
        self._monitor_task_list.append((task, last_time))
        logger.debug('add task: %s' % task.__name__)
        
    def remove_monitor_task(self, task):
        '''移除监控任务
        '''
        for item in self._monitor_task_list:
            if item[0] == task:
                self._monitor_task_list.remove(item)
                logger.debug('remove task: %s' % task.__name__)
                return True
        return False

    def remove_all_task(self):
        '''移除所有任务
        '''
        self._monitor_task_list = []
    
    def start_monitor(self):
        '''启动监控
        '''
        if self._monitor_thread: return
        self._monitor_run = True
        t = ThreadEx(target=self.monitor_thread, name='QT4A App Monitor Thread')
        t.setDaemon(True)
        t.start()
        self._monitor_thread = t
        
    def stop_monitor(self):
        '''停止检测
        '''
        if not self._monitor_thread: return
        self._monitor_run = False
        if self._monitor_thread.is_alive():
            self._monitor_thread.join(30)
        self._monitor_thread = None
        
    def monitor_thread(self):
        '''监控线程
        '''
        interval = 1
        while self._monitor_run:
            for task, task_end_time in self._monitor_task_list:
                time_now = time.time()
                if task_end_time == 0 or (task_end_time > 0 and time_now < task_end_time):
                    try:
                        task()
                        time.sleep(0.1)
                    except:
                        logger.exception('run task %s failed' % task.__name__)
                        self._resume_main_thread()  # 防止出错后主线程阻塞一段时间
            time.sleep(interval)
        
    def get_string_resource(self, string_id, lang=''):
        '''获取字符串资源
        :param string_id: 字符串ID
        :type string_id:  string
        :param lang: 字符串语言,默认为当前系统语言
        :type lang:  string
        '''
        return self.device.get_string_resource(self.package_name, string_id, lang)
    
    def _suspend_main_thread(self, max_wait_time=30):
        '''阻塞主线程
        '''
        if self._main_thread_id == threading.current_thread().ident: return  # 避免卡死
        for key in self._drivers.keys():
            driver = self._drivers[key]
            driver.suspend_thread(self._main_thread_id, max_wait_time)
    
    def _resume_main_thread(self):
        '''解锁主线程
        '''
        if self._main_thread_id == threading.current_thread().ident: return
        for key in self._drivers.keys():
            driver = self._drivers[key]
            driver.resume_thread(self._main_thread_id)
    
    def is_debug(self):
        '''是否是debug包
        '''
        return self.device.is_debug_package(self.package_name)
    
    def _is_use_int_view_id(self):
        '''是否使用整型控件ID
        '''
        if hasattr(self, '_use_int_view_id'): return self._use_int_view_id
        if not self.is_debug():
            logger.debug('release version')
            _, file_list = self.device.list_dir('/data/local/tmp')
            for file in file_list:
                if file['name'] == '%s_map.txt' % self.package_name:
                    self._use_int_view_id = True
                    return True
        logger.debug('map file not exist')
        self._use_int_view_id = False
        return False
    
    def _get_view_id(self, str_id):
        '''从控件字符串ID获取整型ID
        '''
        if not str_id: raise RuntimeError('控件ID不能为空')
        try:
            return self.device._get_view_id(self.package_name, str_id)
        except RuntimeError, e:
            logger.warn(str(e))
            return None
예제 #9
0
 def clean_test(self):
     '''清理测试环境。慎用此函数,尽量将清理放到postTest里。
     '''
     super(AndroidTestBase, self).clean_test()
     Device.release_all_device()  # 释放所有设备