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