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 TestWorkerExceptionBreakpoints01: 35 """ 36 测试用例:多实例 debug 调试异常断点 ALL 和 NONE 模式 37 测试步骤: 38 1. 连接 connect server 和主线程 debugger server 39 2. 主线程使能 Runtime 和 Debugger 40 3. 主线程 Index.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) 41 4. 主线程 resume,停在断点处(Debugger.resume) 42 5. 创建子线程1,连接 debugger server 43 6. 主线程 resume,停在断点处(Debugger.resume) 44 7. 创建子线程2,连接 debugger server 45 8. 所有子线程使能 Runtime 和 Debugger 46 9. 子线程1设置异常断点类型为 ALL(Debugger.setPauseOnExceptions) 47 10. 主线程 resume,停在断点处,子线程1停在第一个异常处(Debugger.resume) 48 11. 子线程1 resume,停在第二个异常断点处(Debugger.resume) 49 12. 子线程1 resume,抛出异常 50 13. 子线程2设置异常断点类型为 NONE(Debugger.setPauseOnExceptions) 51 14. 主线程 resume,子线程2抛出异常 52 15. 关闭所有线程 debugger server 和 connect server 连接 53 关键代码: 54 Index.ets 55 let workerIndex = 0 56 function newWorker() {} // 创建一个子线程, workerIndex++ 57 function terminateWorker() {} // 销毁一个子线程, workerIndex-- 58 for (let i = 0; i < 2; i++) { 59 newWorker() 60 } 61 for (let i = 0; i < workerIndex; i++) { 62 workers[i].postMessage("hello world") 63 } 64 Worker.ets 65 const workerPort: ThreadWorkerGlobalScope = worker.workerPort; 66 workerPort.onmessage = (e: MessageEvents) => { 67 workerPort.postMessage(e.data) 68 try { 69 throw new Error('[worker] caught error') 70 } catch (e) { 71 console.info('[worker] caught error') 72 } 73 throw new Error('[worker] uncaught error') 74 } 75 """ 76 77 def setup_method(self): 78 logging.info('Start running TestWorkerExceptionBreakpoints01: setup') 79 80 self.log_path = rf'{os.path.dirname(__file__)}\..\log' 81 self.hilog_file_name = 'test_worker_exception_breakpoints_01.hilog.txt' 82 self.id_generator = Utils.message_id_generator() 83 84 # receive the hilog before the test start 85 Utils.clear_fault_log() 86 self.hilog_process, self.write_thread = Utils.save_hilog(log_path=self.log_path, 87 file_name=self.hilog_file_name, 88 debug_on=True) 89 90 def teardown_method(self): 91 Application.uninstall(self.config['bundle_name']) 92 93 # terminate the hilog receive process after the test done 94 time.sleep(3) 95 self.hilog_process.stdout.close() 96 self.hilog_process.terminate() 97 self.hilog_process.wait() 98 self.write_thread.join() 99 100 Utils.save_fault_log(log_path=self.log_path) 101 logging.info('TestWorkerExceptionBreakpoints01 done') 102 103 def test(self, test_suite_worker_03_debug): 104 logging.info('Start running TestWorkerExceptionBreakpoints01: test') 105 self.config = test_suite_worker_03_debug 106 websocket = self.config['websocket'] 107 taskpool = self.config['taskpool'] 108 pid = self.config['pid'] 109 self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, websocket) 110 self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, websocket) 111 112 taskpool.submit(websocket.main_task(taskpool, self.procedure, pid)) 113 taskpool.await_taskpool() 114 taskpool.task_join() 115 if taskpool.task_exception: 116 raise taskpool.task_exception 117 118 async def procedure(self, websocket): 119 ################################################################################################################ 120 # main thread: connect the debugger server 121 ################################################################################################################ 122 main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True) 123 logging.info(f'Connect to the debugger server of instance: {main_thread.instance_id}') 124 ################################################################################################################ 125 # main thread: Runtime.enable 126 ################################################################################################################ 127 await self.runtime_impl.send("Runtime.enable", main_thread) 128 ################################################################################################################ 129 # main thread: Debugger.enable 130 ################################################################################################################ 131 await self.debugger_impl.send("Debugger.enable", main_thread) 132 ################################################################################################################ 133 # main thread: Runtime.runIfWaitingForDebugger 134 ################################################################################################################ 135 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", main_thread) 136 ################################################################################################################ 137 # main thread: Debugger.scriptParsed 138 ################################################################################################################ 139 response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread) 140 assert response['params']['url'] == self.config['file_path']['entry_ability'] 141 assert response['params']['endLine'] == 0 142 ################################################################################################################ 143 # main thread: Debugger.paused 144 ################################################################################################################ 145 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 146 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['entry_ability'] 147 assert response['params']['reason'] == 'Break on start' 148 ################################################################################################################ 149 # main thread: Debugger.resume 150 ################################################################################################################ 151 await self.debugger_impl.send("Debugger.resume", main_thread) 152 ################################################################################################################ 153 # main thread: Debugger.scriptParsed 154 ################################################################################################################ 155 response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread) 156 assert response['params']['url'] == self.config['file_path']['index'] 157 assert response['params']['endLine'] == 0 158 ################################################################################################################ 159 # main thread: Debugger.paused 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']['reason'] == 'Break on start' 164 ################################################################################################################ 165 # main thread: Debugger.removeBreakpointsByUrl 166 ################################################################################################################ 167 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['index']) 168 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", main_thread, params) 169 ################################################################################################################ 170 # main thread: Debugger.getPossibleAndSetBreakpointByUrl 171 ################################################################################################################ 172 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=16), 173 debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=84)] 174 params = debugger.SetBreakpointsLocations(locations) 175 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 176 main_thread, params) 177 assert response['result']['locations'][0]['id'] == 'id:16:0:' + self.config['file_path']['index'] 178 assert response['result']['locations'][1]['id'] == 'id:84:0:' + self.config['file_path']['index'] 179 ################################################################################################################ 180 # main thread: Debugger.resume 181 ################################################################################################################ 182 await self.debugger_impl.send("Debugger.resume", main_thread) 183 ################################################################################################################ 184 # main thread: Debugger.paused, hit breakpoint 185 ################################################################################################################ 186 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 187 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 188 assert response['params']['hitBreakpoints'] == ['id:84:1:' + self.config['file_path']['index']] 189 ################################################################################################################ 190 # worker thread: connect the debugger server 191 ################################################################################################################ 192 worker_thread_1 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 193 logging.info(f'Connect to the debugger server of instance: {worker_thread_1.instance_id}') 194 ################################################################################################################ 195 # main thread: Debugger.resume 196 ################################################################################################################ 197 await self.debugger_impl.send("Debugger.resume", main_thread) 198 # main thread: Debugger.paused 199 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 200 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 201 assert response['params']['hitBreakpoints'] == ['id:84:1:' + self.config['file_path']['index']] 202 ################################################################################################################ 203 # worker thread: connect the debugger server 204 ################################################################################################################ 205 worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 206 logging.info(f'Connect to the debugger server of instance: {worker_thread_2.instance_id}') 207 ################################################################################################################ 208 # worker thread: Runtime.enable 209 ################################################################################################################ 210 await self.runtime_impl.send("Runtime.enable", worker_thread_1) 211 await self.runtime_impl.send("Runtime.enable", worker_thread_2) 212 ################################################################################################################ 213 # worker thread: Debugger.enable 214 ################################################################################################################ 215 await self.debugger_impl.send("Debugger.enable", worker_thread_1) 216 await self.debugger_impl.send("Debugger.enable", worker_thread_2) 217 ################################################################################################################ 218 # worker thread: Runtime.runIfWaitingForDebugger 219 ################################################################################################################ 220 # worker thread 1: Runtime.runIfWaitingForDebugger 221 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_1) 222 # worker thread 1: Debugger.scriptParsed 223 response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_1) 224 assert response['params']['url'] == self.config['file_path']['worker'] 225 assert response['params']['endLine'] == 0 226 # worker thread 1: Debugger.paused 227 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1) 228 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 229 assert response['params']['reason'] == 'Break on start' 230 # worker thread 2: Runtime.runIfWaitingForDebugger 231 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2) 232 # worker thread 2: Debugger.scriptParsed 233 response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_2) 234 assert response['params']['url'] == self.config['file_path']['worker'] 235 assert response['params']['endLine'] == 0 236 # worker thread 2: Debugger.paused 237 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2) 238 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 239 assert response['params']['reason'] == 'Break on start' 240 ################################################################################################################ 241 # worker thread: Debugger.resume 242 ################################################################################################################ 243 await self.debugger_impl.send("Debugger.resume", worker_thread_1) 244 await self.debugger_impl.send("Debugger.resume", worker_thread_2) 245 ################################################################################################################ 246 # worker thread: Debugger.setPauseOnExceptions 247 ################################################################################################################ 248 params = debugger.PauseOnExceptionsState.ALL 249 await self.debugger_impl.send("Debugger.setPauseOnExceptions", worker_thread_1, params) 250 ################################################################################################################ 251 # main thread: Debugger.resume 252 ################################################################################################################ 253 await self.debugger_impl.send("Debugger.resume", main_thread) 254 # main thread: Debugger.paused 255 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 256 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 257 assert response['params']['reason'] == 'other' 258 assert response['params']['hitBreakpoints'] == ['id:16:4:' + self.config['file_path']['index']] 259 # worker thread: Debugger.paused 260 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1) 261 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 262 assert response['params']['reason'] == 'exception' 263 assert 'caught error' in response['params']['data']['description'] 264 ################################################################################################################ 265 # worker thread: Debugger.resume 266 ################################################################################################################ 267 await self.debugger_impl.send("Debugger.resume", worker_thread_1) 268 # worker thread: Debugger.paused 269 response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1) 270 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 271 assert response['params']['reason'] == 'exception' 272 assert 'uncaught error' in response['params']['data']['description'] 273 ################################################################################################################ 274 # worker thread: Debugger.resume 275 ################################################################################################################ 276 await self.debugger_impl.send("Debugger.resume", worker_thread_1) 277 ################################################################################################################ 278 # worker thread: Debugger.setPauseOnExceptions 279 ################################################################################################################ 280 params = debugger.PauseOnExceptionsState.NONE 281 await self.debugger_impl.send("Debugger.setPauseOnExceptions", worker_thread_2, params) 282 ################################################################################################################ 283 # main thread: Debugger.resume 284 ################################################################################################################ 285 await self.debugger_impl.send("Debugger.resume", main_thread) 286 ################################################################################################################ 287 # close the websocket connections 288 ################################################################################################################ 289 await websocket.send_msg_to_debugger_server(worker_thread_2.instance_id, worker_thread_2.send_msg_queue, 290 'close') 291 await websocket.send_msg_to_debugger_server(worker_thread_1.instance_id, worker_thread_1.send_msg_queue, 292 'close') 293 await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close') 294 await websocket.send_msg_to_connect_server('close') 295 ################################################################################################################