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 TestWorkerGetProperties: 35 """ 36 测试用例:多实例 getProperties 调试 37 测试步骤: 38 1. 连接 connect server 和主线程 debugger server 39 2. 主线程使能 Runtime 和 Debugger 40 3. 主线程 Index.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) 41 4. 主线程 resume,暂停在下一断点(Debugger.resume) 42 5. 创建子线程,连接子线程 debugger server 43 6. 子线程使能 Runtime 和 Debugger 44 7. 子线程 Worker.ets 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) 45 8. 主线程 resume,发送消息给子线程,子线程暂停在断点(Debugger.resume) 46 9. 子线程 getProperties 获取 local/closure/module/global作用域变量信息(Debugger.getProperties) 47 10. 销毁子线程,对应的 debugger server 连接断开 48 11. 关闭主线程 debugger server 和 connect server 连接 49 关键代码: 50 Index.ets 51 let myWorker = new worker.ThreadWorker("entry/ets/workers/Worker.ets") 52 myWorker.postMessage("hello world") 53 .OnClick(() => { 54 myWorker.terminate() 55 }) 56 Worker.ets 57 const workerPort: ThreadWorkerGlobalScope = worker.workerPort; 58 let closureBoolean = false 59 ...... // 定义不同的闭包变量 60 globalThis.globalBool = new Boolean(closureBoolean) 61 ...... // 定义不同的全局变量 62 workerPort.onmessage = (e: MessageEvents) => { 63 let localNull = null 64 ...... // 定义不同的局部变量 65 workerPort.postMessage(e.data) 66 } 67 """ 68 69 def setup_method(self): 70 logging.info('Start running TestWorkerGetProperties: setup') 71 72 self.log_path = rf'{os.path.dirname(__file__)}\..\log' 73 self.hilog_file_name = 'test_worker_get_properties.hilog.txt' 74 self.id_generator = Utils.message_id_generator() 75 76 # receive the hilog before the test start 77 Utils.clear_fault_log() 78 self.hilog_process, self.write_thread = Utils.save_hilog(log_path=self.log_path, 79 file_name=self.hilog_file_name, 80 debug_on=True) 81 82 def teardown_method(self): 83 Application.uninstall(self.config['bundle_name']) 84 85 # terminate the hilog receive process after the test done 86 time.sleep(3) 87 self.hilog_process.stdout.close() 88 self.hilog_process.terminate() 89 self.hilog_process.wait() 90 self.write_thread.join() 91 92 Utils.save_fault_log(log_path=self.log_path) 93 logging.info('TestWorkerGetProperties done') 94 95 def test(self, test_suite_worker_04_debug): 96 logging.info('Start running TestWorkerGetProperties: test') 97 self.config = test_suite_worker_04_debug 98 websocket = self.config['websocket'] 99 taskpool = self.config['taskpool'] 100 pid = self.config['pid'] 101 self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, websocket) 102 self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, websocket) 103 104 taskpool.submit(websocket.main_task(taskpool, self.procedure, pid)) 105 taskpool.await_taskpool() 106 taskpool.task_join() 107 if taskpool.task_exception: 108 raise taskpool.task_exception 109 110 async def procedure(self, websocket): 111 ################################################################################################################ 112 # main thread: connect the debugger server 113 ################################################################################################################ 114 main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True) 115 logging.info(f'Connect to the debugger server of instance: {main_thread.instance_id}') 116 ################################################################################################################ 117 # main thread: Runtime.enable 118 ################################################################################################################ 119 await self.runtime_impl.send("Runtime.enable", main_thread) 120 ################################################################################################################ 121 # main thread: Debugger.enable 122 ################################################################################################################ 123 await self.debugger_impl.send("Debugger.enable", main_thread) 124 ################################################################################################################ 125 # main thread: Runtime.runIfWaitingForDebugger 126 ################################################################################################################ 127 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", main_thread) 128 ################################################################################################################ 129 # main thread: Debugger.scriptParsed 130 ################################################################################################################ 131 response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread) 132 assert response['params']['url'] == self.config['file_path']['entry_ability'] 133 assert response['params']['endLine'] == 0 134 ################################################################################################################ 135 # main thread: Debugger.paused 136 ################################################################################################################ 137 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 138 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['entry_ability'] 139 assert response['params']['reason'] == 'Break on start' 140 ################################################################################################################ 141 # main thread: Debugger.resume 142 ################################################################################################################ 143 await self.debugger_impl.send("Debugger.resume", 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']['index'] 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']['index'] 155 assert response['params']['reason'] == 'Break on start' 156 ################################################################################################################ 157 # main thread: Debugger.removeBreakpointsByUrl 158 ################################################################################################################ 159 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['index']) 160 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", main_thread, params) 161 ################################################################################################################ 162 # main thread: Debugger.getPossibleAndSetBreakpointByUrl 163 ################################################################################################################ 164 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=12)] 165 params = debugger.SetBreakpointsLocations(locations) 166 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 167 main_thread, params) 168 assert response['result']['locations'][0]['id'] == 'id:12:0:' + self.config['file_path']['index'] 169 ################################################################################################################ 170 # main thread: Debugger.resume 171 ################################################################################################################ 172 await self.debugger_impl.send("Debugger.resume", main_thread) 173 ################################################################################################################ 174 # main thread: Debugger.paused, hit breakpoint 175 ################################################################################################################ 176 response = await self.debugger_impl.recv("Debugger.paused", main_thread) 177 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index'] 178 assert response['params']['hitBreakpoints'] == ['id:12:0:' + self.config['file_path']['index']] 179 ################################################################################################################ 180 # worker thread: connect the debugger server 181 ################################################################################################################ 182 worker_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) 183 logging.info(f'Connect to the debugger server of instance: {worker_thread.instance_id}') 184 ################################################################################################################ 185 # worker thread: Runtime.enable 186 ################################################################################################################ 187 await self.runtime_impl.send("Runtime.enable", worker_thread) 188 ################################################################################################################ 189 # worker thread: Debugger.enable 190 ################################################################################################################ 191 await self.debugger_impl.send("Debugger.enable", worker_thread) 192 ################################################################################################################ 193 # worker thread: Runtime.runIfWaitingForDebugger 194 ################################################################################################################ 195 await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread) 196 # worker thread: Debugger.scriptParsed 197 response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread) 198 assert response['params']['url'] == self.config['file_path']['worker'] 199 assert response['params']['endLine'] == 0 200 # worker thread: Debugger.paused 201 response = await self.debugger_impl.recv("Debugger.paused", worker_thread) 202 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 203 assert response['params']['reason'] == 'Break on start' 204 ################################################################################################################ 205 # worker thread: Debugger.removeBreakpointsByUrl 206 ################################################################################################################ 207 params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker']) 208 await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread, params) 209 ################################################################################################################ 210 # worker thread: Debugger.getPossibleAndSetBreakpointByUrl 211 ################################################################################################################ 212 locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=116)] 213 params = debugger.SetBreakpointsLocations(locations) 214 response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", 215 worker_thread, params) 216 assert response['result']['locations'][0]['id'] == 'id:116:0:' + self.config['file_path']['worker'] 217 ################################################################################################################ 218 # worker thread: Debugger.resume 219 ################################################################################################################ 220 await self.debugger_impl.send("Debugger.resume", worker_thread) 221 ################################################################################################################ 222 # main thread: Debugger.resume 223 ################################################################################################################ 224 await self.debugger_impl.send("Debugger.resume", main_thread) 225 # worker thread: Debugger.paused 226 response = await self.debugger_impl.recv("Debugger.paused", worker_thread) 227 assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker'] 228 assert response['params']['reason'] == 'other' 229 assert response['params']['hitBreakpoints'] == ['id:116:4:' + self.config['file_path']['worker']] 230 ################################################################################################################ 231 # worker thread: Runtime.getProperties 232 ################################################################################################################ 233 params = runtime.GetPropertiesParams('0') 234 response = await self.runtime_impl.send("Runtime.getProperties", worker_thread, params) 235 variables = self.get_variables_from_result(response['result']['result'], 'local') 236 assert variables == {'localArrayList': 'ArrayList', 'localBigInt64Array': 'BigInt64Array', 237 'localBigUint64Array': 'BigUint64Array', 'localDataView': 'DataView(20)', 238 'localDeque': 'Deque', 'localFloat32Array': 'Float32Array', 239 'localFloat64Array': 'Float64Array', 'localHashMap': 'HashMap', 'localHashSet': 'HashSet', 240 'localInt16Array': 'Int16Array(0)', 'localInt32Array': 'Int32Array(0)', 241 'localInt8Array': 'Int8Array(0)', 'localLightWeightMap': 'LightWeightMap', 242 'localLightWeightSet': 'LightWeightSet', 'localLinkedList': 'LinkedList', 243 'localList': 'List', 'localMapIter': 'function entries( { [native code] }', 244 'localNull': 'null', 'localPerson': 'Person', 'localPlainArray': 'PlainArray', 245 'localPromise': 'Promise', 'localProxy': 'Proxy', 'localQueue': 'Queue', 246 'localSendableClass': 'SendableClass[Sendable]', 247 'localSharedArrayBuffer': 'SharedArrayBuffer(32)', 'localStack': 'Stack', 248 'localTreeMap': 'TreeMap', 'localTreeSet': 'TreeSet', 'localUint16Array': 'Uint16Array', 249 'localUint32Array': 'Uint32Array', 'localUint8Array': 'Uint8Array(3)', 250 'localUint8ClampedArray': 'Uint8ClampedArray', 'localUndefined': 'undefined', 251 'localWeakMap': 'WeakMap(0)', 'localWeakRef': 'WeakRef {}', 'localWeakSet': 'WeakSet(0)'} 252 ################################################################################################################ 253 # worker thread: Runtime.getProperties 254 ################################################################################################################ 255 params = runtime.GetPropertiesParams('1') 256 response = await self.runtime_impl.send("Runtime.getProperties", worker_thread, params) 257 variables = self.get_variables_from_result(response['result']['result'], 'closure') 258 assert variables == {'closureArray': 'Array(3)', 'closureArrayBuffer': 'Arraybuffer(20)', 259 'closureMap': 'Map(0)', 'closureNum': '20', 'closureRegExp': '/^ab+c/g', 260 'closureSet': "Set(1) {'closure'}", 'closureString': 'closure'} 261 ################################################################################################################ 262 # worker thread: Runtime.getProperties 263 ################################################################################################################ 264 params = runtime.GetPropertiesParams('2') 265 response = await self.runtime_impl.send("Runtime.getProperties", worker_thread, params) 266 variables = self.get_variables_from_result(response['result']['result'], '') 267 assert variables == {'ArrayList': 'function ArrayList( { [native code] }', 268 'Deque': 'function Deque( { [native code] }', 269 'HashMap': 'function HashMap( { [native code] }', 270 'HashSet': 'function HashSet( { [native code] }', 271 'LightWeightMap': 'function LightWeightMap( { [native code] }', 272 'LightWeightSet': 'function LightWeightSet( { [native code] }', 273 'LinkedList': 'function LinkedList( { [native code] }', 274 'List': 'function List( { [native code] }', 275 'PlainArray': 'function PlainArray( { [native code] }', 276 'Queue': 'function Queue( { [native code] }', 'Stack': 'function Stack( { [native code] }', 277 'TreeMap': 'function TreeMap( { [native code] }', 278 'TreeSet': 'function TreeSet( { [native code] }', 'worker': 'Object'} 279 ################################################################################################################ 280 # worker thread: Runtime.getProperties 281 ################################################################################################################ 282 params = runtime.GetPropertiesParams('3') 283 response = await self.runtime_impl.send("Runtime.getProperties", worker_thread, params) 284 variables = self.get_variables_from_result(response['result']['result'], 'global') 285 assert variables == {'globalArray': 'Array(3)', 'globalBigInt': '9007199254740991n', 286 'globalBool': 'Boolean{[[PrimitiveValue]]: false}', 287 'globalDate': 'Wed Aug 28 2024 02:41:00 GMT+0800', 288 'globalNum': 'Number{[[PrimitiveValue]]: 20}', 289 'globalObject': 'String{[[PrimitiveValue]]: globalObject}', 290 'globalStr': 'String{[[PrimitiveValue]]: globalStr}', 'globalThis': 'Object'} 291 ################################################################################################################ 292 # worker thread: Debugger.resume 293 ################################################################################################################ 294 await self.debugger_impl.send("Debugger.resume", worker_thread) 295 ################################################################################################################ 296 # main thread: click on the screen 297 ################################################################################################################ 298 Application.click_on_middle() 299 ################################################################################################################ 300 # worker thread: destroy instance 301 ################################################################################################################ 302 response = await self.debugger_impl.destroy_instance() 303 assert response['instanceId'] == worker_thread.instance_id 304 ################################################################################################################ 305 # main thread: Debugger.disable 306 ################################################################################################################ 307 await self.debugger_impl.send("Debugger.disable", main_thread) 308 ################################################################################################################ 309 # close the websocket connections 310 ################################################################################################################ 311 await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close') 312 await websocket.send_msg_to_connect_server('close') 313 ################################################################################################################ 314 315 def get_variables_from_result(self, result, prefix_name): 316 variables = {} 317 for var in result: 318 if var['name'].startswith(prefix_name): 319 name = var['name'] 320 value = var['value'] 321 description = value.get('description') 322 if description is not None: 323 index_of_at = description.find('@') 324 variables[name] = description if index_of_at == -1 else \ 325 (description[:index_of_at] + description[index_of_at + 9:].strip()) 326 else: 327 subtype = value.get('subtype') 328 variables[name] = subtype if subtype is not None else value.get('type') 329 return variables