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, runtime 29from aw.api import debugger_api, runtime_api 30 31 32@pytest.mark.debug 33@pytest.mark.timeout(60) 34class TestWorkerStep: 35 """ 36 测试用例:多实例 debug 调试,执行单步操作进行跨线程通信 37 测试步骤: 38 1. 连接 connect server 和主线程 debugger server 39 2. 主线程使能 Runtime 和 Debugger 40 3. 主线程 Index.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) 41 4. 主线程 stepOut,暂停在下一断点(Debugger.stepOut) 42 5. 创建第一个子线程,连接子线程 debugger server 43 6. 主线程 resume,暂停在下一断点(Debugger.resume) 44 7. 创建另一个子线程,连接子线程 debugger server 45 8. 所有子线程使能 Runtime 和 Debugger 46 9. 所有子线程 Worker.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) 47 10. 触发点击事件,主线程命中断点 48 11. 销毁其中一个子线程 49 12. 主线程 stepInto,暂停在下一行(Debugger.stepInto) 50 13. 主线程 getProperties,返回给定对象的属性(Runtime.getProperties) 51 14. 主线程 resume,暂停在下一断点(Debugger.resume) 52 15. 重新创建一个子线程,使能并设置断点 53 16. 主线程 stepOut,发送消息给子线程,主线程暂停在下一断点(Debugger.stepOut) 54 17. 子线程命中断点后 getProperties(Runtime.getProperties) 55 18. 子线程 stepOut 发消息给主线程(Debugger.stepOut) 56 19. 主线程 stepInto,发送消息给另一子线程,主线程暂停在下一行(Debugger.stepInto) 57 20. 子线程命中断点后 resume,发消息给主线程(Debugger.resume) 58 21. 销毁所有子线程,对应的 debugger server 连接断开 59 22. 关闭主线程 debugger server 和 connect server 连接 60 关键代码: 61 Index.ets 62 let workerIndex = 0 63 function newWorker() {} // 创建一个子线程, workerIndex++ 64 function terminateWorker() {} // 销毁一个子线程, workerIndex-- 65 for (let i = 0; i < 2; i++) { 66 newWorker() 67 } 68 .onClick(() => { 69 terminateWorker() 70 newWorker() 71 for (let i = 0; i < workerIndex; i++) { 72 workers[i].postMessage("hello world") 73 } 74 while (workerIndex) { 75 terminateWorker() 76 } 77 }) 78 Worker.ets 79 const workerPort: ThreadWorkerGlobalScope = worker.workerPort; 80 workerPort.onmessage = (e: MessageEvents) => { 81 workerPort.postMessage(e.data); 82 } 83 """ 84 85 def setup_method(self): 86 logging.info('Start running TestWorkerStep: setup') 87 88 self.log_path = rf'{os.path.dirname(__file__)}\..\log' 89 self.hilog_file_name = 'test_worker_step.hilog.txt' 90 self.id_generator = Utils.message_id_generator() 91 92 # receive the hilog before the test start 93 Utils.clear_fault_log() 94 self.hilog_process, self.write_thread = Utils.save_hilog(log_path=self.log_path, 95 file_name=self.hilog_file_name, 96 debug_on=True) 97 98 def teardown_method(self): 99 Application.uninstall(self.config['bundle_name']) 100 101 # terminate the hilog receive process after the test done 102 time.sleep(3) 103 self.hilog_process.stdout.close() 104 self.hilog_process.terminate() 105 self.hilog_process.wait() 106 self.write_thread.join() 107 108 Utils.save_fault_log(log_path=self.log_path) 109 logging.info('TestWorkerStep done') 110 111 def test(self, test_suite_worker_01_debug): 112 logging.info('Start running TestWorkerStep: test') 113 self.config = test_suite_worker_01_debug 114 websocket = self.config['websocket'] 115 taskpool = self.config['taskpool'] 116 pid = self.config['pid'] 117 self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, websocket) 118 self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, websocket) 119 120 taskpool.submit(websocket.main_task(taskpool, self.procedure, pid)) 121 taskpool.await_taskpool() 122 taskpool.task_join() 123 if taskpool.task_exception: 124 raise taskpool.task_exception 125 126 async def procedure(self, websocket): 127 ################################################################################################################ 128 # main thread: connect the debugger server 129 ################################################################################################################ 130 main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True) 131 logging.info(f'Connect to the debugger server of instance: {main_thread.instance_id}') 132 ################################################################################################################ 133 # main thread: Runtime.enable 134 ################################################################################################################ 135 await self.runtime_impl.send("Runtime.enable", main_thread) 136 ################################################################################################################ 137 # main thread: Debugger.enable 138 ################################################################################################################ 139 await self.debugger_impl.send("Debugger.enable", main_thread) 140 ################################################################################################################ 141 # main thread: Runtime.runIfWaitingForDebugger 142 ################################################################################################################ 143 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", main_thread) 144 ################################################################################################################ 145 # main thread: Debugger.scriptParsed 146 ################################################################################################################ 147 response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread) 148 assert response['params']['url'] == self.config['file_path']['entry_ability'] 149 assert response['params']['endLine'] == 0 150 ################################################################################################################ 151 # main thread: Debugger.paused 152 ################################################################################################################ 153 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 154 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['entry_ability'] 155 assert response['params']['reason'] == 'Break on start' 156 ################################################################################################################ 157 # main thread: Debugger.resume 158 ################################################################################################################ 159 await self.debugger_impl.send("Debugger.resume", main_thread) 160 ################################################################################################################ 161 # main thread: Debugger.scriptParsed 162 ################################################################################################################ 163 response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread) 164 assert response['params']['url'] == self.config['file_path']['index'] 165 assert response['params']['endLine'] == 0 166 ################################################################################################################ 167 # main thread: Debugger.paused 168 ################################################################################################################ 169 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 170 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 171 assert response['params']['reason'] == 'Break on start' 172 ################################################################################################################ 173 # main thread: Debugger.removeBreakpointsByUrl 174 ################################################################################################################ 175 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['index']) 176 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", main_thread, params) 177 ################################################################################################################ 178 # main thread: Debugger.getPossibleAndSetBreakpointByUrl 179 ################################################################################################################ 180 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=12), 181 debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=53), 182 debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=57)] 183 params = debugger.SetBreakpointsLocations(locations) 184 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 185 main_thread, params) 186 assert response['result']['locations'][0]['id'] == 'id:12:0:' + self.config['file_path']['index'] 187 assert response['result']['locations'][1]['id'] == 'id:53:0:' + self.config['file_path']['index'] 188 assert response['result']['locations'][2]['id'] == 'id:57:0:' + self.config['file_path']['index'] 189 ################################################################################################################ 190 # main thread: Debugger.resume 191 ################################################################################################################ 192 await self.debugger_impl.send("Debugger.resume", main_thread) 193 ################################################################################################################ 194 # main thread: Debugger.paused, hit breakpoint 195 ################################################################################################################ 196 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 197 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 198 assert response['params']['hitBreakpoints'] == ['id:12:4:' + self.config['file_path']['index']] 199 ################################################################################################################ 200 # main thread: Debugger.stepOut 201 ################################################################################################################ 202 await self.debugger_impl.send("Debugger.stepOut", main_thread) 203 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 204 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 205 assert response['params']['hitBreakpoints'] == ['id:12:4:' + self.config['file_path']['index']] 206 ################################################################################################################ 207 # worker thread: connect the debugger server 208 ################################################################################################################ 209 worker_thread_1 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 210 logging.info(f'Connect to the debugger server of instance: {worker_thread_1.instance_id}') 211 ################################################################################################################ 212 # main thread: Debugger.resume 213 ################################################################################################################ 214 await self.debugger_impl.send("Debugger.resume", main_thread) 215 ################################################################################################################ 216 # worker thread: connect the debugger server 217 ################################################################################################################ 218 worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 219 logging.info(f'Connect to the debugger server of instance: {worker_thread_2.instance_id}') 220 ################################################################################################################ 221 # worker thread: Runtime.enable 222 ################################################################################################################ 223 await self.runtime_impl.send("Runtime.enable", worker_thread_1) 224 await self.runtime_impl.send("Runtime.enable", worker_thread_2) 225 ################################################################################################################ 226 # worker thread: Debugger.enable 227 ################################################################################################################ 228 await self.debugger_impl.send("Debugger.enable", worker_thread_1) 229 await self.debugger_impl.send("Debugger.enable", worker_thread_2) 230 ################################################################################################################ 231 # worker thread: Runtime.runIfWaitingForDebugger 232 ################################################################################################################ 233 # worker thread 1: Runtime.runIfWaitingForDebugger 234 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_1) 235 # worker thread 1: Debugger.scriptParsed 236 response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_1) 237 assert response['params']['url'] == self.config['file_path']['worker'] 238 assert response['params']['endLine'] == 0 239 # worker thread 1: Debugger.paused 240 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1) 241 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 242 assert response['params']['reason'] == 'Break on start' 243 # worker thread 2: Runtime.runIfWaitingForDebugger 244 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2) 245 # worker thread 2: Debugger.scriptParsed 246 response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_2) 247 assert response['params']['url'] == self.config['file_path']['worker'] 248 assert response['params']['endLine'] == 0 249 # worker thread 2: Debugger.paused 250 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2) 251 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 252 assert response['params']['reason'] == 'Break on start' 253 ################################################################################################################ 254 # worker thread: Debugger.removeBreakpointsByUrl 255 ################################################################################################################ 256 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker']) 257 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_1, params) 258 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_2, params) 259 ################################################################################################################ 260 # worker thread: Debugger.getPossibleAndSetBreakpointByUrl 261 ################################################################################################################ 262 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)] 263 params = debugger.SetBreakpointsLocations(locations) 264 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 265 worker_thread_1, params) 266 assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker'] 267 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 268 worker_thread_2, params) 269 assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker'] 270 ################################################################################################################ 271 # worker thread: Debugger.resume 272 ################################################################################################################ 273 await self.debugger_impl.send("Debugger.resume", worker_thread_1) 274 await self.debugger_impl.send("Debugger.resume", worker_thread_2) 275 ################################################################################################################ 276 # main thread: click on the screen 277 ################################################################################################################ 278 Application.click_on_middle() 279 ################################################################################################################ 280 # main thread: Debugger.paused, hit breakpoint 281 ################################################################################################################ 282 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 283 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 284 assert response['params']['hitBreakpoints'] == ['id:53:16:' + self.config['file_path']['index']] 285 ################################################################################################################ 286 # worker thread: destroy instance 287 ################################################################################################################ 288 # worker thread 2 destroyed 289 response = await self.debugger_impl.destroy_instance() 290 assert response['instanceId'] == worker_thread_2.instance_id 291 ################################################################################################################ 292 # main thread: Debugger.stepInto 293 ################################################################################################################ 294 await self.debugger_impl.send("Debugger.stepInto", main_thread) 295 await self.debugger_impl.recv("Debugger.paused", main_thread) 296 ################################################################################################################ 297 # main thread: Runtime.getProperties 298 ################################################################################################################ 299 params = runtime.GetPropertiesParams('0', True, False, True) 300 response = await self.runtime_impl.send("Runtime.getProperties", main_thread, params) 301 assert response['result']['result'][0]['name'] == 'set message' 302 assert response['result']['result'][0]['value']['type'] == 'function' 303 assert response['result']['result'][1]['name'] == 'newValue' 304 assert response['result']['result'][1]['value']['type'] == 'string' 305 ################################################################################################################ 306 # main thread: Debugger.resume 307 ################################################################################################################ 308 await self.debugger_impl.send("Debugger.resume", main_thread) 309 ################################################################################################################ 310 # main thread: Debugger.paused, hit breakpoint 311 ################################################################################################################ 312 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 313 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 314 assert response['params']['hitBreakpoints'] == ['id:57:20:' + self.config['file_path']['index']] 315 ################################################################################################################ 316 # worker thread: connect the debugger server 317 ################################################################################################################ 318 worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 319 logging.info(f'Connect to the debugger server of instance: {worker_thread_2.instance_id}') 320 ################################################################################################################ 321 # worker thread: Runtime.enable 322 ################################################################################################################ 323 await self.runtime_impl.send("Runtime.enable", worker_thread_2) 324 ################################################################################################################ 325 # worker thread: Debugger.enable 326 ################################################################################################################ 327 await self.debugger_impl.send("Debugger.enable", worker_thread_2) 328 ################################################################################################################ 329 # worker thread: Runtime.runIfWaitingForDebugger 330 ################################################################################################################ 331 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2) 332 # worker thread: Debugger.scriptParsed 333 response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_2) 334 assert response['params']['url'] == self.config['file_path']['worker'] 335 assert response['params']['endLine'] == 0 336 # worker thread: Debugger.paused 337 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2) 338 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 339 assert response['params']['reason'] == 'Break on start' 340 ################################################################################################################ 341 # worker thread: Debugger.removeBreakpointsByUrl 342 ################################################################################################################ 343 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker']) 344 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_2, params) 345 ################################################################################################################ 346 # worker thread: Debugger.getPossibleAndSetBreakpointByUrl 347 ################################################################################################################ 348 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)] 349 params = debugger.SetBreakpointsLocations(locations) 350 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 351 worker_thread_2, params) 352 assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker'] 353 ################################################################################################################ 354 # worker thread: Debugger.resume 355 ################################################################################################################ 356 await self.debugger_impl.send("Debugger.resume", worker_thread_2) 357 ################################################################################################################ 358 # main thread: Debugger.stepOut 359 ################################################################################################################ 360 await self.debugger_impl.send("Debugger.stepOut", main_thread) 361 # main thread: Debugger.paused 362 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 363 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 364 assert response['params']['reason'] == 'other' 365 assert response['params']['hitBreakpoints'] == ['id:57:20:' + self.config['file_path']['index']] 366 # worker thread: Debugger.paused 367 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1) 368 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 369 assert response['params']['reason'] == 'other' 370 assert response['params']['hitBreakpoints'] == ['id:11:4:' + self.config['file_path']['worker']] 371 ################################################################################################################ 372 # worker thread: Runtime.getProperties 373 ################################################################################################################ 374 params = runtime.GetPropertiesParams('0', True, False, True) 375 response = await self.runtime_impl.send("Runtime.getProperties", worker_thread_1, params) 376 assert response['result']['result'][0]['name'] == '' 377 assert response['result']['result'][0]['value']['type'] == 'function' 378 assert response['result']['result'][1]['name'] == 'e' 379 assert response['result']['result'][1]['value']['type'] == 'object' 380 ################################################################################################################ 381 # worker thread: Debugger.stepOut 382 ################################################################################################################ 383 await self.debugger_impl.send("Debugger.stepOut", worker_thread_1) 384 ################################################################################################################ 385 # worker thread: Debugger.disable 386 ################################################################################################################ 387 await self.debugger_impl.send("Debugger.disable", worker_thread_1) 388 ################################################################################################################ 389 # main thread: Debugger.stepInto 390 ################################################################################################################ 391 await self.debugger_impl.send("Debugger.stepInto", main_thread) 392 # main thread: Debugger.paused 393 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 394 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 395 assert response['params']['reason'] == 'other' 396 assert response['params']['hitBreakpoints'] == [] 397 # worker thread: Debugger.paused 398 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2) 399 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 400 assert response['params']['reason'] == 'other' 401 assert response['params']['hitBreakpoints'] == ['id:11:4:' + self.config['file_path']['worker']] 402 ################################################################################################################ 403 # worker thread: Debugger.resume 404 ################################################################################################################ 405 await self.debugger_impl.send("Debugger.resume", worker_thread_2) 406 ################################################################################################################ 407 # worker thread: Debugger.disable 408 ################################################################################################################ 409 await self.debugger_impl.send("Debugger.disable", worker_thread_2) 410 ################################################################################################################ 411 # main thread: Debugger.resume 412 ################################################################################################################ 413 await self.debugger_impl.send("Debugger.resume", main_thread) 414 ################################################################################################################ 415 # worker thread: destroy instance 416 ################################################################################################################ 417 response = await self.debugger_impl.destroy_instance() 418 assert response['instanceId'] != self.config['pid'] 419 response = await self.debugger_impl.destroy_instance() 420 assert response['instanceId'] != self.config['pid'] 421 ################################################################################################################ 422 # main thread: Debugger.disable 423 ################################################################################################################ 424 await self.debugger_impl.send("Debugger.disable", main_thread) 425 ################################################################################################################ 426 # close the websocket connections 427 ################################################################################################################ 428 await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close') 429 await websocket.send_msg_to_connect_server('close') 430 ################################################################################################################