Skip to content

Commit

Permalink
GAutomator 2.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
LegendKan committed Jan 16, 2017
1 parent 8127b3b commit 63f0ad9
Show file tree
Hide file tree
Showing 13 changed files with 870 additions and 1,207 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# GAutomator
[![Wetest](https://img.shields.io/badge/wetest-2.2.0-brightgreen.svg)](wetest.qq.com) [![license](https://img.shields.io/badge/license-mit-red.svg)](https://github.com/Tencent/tinker/blob/master/LICENSE)
[![Wetest](https://img.shields.io/badge/wetest-2.3.0-brightgreen.svg)](wetest.qq.com) [![license](https://img.shields.io/badge/license-mit-red.svg)](https://github.com/Tencent/tinker/blob/master/LICENSE)

GAutomator是一个针对Unity手游的UI自动化测试框架。设计理念与使用方式,类似于Android的UIAutomator。GAutomator以Unity中的GameObject为操作对象,通过操作GameObject实现UI自动化测试。基于GameObject的方式,不存在手机分辨率适配的问题,一份脚本能够运行在不同手机之上,基于GameObject的另外一个优点为鲁棒性较强,游戏的UI界面经常发生变化,GameObject变化频率相对较低。

Expand Down Expand Up @@ -80,6 +80,8 @@ Unity引擎相关的API均放在engine.py模块中
|[get_touchable_elements_bound](https://github.com/Tencent/GAutomator/blob/master/doc/GAutomator%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3.md#45-弹出框处理获取可交互节点)|获取当前游戏界面的可点击节点(能有效过滤弹出框下的按钮),NGUI源码有修改的游戏可能会无效|
|[get_registered_handlers](https://github.com/Tencent/GAutomator/blob/master/doc/GAutomator%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3.md#72-脚本调用)|游戏开发人员可在WeTest SDK注册方法供脚本调用,完成复杂功能,如GM命令、人物移动等。该接口获取所有注册的方法|
|[call_registered_handler](https://github.com/Tencent/GAutomator/blob/master/doc/GAutomator%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3.md#722-执行委托)|调用游戏开发人员注册的方法,如发送GM命令、人物移动到指定位置|
|[get_component_methods](https://github.com/Tencent/GAutomator/blob/master/doc/GAutomator%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3.md#723-获取方法)|获取游戏组件上的public方法信息|
|[call_component_method](https://github.com/Tencent/GAutomator/blob/master/doc/GAutomator%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3.md#724-执行委托)|调用游戏组件上的public方法,需要传入组件名称方法名称和参数列表|

手机相关的接口均放在device.py中,主要通过uiautomator实现

Expand Down
Binary file modified bin/wetest_unity_ngui.zip
Binary file not shown.
Binary file modified bin/wetest_unity_ugui.zip
Binary file not shown.
8 changes: 3 additions & 5 deletions doc/GAutomatorView游戏控件查看器.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@ step 4:中间控件树右击可复制

## 三、常见问题
**Q:GAutomatorView崩溃?**

**A:**GAutomatorView目前不支持中文目录,请勿将GAutomatorView移动到非中文目录下
A:GAutomatorView目前不支持中文目录,请勿将GAutomatorView移动到非中文目录下

**Q:无法同步游戏?**

<img src="image/gaview_error1.png" alt="Drawing" width="800px" />

**A:**请确保游戏已经拉起,且拉起的游戏已经集成SDK
A:请确保游戏已经拉起,且拉起的游戏已经集成SDK

**Q:点击无法查找到控件?**

**A:**1.非NGUI及UGUI UI无法通过点击的方式查找到;2、NGUI源码的事件模块如果进行过修改可能会让这部分功能无效;3、请确保SDK集成成功,u3dautomation.jar已经集成到游戏之中。
A:1.非NGUI及UGUI UI无法通过点击的方式查找到;2、NGUI源码的事件模块如果进行过修改可能会让这部分功能无效;3、请确保SDK集成成功,u3dautomation.jar已经集成到游戏之中。
1,924 changes: 733 additions & 1,191 deletions doc/GAutomator使用说明文档.html

Large diffs are not rendered by default.

34 changes: 32 additions & 2 deletions doc/GAutomator使用说明文档.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
- [7.2 脚本调用](#7.2)
- [7.2.1 获取可执行委托](#7.2.1)
- [7.2.2 执行委托](#7.2.2)
- [7.2.3 获取组件上的方法](#7.2.3)
- [7.2.4 调用组件上的方法](#7.2.4)
- [7.3 反射获取游戏中属性值](#7.3)
- [7.4 设置最佳渲染Camera](#7.4)
- [8 实战用例](#8)
Expand Down Expand Up @@ -718,7 +720,7 @@ Button : GameObject /Canvas/Dialog(Clone)/Cancel Instance = 4294957136,Bound : {
<a name="4.6"></a>

## 4.6 获取文字内容
SDK 9版本中,可以获取到游戏中的文字内容。NGUI能够获取到UILable、UIInput、GUIText组件上的文字内容,如果GameObject上不包含以上组件,将抛出异常。UGUI能够获取Text、GUIText组件上的文字信息。示例在interaction.py中,wetest_demo.apk需要在interaction界面。
可以获取到游戏中的文字内容。NGUI能够获取到UILable、UIInput、GUIText组件上的文字内容,如果GameObject上不包含以上组件,将抛出异常。UGUI能够获取Text、GUIText组件上的文字信息。示例在interaction.py中,wetest_demo.apk需要在interaction界面。
```python
def test_get_element_txt():
e=engine.find_element("Click/Text")
Expand All @@ -731,7 +733,7 @@ def test_get_element_txt():
<a name="4.7"></a>

## 4.7 获取图片名称
SDK 9版本中,可以获取到游戏中的GameObject上面对应的图片名称。NGUI取UITexture、UISprite、SpriteRenderer组件上的图片名称,如果GameObject上不包含以上组件,将抛出异常。UGUI能够获取Image、RawImage、SpriteRenderer组件上的图片名称。示例在interaction.py中,wetest_demo.apk需要在interaction界面。
可以获取到游戏中的GameObject上面对应的图片名称。NGUI取UITexture、UISprite、SpriteRenderer组件上的图片名称,如果GameObject上不包含以上组件,将抛出异常。UGUI能够获取Image、RawImage、SpriteRenderer组件上的图片名称。示例在interaction.py中,wetest_demo.apk需要在interaction界面。
```python
def test_get_element_image():
e = engine.find_element("Back")
Expand All @@ -748,6 +750,7 @@ def test_get_element_image():
<a name="5.1"></a>

## 5.1 屏幕尺寸与转向

```python
def test_get_display_size():
display_size=device.get_display_size()
Expand Down Expand Up @@ -1004,6 +1007,33 @@ python self_define_fun.py
```
运行"test"关键词对应的注册的委托,传入参数为"python call test"。获取委托执行后的返回值"python call test Response"

<a name="7.2.3"></a>

### 7.2.3 获取组件上的方法
可以获取到游戏中的GameObject上某个Component上的public方法信息,包括方法名称,方法需要的参数和返回的类型。在wetest_demo.apk中,Sample按钮上挂载了ReflectionTest组件,可以使用下面代码返回该组件中的方法。
```python
def test_get_component_methods(self):
element = self.engine.find_element("Sample")
methods = self.engine.get_component_methods(element, "ReflectionTest")
logger.debug(methods)
```
上面的代码在sample/interaction.py中,运行该函数可以获得全部的public方法。

<a name="7.2.4"></a>

### 7.2.4 调用组件上的方法
通过反射可以调用GameObject某个组件上的public方法,并获得返回值。调用时需要传入组件名称、方法名称和参数列表。wetest_demo.apk中调用Sample按钮ReflectionTest组件上的TestReflection方法(需要两个参数int,string,返回int值),调用代码如下:
```python
def test_call_component_method(self):
element = self.engine.find_element("Sample")
params = []
params.append(5)
params.append("Hello World")
result = self.engine.call_component_method(element, "ReflectionTest", "TestReflection", params)
logger.debug(result)
```
上面的代码在sample/interaction.py中,将调用游戏中的TestReflection方法,并返回105(方法的返回值)。


<a name="7.3"></a>

Expand Down
Binary file modified doc/GAutomator使用说明文档.pdf
Binary file not shown.
4 changes: 2 additions & 2 deletions doc/开始教程.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 开始教程

示例代码:sample/sample.py中的**test函数**,示例apk:sampel/wetest_demo.apk。wetest_demo.apk已经集成了wetest sdk。
可以GAutomator脚本可前往http://wetest.qq.com/cloud/index.php/phone/blrooike 下载。示例:sample/sample.py中的**test函数**,示例apk:sampel/wetest_demo.apk。wetest_demo.apk已经集成了wetest sdk。

## 1.1 Simple Usage
已经安装好python及依赖库后,可以使用pycharm(请下社区版,社区版免费)直接打开工程,你可以下面的代码开始我们的测试
Expand Down Expand Up @@ -151,4 +151,4 @@ python main.py --qqname=2952020111 --qqpwd=wetestpwd --engineport=50032 --uiport
--engineport:与手机端的sdk服务建立网络映射,填入的为本地的网络端口号(如,50031),不同手机之间要确保不同
--uiport:与手机端的UIAutomator服务建立网络映射,填入的为本地的网络端口号(如,19008),不同手机之间要确保不同
--serial:adb devcies能够查看手机的序列号,不同的序列号代表不同的手机
```
```
15 changes: 15 additions & 0 deletions sample/interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,19 @@ def test_get_touchable_elements():
engine.click_position(elements[0][1]["x"], elements[0][1]["y"])


def test_get_component_methods():
element = engine.find_element("Sample")
methods = engine.get_component_methods(element, "ReflectionTest")
logger.debug(methods)


def test_call_component_method():
element = engine.find_element("Sample")
params = []
params.append(5)
params.append("Hello World")
result = engine.call_component_method(element, "ReflectionTest", "TestReflection", params)
logger.debug(result)


test_get_touchable_elements()
Binary file modified sample/wetest_demo.apk
Binary file not shown.
2 changes: 2 additions & 0 deletions wpyscripts/common/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class Commands(object):
GET_OBJECT_FIELD=112 # 通过反射获取gameobject中component的属性值
FIND_ELEMENTS_COMPONENT=113 #获取所有包含改组件的gameobject
SET_CAMERA_NAME=114 #设置渲染的最佳的Camera
GET_COMPONENT_METHODS = 115 # 反射获取组件上的方法
CALL_COMPONENT_MOTHOD = 116 # 通过反射调用组件的函数

#######################/
HANDLE_TOUCH_EVENTS = 200 # 发送down move up
Expand Down
25 changes: 24 additions & 1 deletion wpyscripts/test/game_engine_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def test_get_sdk_version(self):
logger.info(sdk_info)
#self.assertTrue(cmp(sdk_info.engine_version, "5.1.2f1") == 0, "engine version Error")
self.assertTrue(cmp(sdk_info.engine, "Unity3D") == 0, "engine Error")
self.assertTrue(sdk_info.sdk_version == "1.2.1", "sdkversion Error")
self.assertTrue(sdk_info.sdk_version == "1.3.1", "sdkversion Error")

def test_find_element(self):
self._start()
Expand Down Expand Up @@ -316,6 +316,29 @@ def test_set_camera(self):
# bounds = self.engine.get_element_bound(elements[0])
# print(bounds)

def test_get_component_methods(self):
element = self.engine.find_element("Sample")
self._start()
methods = self.engine.get_component_methods(element, "ReflectionTest")
logger.debug(methods)
self._end("get_component_methods")

def test_call_component_method(self):
element = self.engine.find_element("Sample")
params = []
params.append(5)
params.append("Hello World")
self._start()
result = self.engine.call_component_method(element, "ReflectionTest", "TestReflection", params)
logger.debug(result)
self._end("call_component_method")

def test_find_elements_path2(self):
self._start()
elements = self.engine.find_elements_path("/Canvas/Panel/{{(.*(S|s).*)}}/Text")
logger.debug(elements)
self._end("find_elements_path /root")


if __name__ == '__main__':
unittest.main()
61 changes: 56 additions & 5 deletions wpyscripts/wetest/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,19 +166,26 @@ def _parse_path(self, path):
if len(nodes) == 0:
return None
elif nodes[0] == '':
pathNode = {"name": "/", "index": -1, "txt": "", "img": ""}
pathNode = {"name": "/", "index": -1, "txt": "", "img": "", "regex": ""}
nodeInfos.append(pathNode)
nodes = nodes[1:]
name_re = re.compile(r"(?P<name>[^[{]+)")
txt_re = re.compile(r"(txt\s*=\s*(?P<txt>[^,\}]*))")
img_re = re.compile(r"(img\s*=\s*(?P<img>[^,\}]*))")
index_re = re.compile(r"\[(?P<index>\d+)\]")
regex_re = re.compile(r"\{\{(?P<regex>.+)\}\}")
for node in nodes:
result = name_re.search(node)
result = regex_re.search(node)
if result:
name = result.groupdict().get("name", "")
else:
regex = result.groupdict().get("regex", "")
name = ""
else:
regex = ""
result = name_re.search(node)
if result:
name = result.groupdict().get("name", "")
else:
name = ""

result = txt_re.search(node)
if result:
Expand All @@ -197,7 +204,7 @@ def _parse_path(self, path):
index = int(index)
else:
index = -1
pathNode = {"name": name, "index": index, "txt": txt, "img": img}
pathNode = {"name": name, "index": index, "txt": txt, "img": img, "regex": regex}
nodeInfos.append(pathNode)
return nodeInfos

Expand Down Expand Up @@ -775,3 +782,47 @@ def get_component_field(self, element, component, attribute):
def set_camera(self, camera):
ret = self.socket.send_command(Commands.SET_CAMERA_NAME, [camera])
return ret

def get_component_methods(self, element, component):
"""
通过反射获取组件的方法
:param element:已经找到的GameObject对象
:param component:组件名称
:return:方法的描述,包括方法名称,返回值类型,参数类型
:Usage:
>>>import wpyscripts.manager as manager
>>>engine=manager.get_engine()
>>>element = engine.find_element("Sample")
>>>methods = engine.get_component_methods(element, "ReflectionTest")
:raise WeTestInvaildArg,WeTestRuntimeError
"""
if element is None or component is None:
raise WeTestInvaildArg("Invaild Instance")

ret = self.socket.send_command(Commands.GET_COMPONENT_METHODS,
{"instance": element.instance, "comopentName": component})
return ret

def call_component_method(self, element, component, method, params):
"""
通过反射调用组件上的方法
:param element:已经找到的GameObject对象
:param component:组件名称
:param method:方法名称
:param params:方法参数数组
:return:方法的返回
:Usage:
>>>import wpyscripts.manager as manager
>>>engine=manager.get_engine()
>>>element = engine.find_element("Sample")
>>>params = [5, "Hello World"]
>>>result = engine.call_component_method(element, "ReflectionTest", "TestReflection", params)
:raise WeTestInvaildArg,WeTestRuntimeError
"""
if element is None or component is None or method is None:
raise WeTestInvaildArg("Invaild Instance")

ret = self.socket.send_command(Commands.CALL_COMPONENT_MOTHOD,
{"instance": element.instance, "comopentName": component,
"methodName": method, "parameters": params})
return ret

0 comments on commit 63f0ad9

Please sign in to comment.