README.md
1# 调试调优自动化测试框架使用指南
2
3[TOC]
4
5## 1 框架设计
6
7调试调优的通用流程可抽象为如下步骤:
8
9- IDE将应用推入设备,并拉起应用(或attach到已启动应用上)
10
11- IDE与应用建立websocket连接
12- IDE与应用之间通过websocket发送和接受调试调优相关的业务消息,消息格式遵循CDP协议
13
14该自动化框架依据以上流程,实现了应用启动、websocket连接、消息交互等步骤的自动化,基于此可以进行调试调优用例的执行与开发
15
16框架的目录结构与功能描述如下:
17
18### aw
19
20封装了自动化框架通用的Action Words,包括:
21
22- `application.py`:应用启动、停止、安装、卸载等相关接口
23- `fport.py`:执行hdc fport命令的相关接口,指定端口号、进程ID和线程ID等,用于后续的websocket连接
24- `utils.py`:封装各种通用的工具类接口
25- `websocket.py`:websocket连接的关键基础类,功能包括:
26 - connect server和debugger server的websocket连接
27 - 采用消息队列实现websocket的消息发送与接收
28 - **支持多实例应用的实现**:connect server的消息接收队列持续监听addInstance消息,一旦接收到新的instance后,便会创建对应的debugger server连接
29 - **异步实现**:多条websocket连接的消息接收与发送通道均实现为协程,可被提交到taskpool中作为异步任务执行
30- `taskpool.py`:异步任务池,功能包括:
31 - 采用队列实现任务池的生命周期控制
32 - **可在任意时刻提交任务到taskpool中进行异步执行**
33 - 可为每个任务添加任意多个回调函数,在任务结束后执行
34- `cdp`:cdp协议的封装
35
36### scenario_test
37
38场景测试用例目录
39
40- `conftest.py`:基于pytest fixture实现的测试套方法
41- `test_name_number.py`:测试用例文件
42
43### resource
44
45用于放置编译后的应用hap包
46
47目录下的archive.json记录了应用工程名、hap包名和bundle name的对应关系
48
49### pytest.ini
50
51python pytest库的配置文件
52
53### run.py
54
55批量执行测试用例的入口文件
56
57### requirements.txt
58
59自动化测试框架的python库依赖
60
61## 2 测试用例执行
62
63### 准备工作
64
65- 操作系统为Windows
66
67- 已安装hdc.exe并配置好环境变量,可通过cmd启动执行
68
69- 测试设备已连接,且开启USB调试
70
71- 下载自动化测试项目
72
73 代码仓:https://gitee.com/openharmony/arkcompiler_toolchain
74
75 项目目录:`.\arkcompiler_toolchain\test\autotest`
76
77- Python已安装且版本 >= 3.8.0
78
79- 安装Python依赖
80
81 ```powershell
82 > cd .\arkcompiler_toolchain\test\autotest
83 > python -m pip install -r requirements.txt [--trusted-host hostname -i Python_Package_Index]
84 ```
85
86- 下载hap包
87
88 通过归档链接下载hap包,并将所有hap包移动到.\arkcompiler_toolchain\test\autotest\resource目录下
89
90### 执行用例
91
92cmd进入`.\arkcompiler_toolchain\test\autotest`目录,执行:
93
94```python
95> python .\run.py
96```
97
98用例执行过程中,cmd会实时输出用例日志信息,执行完成后,cmd会展示执行结果,同时会生成测试报告,并导出hilog日志:
99
100- `.\arkcompiler_toolchain\test\autotest\report\report.html`:测试报告
101
102- `.\arkcompiler_toolchain\test\autotest\report\xxx.hilog.txt`:每个测试用例对应一份debug级别的hilog日志
103
104- `.\arkcompiler_toolchain\test\autotest\report\faultlogger`:用例执行过程中产生的错误日志
105
106## 3 测试用例开发
107
108### 测试应用
109
110根据测试用例场景,确定并开发你的测试应用,编译后将应用hap包放在`.\arkcompiler_toolchain\test\autotest\resource`目录下
111
112### 测试套
113
114测试套以pytest fixture形式开发,对应于`.\arkcompiler_toolchain\test\autotest\scenario_test\conftest.py`中的一个方法;
115
116对于一个确定的测试应用,和一套固定的配置信息,我们可以将他们抽象出来作为一个fixture,基于该fixture我们可以开发多个测试用例
117
118fixture写法举例如下:
119
120```python
121@pytest.fixture(scope='class') # 该装饰器指定下面的函数是一个fixture
122def test_suite_debug_01(): # fixture名称
123 logging.info('running test_suite_debug_01')
124
125 bundle_name = 'com.example.worker'
126 hap_name = 'MyApplicationWorker.hap'
127
128 config = {'connect_server_port': 15678,
129 'debugger_server_port': 15679,
130 'bundle_name': bundle_name,
131 'hap_path': rf'{os.path.dirname(__file__)}\..\resource\{hap_name}'}
132
133 # 将应用推入设备,并以-D模式启动
134 pid = Application.launch_application(config['bundle_name'], config['hap_path'], start_mode='-D')
135 assert pid != 0, logging.error(f'Pid of {hap_name} is 0!')
136 config['pid'] = pid
137
138 # hdc fport
139 Fport.clear_fport()
140 Fport.fport_connect_server(config['connect_server_port'], config['pid'], config['bundle_name'])
141
142 # 实例化WebSocket对象
143 config['websocket'] = WebSocket(config['connect_server_port'], config['debugger_server_port'])
144
145 # 实例化TaskPool对象
146 config['taskpool'] = TaskPool()
147
148 return config
149
150```
151
152样例参考:`.\arkcompiler_toolchain\test\autotest\scenario_test\conftest.py => test_suite_debug_01`
153
154### 测试用例
155
156测试用例放置在`.\arkcompiler_toolchain\test\autotest\scenario_test\`目录下,每个.py文件对应一个测试用例,命名规则为`test\_测试项\_编号.py`
157
158测试用例代码文件结构如下:
159
160```python
161@pytest.mark.debug
162@pytest.mark.timeout(40)
163class TestDebug01:
164 def setup_method(self):
165 pass
166
167 def teardown_method(self):
168 pass
169
170 def test(self, test_suite_debug_01):
171 pass
172
173 async def procedure(self, websocket):
174 pass
175
176```
177
178- 装饰器`@pytest.mark.debug`标明该用例是一个测试debug功能的用例,debug可以改为其它任意名字,如cpu_profiler
179
180- 装饰器`@pytest.mark.timeout(40)`标明该用例必须在40s内执行完毕,否则不通过
181
182- `class TestDebug01` 是该测试用例对应的测试类,测试类必须以**Test**开头
183
184- `setup_class` 方法在用例开始执行前被执行,其中放置一些需要初始化的内容
185
186- `teardown_clas` 方法在用例结束执行后被执行,其中放置一些需要结束或清理的内容
187
188- `test` 方法是测试用例执行的入口,注意以下几点:
189
190 - 第二个参数为fixture,不仅可以标识该用例属于哪个测试套,还可以获取该fixture执行后的返回结果;在test方法被执行前,fixture会首先被执行
191
192 - 通过下面的代码,将websocket中封装的main task提交到taskpool,等待所有任务结束并捕获异常
193
194 ```Python
195 taskpool.submit(websocket.main_task(taskpool, websocket, self.procedure, pid))
196 taskpool.await_taskpool()
197 taskpool.task_join()
198 if taskpool.task_exception:
199 raise taskpool.task_exception
200 ```
201
202- **`procedure`方法**:测试步骤和预期结果执行的位置,在test方法中,procedure作为参数被提交到taskpool中,因此其同样作为一个**异步任务**在执行
203
204当我们新增一个测试用例时,主要工作就是在procedure方法中放置我们的测试步骤和预期结果。首先,我们可以手动执行一次测试用例,并抓出connect server、debugger server交互的日志信息。此后,我们可以对照日志信息在procedure方法中添加内容,每一次消息发送可以当作一个测试步骤,每一次消息接收可以当作一次预期结果
205
206例如:
207
208```Python
209################################################################################################################
210# main thread: Debugger.getPossibleAndSetBreakpointByUrl
211################################################################################################################
212id = next(self.id_generator) # 获取此次发送消息的id
213
214# 构造断点信息
215locations = [debugger.BreakLocationUrl(url='entry|entry|1.0.0|src/main/ets/pages/Index.ts', line_number=22),
216 debugger.BreakLocationUrl(url='entry|entry|1.0.0|src/main/ets/pages/Index.ts', line_number=26)]
217
218# 发送消息并获取返回值,调用cdp库中的debugger.get_possible_and_set_breakpoint_by_url(locations)可以自动拼接cdp消息
219response = await communicate_with_debugger_server(main_thread_instance_id,
220 main_thread_to_send_queue,
221 main_thread_received_queue,
222 debugger.get_possible_and_set_breakpoint_by_url(locations),
223 id)
224
225# 判断返回结果是否符合预期
226response = json.loads(response)
227assert response['id'] == id
228assert response['result']['locations'][0]['id'] == 'id:22:0:entry|entry|1.0.0|src/main/ets/pages/Index.ts'
229assert response['result']['locations'][1]['id'] == 'id:26:0:entry|entry|1.0.0|src/main/ets/pages/Index.ts'
230```
231
232注意:
233
234- 建议在测试类声明后,用类注释写明该测试用例的内容;
235- 每个测试步骤和预期结果之间,用上述注释方式隔开,并标明此次测试步骤的操作;
236- 对于aw中未实现的CDP协议,请封装在aw/cdp目录下对应的domain文件中
237
238样例参考:`.\arkcompiler_toolchain\test\autotest\scenario_test\test_debug_01.py`
239
240