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