游戏测试 自建 GAutomator 的框架 使用解读 (一)

陈子昂 · 2019年10月01日 · 3908 次阅读

十一节有时间,看完阅兵后正好写文章。

使用前注意

属于腾讯开源平台,安卓的话不低于 API18,python 版本支持 2.7,3.4+ 以上版本,3.7 不确定是否支持,3.6 可用。
支持引擎:unity,虚幻 4
支持不是魔改版本的 NGUI 和 UGUI。如果不是这 2 类 Ui 插件的,可以在 GAutomatorView,就是类似 appium Inspector,用于定位 UInode 元素和提供查询。
GAutomatorView 和 github 上 clone 的项目不要放在中文目录下。
同样也是嵌入式的,游戏界面修改频繁,但是 gameObject 和 gameResource 变动频率并不高,使用嵌入式和 ORC 配合,可以在场景选择方面更灵活,自动化代码稳定性更高。

使用描述

GA 是属于打包了 python 类库进去,属于一个工程项目,毕竟一些公司因为内网权限问题,从外网导入 python 类库源码编译安装并不方便。GA 的理念就是 clone 项目后,在 MD 文件内就图片和 tables 说明了项目结构。
PS:如果在这个基础上进行二次开发和增加前置和后处理的,需要去自己治理本地文件目录和新扩展的怎么放,一些函数形式的 API 功能文件进行导入覆盖重写,这个是后话。
上述说明地址
sample 是各接口使用例子。
wpyscripts 里面是 GA 的核心逻辑,链接内图片会红线说明,和它平级的有一个文件 manager.py

manager.py

跳转 manager.py
主要是单例模式的实例化和获取 devices 和 engine
如果自研框架需要做前置开发,让链接更稳定和异常判断出现问题进行重新实例化获取 devices 和 engine 就需要依赖这个文件

from wpyscripts.wetest.device import *  #line:18行 导入后对获取devices
from wpyscripts.wetest.engine import * #engine 和 UnityEngine没直接关系 导入后对获取engine 
import wpyscripts.common.adb_process as adb #adb工具类
#os.environ.get(xxxx)和环境变量和本地配置挂钩.
#需要修改可以存到字典 manage_config ={"env": os.environ.get("PLATFORM_IP"),"hostip ":"省略"}
env = os.environ.get("PLATFORM_IP") #主要用来分岔行为,env 如果不为None,则是在云平台上使用。
hostip = os.environ.get("PLATFORM_IP", "127.0.0.1")
platform_port = os.environ.get("PLATFORM_PORT")
#line 67
if env:
        get_device.instance = CloudDevice(serial, pkgname, launch_activity, ui_device) 
    else:
        get_device.instance = NativeDevice(serial, ui_device)
#CloudDevice 暂时不考虑,看导入的区域,根据名字推断应该在 line18行 
#NativeDevice 我们看看本地创建的

python 函数都是有返回的,没有返回就是 None,所以在做一些判断条件时,不用和其他语言一样还需要特别添加布尔。

device.py/NativeDevice

跳转 device.py

#adb模式
get_device.instance = NativeDevice(serial, ui_device) # manager.py line70行
get_device.instance = None  #一个全局这句话很巧妙,也可以用到__init__.py里面
#serial和常规一样是获取adb.这个可以特殊编译过打包在项目工程内
#line 77
def _platform_forward(remote_port): #这个函数其实可以根据上面的env会返回什么来写个装饰器,如果本地运行调错了就会抛出信息
    """
        在wetest平台运行时,forward映射的端口交由平台分配并且实现映射
    :param remote_port:
    :return:
    """
    platform_client = platform.get_platform_client()
    response = platform_client.platform_forward(remote_port)
    return response["localPort"]

def get_engine(engine_type=EngineType, port=None)#line 88 获取引擎对象单例
#这个函数内,有env分岔
if env:
        result = _platform_forward(int(unity_sdk_port))
        local_port = result
    else:
        local_engine_port = os.environ.get("LOCAL_ENGINE_PORT", "53001")  # 本地模式时与engine forward的端口号
        res = forward(local_engine_port, unity_sdk_port)
        logger.info(res)
        local_port = int(local_engine_port)

#引擎名称分岔
 ui_device = uiauto.get_uiautomator() 
print("打印当前引擎信息:{}".format(ui_device))
    if engine_type == "unity":
        get_engine.instance = UnityEngine(hostip, local_port,ui_device)
    elif engine_type == "ue4":
        get_engine.instance=UnRealEngine(hostip,local_port,ui_device)
    else:
        raise ValueError("No {0} engine type".format(engine_type))

serial 要单独讲下,如果里面不止一套的双框架模式(GA/airtest,GA/Ui2),这个 adb 环境变量地址有可能会和其他框架冲突,所以可以在连接时把 adb 地址打印出来。
开始检查 跳转 uiautomator_manager.py
核心是 from libs.uiauto.uiautomator import AutomatorDevice

def get_reporter():   #line 118 行 属于wetest云平台,如果在其他函数内有使用到get_reporter 也说明和wetest云平台有关。
def save_sdk_version(version):  #这个也是属于wetest云平台的。
    """
        收集现有版本使用情况,可以针对性的去除你不想被我们收集的数据。
    :param version: SDK版本
    :return:
    """

如果自己自建了 openstf 和收集数据可以解析上面那段代码。

wetest/device.py

跳转 device.py

class DisplaySize(object): #line 35 屏幕显示,GA可以对屏幕自适应,看看这段信息
    """
        Mobile Screen resolution
    Attributes:
        width: width px
        height:height px
    """
#常规用法 长宽的px和类似一个toString()的__str__

class TopActivity(object): #line 58  应该只是一个获取Activity信息的实体属性类
#省略代码
def exception_call_super(fn):
    @wraps(fn)
    def wrap_function(*args, **kwargs):
        try:
            return fn(*args, **kwargs)
        except (WeTestRuntimeError, ConnectionException):
            logger.warning("call cloud function {0} error".format(fn.__name__))
            return getattr(super(CloudDevice, args[0]), fn.__name__)(*args[1:], **kwargs)
#CloudDevice看到有这个,而且在except 区域内说明是非本地的

class Device(object): #line 96 基础base
@retry_if_fail()
    def get_rotation(self):#屏幕旋转 这个暂时不知道什么作用,因为游戏sdk可以设置是否反转和方向。

@retry_if_fail()
    def get_display_size(self): #line 130 获取当前屏幕显示分辨率

@retry_if_fail()
    def get_top_package_activity(self): #line 157  获取活动的窗体,注释using可以作为日常使用。

while line:
                    match = pattern.search(line) #这里加打印 match 
                    if match:
                        top_activity = TopActivity(match.group(2), match.group(1)) #match.group(0)是全部,按顺序看。
                        return top_activity
                    line = result.readline()
            return TopActivity("", self.ui_device.info["currentPackageName"])
#剩余的都是些功能函数

Minitouch.py

跳转 Minitouch.py

import six #做2和3python兼容
import atexit #这个很重要 按堆栈顺序来按顺序解除注册服务。

Mini 连接器的类上面有一块的全局变量区域
里面有端口映射等,下面是 minitouch 工具类,因为主要是讲自建,先一笔带过,需要对文件夹和文件进行治理,就需要对工程内的最小单位到类和变量区域分别代表什么,进行了解。
下个文章研究 wpyscripts/wetest 文件夹下面其他文件。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 2 条回复 时间 点赞

🎁 👑 国庆节快乐。

匿名 #2 · 2020年04月11日

GA 也可以支持 3.7 以上 Python

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册