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 logging 21import os 22import time 23 24import pytest 25 26from aw import Application 27from aw import Utils 28from aw import debugger 29from aw.api import debugger_api, runtime_api 30 31 32@pytest.mark.debug 33@pytest.mark.timeout(60) 34class TestAttachWorkerStep: 35 """ 36 测试用例:多实例 attach 调试,执行单步操作进行跨线程通信 37 测试步骤: 38 1. 拉起应用,attach 主线程 39 2. 连接 connect server 和主线程 debugger server 40 3. 创建两个子线程,连接子线程 debugger server 41 4. 所有线程使能 Runtime 和 Debugger 42 5. 主线程 Index.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) 43 6. 触发点击事件,主线程命中断点 44 7. 销毁其中一个子线程 45 8. 子线程 Worker.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) 46 9. 主线程 resume,暂停在下一断点(Debugger.resume) 47 10. 重新创建一个子线程,使能并设置断点 48 11. 主线程 resume,发送消息给子线程,主线程暂停在下一断点(Debugger.resume) 49 12. 子线程命中断点后 stepOut,发消息给主线程(Debugger.stepOut) 50 13. 主线程 stepOver,发送消息给另一子线程,主线程暂停在下一行(Debugger.stepOver) 51 14. 子线程命中断点后 resume,发消息给主线程(Debugger.resume) 52 15. 销毁所有子线程,对应的 debugger server 连接断开 53 16. 关闭主线程 debugger server 和 connect server 连接 54 关键代码: 55 Index.ets 56 let workerIndex = 0 57 function newWorker() {} // 创建一个子线程, workerIndex++ 58 function terminateWorker() {} // 销毁一个子线程, workerIndex-- 59 for (let i = 0; i < 2; i++) { 60 newWorker() 61 } 62 .OnClick(() => { 63 terminateWorker() 64 newWorker() 65 for (let i = 0; i < workerIndex; i++) { 66 workers[i].postMessage("hello world") 67 } 68 while (workerIndex) { 69 terminateWorker() 70 } 71 }) 72 Worker.ets 73 const workerPort: ThreadWorkerGlobalScope = worker.workerPort; 74 workerPort.onmessage = (e: MessageEvents) => { 75 workerPort.postMessage(e.data); 76 } 77 """ 78 79 def setup_method(self): 80 logging.info('Start running TestAttachWorkerStep: setup') 81 82 self.log_path = rf'{os.path.dirname(__file__)}\..\log' 83 self.hilog_file_name = 'test_attach_worker_step.hilog.txt' 84 self.id_generator = Utils.message_id_generator() 85 86 # receive the hilog before the test start 87 Utils.clear_fault_log() 88 self.hilog_process, self.write_thread = Utils.save_hilog(log_path=self.log_path, 89 file_name=self.hilog_file_name, 90 debug_on=True) 91 92 def teardown_method(self): 93 Application.uninstall(self.config['bundle_name']) 94 95 # terminate the hilog receive process after the test done 96 time.sleep(3) 97 self.hilog_process.stdout.close() 98 self.hilog_process.terminate() 99 self.hilog_process.wait() 100 self.write_thread.join() 101 102 Utils.save_fault_log(log_path=self.log_path) 103 logging.info('TestAttachWorkerStep done') 104 105 def test(self, test_suite_worker_01): 106 logging.info('Start running TestAttachWorkerStep: test') 107 self.config = test_suite_worker_01 108 websocket = self.config['websocket'] 109 taskpool = self.config['taskpool'] 110 pid = self.config['pid'] 111 self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, websocket) 112 self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, websocket) 113 114 Application.attach(self.config['bundle_name']) 115 taskpool.submit(websocket.main_task(taskpool, self.procedure, pid)) 116 taskpool.await_taskpool() 117 taskpool.task_join() 118 if taskpool.task_exception: 119 raise taskpool.task_exception 120 121 async def procedure(self, websocket): 122 ################################################################################################################ 123 # main thread: connect the debugger server 124 ################################################################################################################ 125 main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True) 126 logging.info(f'Connect to the debugger server of instance: {main_thread.instance_id}') 127 ################################################################################################################ 128 # worker thread: connect the debugger server 129 ################################################################################################################ 130 worker_thread_1 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 131 logging.info(f'Connect to the debugger server of instance: {worker_thread_1.instance_id}') 132 worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 133 logging.info(f'Connect to the debugger server of instance: {worker_thread_2.instance_id}') 134 ################################################################################################################ 135 # worker thread: Runtime.enable 136 ################################################################################################################ 137 await self.runtime_impl.send("Runtime.enable", worker_thread_1) 138 await self.runtime_impl.send("Runtime.enable", worker_thread_2) 139 ################################################################################################################ 140 # worker thread: Debugger.enable 141 ################################################################################################################ 142 await self.debugger_impl.send("Debugger.enable", worker_thread_1) 143 await self.debugger_impl.send("Debugger.enable", worker_thread_2) 144 ################################################################################################################ 145 # worker thread: Runtime.runIfWaitingForDebugger 146 ################################################################################################################ 147 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_1) 148 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2) 149 ################################################################################################################ 150 # main thread: Runtime.enable 151 ################################################################################################################ 152 await self.runtime_impl.send("Runtime.enable", main_thread) 153 ################################################################################################################ 154 # main thread: Debugger.enable 155 ################################################################################################################ 156 await self.debugger_impl.send("Debugger.enable", main_thread) 157 ################################################################################################################ 158 # main thread: Runtime.runIfWaitingForDebugger 159 ################################################################################################################ 160 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", main_thread) 161 ################################################################################################################ 162 # main thread: Debugger.removeBreakpointsByUrl 163 ################################################################################################################ 164 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['index']) 165 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", main_thread, params) 166 ################################################################################################################ 167 # main thread: Debugger.getPossibleAndSetBreakpointByUrl 168 ################################################################################################################ 169 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=53), 170 debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=57)] 171 params = debugger.SetBreakpointsLocations(locations) 172 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 173 main_thread, params) 174 assert response['result']['locations'][0]['id'] == 'id:53:0:' + self.config['file_path']['index'] 175 assert response['result']['locations'][1]['id'] == 'id:57:0:' + self.config['file_path']['index'] 176 ################################################################################################################ 177 # main thread: click on the screen 178 ################################################################################################################ 179 Application.click_on_middle() 180 ################################################################################################################ 181 # main thread: Debugger.paused, hit breakpoint 182 ################################################################################################################ 183 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 184 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 185 assert response['params']['hitBreakpoints'] == ['id:53:16:' + self.config['file_path']['index']] 186 ################################################################################################################ 187 # worker thread: destroy instance 188 ################################################################################################################ 189 response = await self.debugger_impl.destroy_instance() 190 if response['instanceId'] == worker_thread_1.instance_id: 191 worker_thread_1 = worker_thread_2 192 ################################################################################################################ 193 # worker thread: Debugger.removeBreakpointsByUrl 194 ################################################################################################################ 195 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker']) 196 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_1, params) 197 ################################################################################################################ 198 # worker thread: Debugger.getPossibleAndSetBreakpointByUrl 199 ################################################################################################################ 200 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)] 201 params = debugger.SetBreakpointsLocations(locations) 202 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 203 worker_thread_1, params) 204 assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker'] 205 ################################################################################################################ 206 # main thread: Debugger.resume 207 ################################################################################################################ 208 await self.debugger_impl.send("Debugger.resume", main_thread) 209 # main thread: Debugger.paused, hit breakpoint 210 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 211 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 212 assert response['params']['hitBreakpoints'] == ['id:57:20:' + self.config['file_path']['index']] 213 ################################################################################################################ 214 # worker thread: connect the debugger server 215 ################################################################################################################ 216 worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 217 logging.info(f'Connect to the debugger server of instance: {worker_thread_2.instance_id}') 218 ################################################################################################################ 219 # worker thread: Runtime.enable 220 ################################################################################################################ 221 await self.runtime_impl.send("Runtime.enable", worker_thread_2) 222 ################################################################################################################ 223 # worker thread: Debugger.enable 224 ################################################################################################################ 225 await self.debugger_impl.send("Debugger.enable", worker_thread_2) 226 ################################################################################################################ 227 # worker thread: Runtime.runIfWaitingForDebugger 228 ################################################################################################################ 229 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2) 230 # worker thread: Debugger.scriptParsed 231 response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_2) 232 assert response['params']['url'] == self.config['file_path']['worker'] 233 assert response['params']['endLine'] == 0 234 # worker thread: Debugger.paused 235 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2) 236 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 237 assert response['params']['reason'] == 'Break on start' 238 ################################################################################################################ 239 # worker thread: Debugger.removeBreakpointsByUrl 240 ################################################################################################################ 241 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker']) 242 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_2, params) 243 ################################################################################################################ 244 # worker thread: Debugger.getPossibleAndSetBreakpointByUrl 245 ################################################################################################################ 246 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)] 247 params = debugger.SetBreakpointsLocations(locations) 248 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 249 worker_thread_2, params) 250 assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker'] 251 ################################################################################################################ 252 # worker thread: Debugger.resume 253 ################################################################################################################ 254 await self.debugger_impl.send("Debugger.resume", worker_thread_2) 255 ################################################################################################################ 256 # main thread: Debugger.resume 257 ################################################################################################################ 258 await self.debugger_impl.send("Debugger.resume", main_thread) 259 # main thread: Debugger.paused 260 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 261 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 262 assert response['params']['reason'] == 'other' 263 assert response['params']['hitBreakpoints'] == ['id:57:20:' + self.config['file_path']['index']] 264 # worker thread: Debugger.paused 265 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1) 266 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 267 assert response['params']['reason'] == 'Break on start' 268 assert response['params']['hitBreakpoints'] == [] 269 ################################################################################################################ 270 # worker thread: Debugger.resume 271 ################################################################################################################ 272 await self.debugger_impl.send("Debugger.resume", worker_thread_1) 273 # worker thread: Debugger.paused 274 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1) 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.stepOut 280 ################################################################################################################ 281 await self.debugger_impl.send("Debugger.stepOut", worker_thread_1) 282 ################################################################################################################ 283 # worker thread: Debugger.disable 284 ################################################################################################################ 285 await self.debugger_impl.send("Debugger.disable", worker_thread_1) 286 ################################################################################################################ 287 # main thread: Debugger.stepOver 288 ################################################################################################################ 289 await self.debugger_impl.send("Debugger.stepOver", main_thread) 290 # main thread: Debugger.paused 291 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 292 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 293 assert response['params']['reason'] == 'other' 294 assert response['params']['hitBreakpoints'] == [] 295 # worker thread: Debugger.paused 296 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2) 297 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 298 assert response['params']['reason'] == 'other' 299 assert response['params']['hitBreakpoints'] == ['id:11:4:' + self.config['file_path']['worker']] 300 ################################################################################################################ 301 # worker thread: Debugger.resume 302 ################################################################################################################ 303 await self.debugger_impl.send("Debugger.resume", worker_thread_2) 304 ################################################################################################################ 305 # worker thread: Debugger.disable 306 ################################################################################################################ 307 await self.debugger_impl.send("Debugger.disable", worker_thread_2) 308 ################################################################################################################ 309 # main thread: Debugger.resume 310 ################################################################################################################ 311 await self.debugger_impl.send("Debugger.resume", main_thread) 312 ################################################################################################################ 313 # worker thread: destroy instance 314 ################################################################################################################ 315 response = await self.debugger_impl.destroy_instance() 316 assert response['instanceId'] != self.config['pid'] 317 response = await self.debugger_impl.destroy_instance() 318 assert response['instanceId'] != self.config['pid'] 319 ################################################################################################################ 320 # main thread: Debugger.disable 321 ################################################################################################################ 322 await self.debugger_impl.send("Debugger.disable", main_thread) 323 ################################################################################################################ 324 # close the websocket connections 325 ################################################################################################################ 326 await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close') 327 await websocket.send_msg_to_connect_server('close') 328 ################################################################################################################