1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3""" 4Copyright (c) 2024 Huawei Device Co., Ltd. 5Licensed under the Apache License, Version 2.0 (the "License"); 6you may not use this file except in compliance with the License. 7You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11Unless required by applicable law or agreed to in writing, software 12distributed under the License is distributed on an "AS IS" BASIS, 13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14See the License for the specific language governing permissions and 15limitations under the License. 16 17Description: Scenario test case. 18""" 19 20import json 21import logging 22import os 23import time 24 25import pytest 26 27from aw import Application 28from aw import Utils 29from aw import debugger 30from aw.api import debugger_api, runtime_api 31 32 33@pytest.mark.debug 34@pytest.mark.timeout(30) 35class TestDebug02: 36 """ 37 测试用例:多实例 attach 调试 38 测试步骤: 39 1. 拉起应用,attach 主线程 40 2. 连接 connect server 和主线程 debugger server 41 3. 创建两个子线程,连接子线程 debugger server 42 4. 所有线程使能 Runtime 和 Debugger 43 5. 主线程 Index.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) 44 6. 触发点击事件,主线程命中断点 45 7. 销毁其中一个子线程 46 8. 子线程 Worker.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) 47 9. 主线程 resume,暂停在下一断点(Debugger.resume) 48 10. 重新创建一个子线程,使能并设置断点 49 11. 主线程 resume,发送消息给子线程,主线程暂停在下一断点(Debugger.resume) 50 12. 子线程命中断点后 stepOut,发消息给主线程(Debugger.stepOut) 51 13. 主线程 stepOver,发送消息给另一子线程,主线程暂停在下一行(Debugger.stepOver) 52 14. 子线程命中断点后 resume,发消息给主线程(Debugger.resume) 53 15. 销毁所有子线程,对应的 debugger server 连接断开 54 16. 关闭主线程 debugger server 和 connect server 连接 55 """ 56 57 def setup_method(self): 58 logging.info('Start running TestDebug02: setup') 59 60 self.log_path = rf'{os.path.dirname(__file__)}\..\log' 61 self.hilog_file_name = 'test_debug_02.hilog.txt' 62 self.id_generator = Utils.message_id_generator() 63 64 # receive the hilog before the test start 65 Utils.clear_fault_log() 66 self.hilog_process, self.write_thread = Utils.save_hilog(log_path=self.log_path, 67 file_name=self.hilog_file_name, 68 debug_on=True) 69 70 def teardown_method(self): 71 Application.uninstall(self.config['bundle_name']) 72 73 # terminate the hilog receive process after the test done 74 time.sleep(3) 75 self.hilog_process.stdout.close() 76 self.hilog_process.terminate() 77 self.hilog_process.wait() 78 self.write_thread.join() 79 80 Utils.save_fault_log(log_path=self.log_path) 81 logging.info('TestDebug02 done') 82 83 def test(self, test_suite_worker_01): 84 logging.info('Start running TestDebug02: test') 85 self.config = test_suite_worker_01 86 websocket = self.config['websocket'] 87 taskpool = self.config['taskpool'] 88 pid = self.config['pid'] 89 self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, websocket) 90 self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, websocket) 91 92 Application.attach(self.config['bundle_name']) 93 taskpool.submit(websocket.main_task(taskpool, self.procedure, pid)) 94 taskpool.await_taskpool() 95 taskpool.task_join() 96 if taskpool.task_exception: 97 raise taskpool.task_exception 98 99 async def procedure(self, websocket): 100 ################################################################################################################ 101 # main thread: connect the debugger server 102 ################################################################################################################ 103 main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True) 104 logging.info(f'Connect to the debugger server of instance: {main_thread.instance_id}') 105 ################################################################################################################ 106 # worker thread: connect the debugger server 107 ################################################################################################################ 108 worker_thread_1 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 109 logging.info(f'Connect to the debugger server of instance: {worker_thread_1.instance_id}') 110 worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 111 logging.info(f'Connect to the debugger server of instance: {worker_thread_2.instance_id}') 112 ################################################################################################################ 113 # worker thread: Runtime.enable 114 ################################################################################################################ 115 await self.runtime_impl.send("Runtime.enable", worker_thread_1) 116 await self.runtime_impl.send("Runtime.enable", worker_thread_2) 117 ################################################################################################################ 118 # worker thread: Debugger.enable 119 ################################################################################################################ 120 await self.debugger_impl.send("Debugger.enable", worker_thread_1) 121 await self.debugger_impl.send("Debugger.enable", worker_thread_2) 122 ################################################################################################################ 123 # worker thread: Runtime.runIfWaitingForDebugger 124 ################################################################################################################ 125 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_1) 126 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2) 127 ################################################################################################################ 128 # main thread: Runtime.enable 129 ################################################################################################################ 130 await self.runtime_impl.send("Runtime.enable", main_thread) 131 ################################################################################################################ 132 # main thread: Debugger.enable 133 ################################################################################################################ 134 await self.debugger_impl.send("Debugger.enable", main_thread) 135 ################################################################################################################ 136 # main thread: Runtime.runIfWaitingForDebugger 137 ################################################################################################################ 138 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", main_thread) 139 ################################################################################################################ 140 # main thread: Debugger.removeBreakpointsByUrl 141 ################################################################################################################ 142 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['index']) 143 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", main_thread, params) 144 ################################################################################################################ 145 # main thread: Debugger.getPossibleAndSetBreakpointByUrl 146 ################################################################################################################ 147 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=53), 148 debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=57)] 149 params = debugger.SetBreakpointsLocations(locations) 150 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 151 main_thread, params) 152 assert response['result']['locations'][0]['id'] == 'id:53:0:' + self.config['file_path']['index'] 153 assert response['result']['locations'][1]['id'] == 'id:57:0:' + self.config['file_path']['index'] 154 ################################################################################################################ 155 # main thread: click on the screen 156 ################################################################################################################ 157 Application.click_on_middle() 158 ################################################################################################################ 159 # main thread: Debugger.paused, hit breakpoint 160 ################################################################################################################ 161 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 162 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 163 assert response['params']['hitBreakpoints'] == ['id:53:16:' + self.config['file_path']['index']] 164 ################################################################################################################ 165 # worker thread: destroy instance 166 ################################################################################################################ 167 response = await self.debugger_impl.destroy_instance() 168 if response['instanceId'] == worker_thread_1.instance_id: 169 worker_thread_1 = worker_thread_2 170 ################################################################################################################ 171 # worker thread: Debugger.removeBreakpointsByUrl 172 ################################################################################################################ 173 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker']) 174 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_1, params) 175 ################################################################################################################ 176 # worker thread: Debugger.getPossibleAndSetBreakpointByUrl 177 ################################################################################################################ 178 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)] 179 params = debugger.SetBreakpointsLocations(locations) 180 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 181 worker_thread_1, params) 182 assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker'] 183 ################################################################################################################ 184 # main thread: Debugger.resume 185 ################################################################################################################ 186 await self.debugger_impl.send("Debugger.resume", main_thread) 187 # main thread: Debugger.paused, hit breakpoint 188 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 189 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 190 assert response['params']['hitBreakpoints'] == ['id:57:20:' + self.config['file_path']['index']] 191 ################################################################################################################ 192 # worker thread: connect the debugger server 193 ################################################################################################################ 194 worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 195 logging.info(f'Connect to the debugger server of instance: {worker_thread_2.instance_id}') 196 ################################################################################################################ 197 # worker thread: Runtime.enable 198 ################################################################################################################ 199 await self.runtime_impl.send("Runtime.enable", worker_thread_2) 200 ################################################################################################################ 201 # worker thread: Debugger.enable 202 ################################################################################################################ 203 await self.debugger_impl.send("Debugger.enable", worker_thread_2) 204 ################################################################################################################ 205 # worker thread: Runtime.runIfWaitingForDebugger 206 ################################################################################################################ 207 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2) 208 # worker thread: Debugger.scriptParsed 209 response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_2) 210 assert response['params']['url'] == self.config['file_path']['worker'] 211 assert response['params']['endLine'] == 0 212 # worker thread: Debugger.paused 213 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2) 214 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 215 assert response['params']['reason'] == 'Break on start' 216 ################################################################################################################ 217 # worker thread: Debugger.removeBreakpointsByUrl 218 ################################################################################################################ 219 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker']) 220 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_2, params) 221 ################################################################################################################ 222 # worker thread: Debugger.getPossibleAndSetBreakpointByUrl 223 ################################################################################################################ 224 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)] 225 params = debugger.SetBreakpointsLocations(locations) 226 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 227 worker_thread_2, params) 228 assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker'] 229 ################################################################################################################ 230 # worker thread: Debugger.resume 231 ################################################################################################################ 232 await self.debugger_impl.send("Debugger.resume", worker_thread_2) 233 ################################################################################################################ 234 # main thread: Debugger.resume 235 ################################################################################################################ 236 await self.debugger_impl.send("Debugger.resume", main_thread) 237 # main thread: Debugger.paused 238 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 239 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 240 assert response['params']['reason'] == 'other' 241 assert response['params']['hitBreakpoints'] == ['id:57:20:' + self.config['file_path']['index']] 242 # worker thread: Debugger.paused 243 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1) 244 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 245 assert response['params']['reason'] == 'Break on start' 246 assert response['params']['hitBreakpoints'] == [] 247 ################################################################################################################ 248 # worker thread: Debugger.resume 249 ################################################################################################################ 250 await self.debugger_impl.send("Debugger.resume", worker_thread_1) 251 # worker thread: Debugger.paused 252 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1) 253 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 254 assert response['params']['reason'] == 'other' 255 assert response['params']['hitBreakpoints'] == ['id:11:4:' + self.config['file_path']['worker']] 256 ################################################################################################################ 257 # worker thread: Debugger.stepOut 258 ################################################################################################################ 259 await self.debugger_impl.send("Debugger.stepOut", worker_thread_1) 260 ################################################################################################################ 261 # worker thread: Debugger.disable 262 ################################################################################################################ 263 await self.debugger_impl.send("Debugger.disable", worker_thread_1) 264 ################################################################################################################ 265 # main thread: Debugger.stepOver 266 ################################################################################################################ 267 await self.debugger_impl.send("Debugger.stepOver", main_thread) 268 # main thread: Debugger.paused 269 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 270 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 271 assert response['params']['reason'] == 'other' 272 assert response['params']['hitBreakpoints'] == [] 273 # worker thread: Debugger.paused 274 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2) 275 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 276 assert response['params']['reason'] == 'other' 277 assert response['params']['hitBreakpoints'] == ['id:11:4:' + self.config['file_path']['worker']] 278 ################################################################################################################ 279 # worker thread: Debugger.resume 280 ################################################################################################################ 281 await self.debugger_impl.send("Debugger.resume", worker_thread_2) 282 ################################################################################################################ 283 # worker thread: Debugger.disable 284 ################################################################################################################ 285 await self.debugger_impl.send("Debugger.disable", worker_thread_2) 286 ################################################################################################################ 287 # main thread: Debugger.resume 288 ################################################################################################################ 289 await self.debugger_impl.send("Debugger.resume", main_thread) 290 ################################################################################################################ 291 # worker thread: destroy instance 292 ################################################################################################################ 293 response = await self.debugger_impl.destroy_instance() 294 assert response['instanceId'] != self.config['pid'] 295 response = await self.debugger_impl.destroy_instance() 296 assert response['instanceId'] != self.config['pid'] 297 ################################################################################################################ 298 # main thread: Debugger.disable 299 ################################################################################################################ 300 await self.debugger_impl.send("Debugger.disable", main_thread) 301 ################################################################################################################ 302 # close the websocket connections 303 ################################################################################################################ 304 await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close') 305 await websocket.send_msg_to_connect_server('close') 306 ################################################################################################################