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(30)
34class TestDebug01:
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. 主线程 resume,发送消息给子线程,主线程暂停在下一断点(Debugger.resume)
54        17. 子线程命中断点后 getProperties(Runtime.getProperties)
55        18. 子线程 stepOut 发消息给主线程(Debugger.stepOut)
56        19. 主线程 stepOver,发送消息给另一子线程,主线程暂停在下一行(Debugger.stepOver)
57        20. 子线程命中断点后 resume,发消息给主线程(Debugger.resume)
58        21. 销毁所有子线程,对应的 debugger server 连接断开
59        22. 关闭主线程 debugger server 和 connect server 连接
60    """
61
62    def setup_method(self):
63        logging.info('Start running TestDebug01: setup')
64
65        self.log_path = rf'{os.path.dirname(__file__)}\..\log'
66        self.hilog_file_name = 'test_debug_01.hilog.txt'
67        self.id_generator = Utils.message_id_generator()
68
69        # receive the hilog before the test start
70        Utils.clear_fault_log()
71        self.hilog_process, self.write_thread = Utils.save_hilog(log_path=self.log_path,
72                                                                 file_name=self.hilog_file_name,
73                                                                 debug_on=True)
74
75    def teardown_method(self):
76        Application.uninstall(self.config['bundle_name'])
77
78        # terminate the hilog receive process after the test done
79        time.sleep(3)
80        self.hilog_process.stdout.close()
81        self.hilog_process.terminate()
82        self.hilog_process.wait()
83        self.write_thread.join()
84
85        Utils.save_fault_log(log_path=self.log_path)
86        logging.info('TestDebug01 done')
87
88    def test(self, test_suite_worker_01_debug):
89        logging.info('Start running TestDebug01: test')
90        self.config = test_suite_worker_01_debug
91        websocket = self.config['websocket']
92        taskpool = self.config['taskpool']
93        pid = self.config['pid']
94        self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, websocket)
95        self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, websocket)
96
97        taskpool.submit(websocket.main_task(taskpool, self.procedure, pid))
98        taskpool.await_taskpool()
99        taskpool.task_join()
100        if taskpool.task_exception:
101            raise taskpool.task_exception
102
103    async def procedure(self, websocket):
104        ################################################################################################################
105        # main thread: connect the debugger server
106        ################################################################################################################
107        main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True)
108        logging.info(f'Connect to the debugger server of instance: {main_thread.instance_id}')
109        ################################################################################################################
110        # main thread: Runtime.enable
111        ################################################################################################################
112        await self.runtime_impl.send("Runtime.enable", main_thread)
113        ################################################################################################################
114        # main thread: Debugger.enable
115        ################################################################################################################
116        await self.debugger_impl.send("Debugger.enable", main_thread)
117        ################################################################################################################
118        # main thread: Runtime.runIfWaitingForDebugger
119        ################################################################################################################
120        await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", main_thread)
121        ################################################################################################################
122        # main thread: Debugger.scriptParsed
123        ################################################################################################################
124        response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread)
125        assert response['params']['url'] == self.config['file_path']['entry_ability']
126        assert response['params']['endLine'] == 0
127        ################################################################################################################
128        # main thread: Debugger.paused
129        ################################################################################################################
130        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
131        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['entry_ability']
132        assert response['params']['reason'] == 'Break on start'
133        ################################################################################################################
134        # main thread: Debugger.resume
135        ################################################################################################################
136        await self.debugger_impl.send("Debugger.resume", 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']['index']
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']['index']
148        assert response['params']['reason'] == 'Break on start'
149        ################################################################################################################
150        # main thread: Debugger.removeBreakpointsByUrl
151        ################################################################################################################
152        params = debugger.RemoveBreakpointsUrl(self.config['file_path']['index'])
153        await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", main_thread, params)
154        ################################################################################################################
155        # main thread: Debugger.getPossibleAndSetBreakpointByUrl
156        ################################################################################################################
157        locations = [debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=12),
158                     debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=53),
159                     debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=57)]
160        params = debugger.SetBreakpointsLocations(locations)
161        response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl",
162                                                 main_thread, params)
163        assert response['result']['locations'][0]['id'] == 'id:12:0:' + self.config['file_path']['index']
164        assert response['result']['locations'][1]['id'] == 'id:53:0:' + self.config['file_path']['index']
165        assert response['result']['locations'][2]['id'] == 'id:57:0:' + self.config['file_path']['index']
166        ################################################################################################################
167        # main thread: Debugger.resume
168        ################################################################################################################
169        await self.debugger_impl.send("Debugger.resume", main_thread)
170        ################################################################################################################
171        # main thread: Debugger.paused, hit breakpoint
172        ################################################################################################################
173        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
174        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
175        assert response['params']['hitBreakpoints'] == ['id:12:4:' + self.config['file_path']['index']]
176        ################################################################################################################
177        # main thread: Debugger.stepOut
178        ################################################################################################################
179        await self.debugger_impl.send("Debugger.stepOut", main_thread)
180        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
181        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
182        assert response['params']['hitBreakpoints'] == ['id:12:4:' + self.config['file_path']['index']]
183        ################################################################################################################
184        # worker thread: connect the debugger server
185        ################################################################################################################
186        worker_thread_1 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False)
187        logging.info(f'Connect to the debugger server of instance: {worker_thread_1.instance_id}')
188        ################################################################################################################
189        # main thread: Debugger.resume
190        ################################################################################################################
191        await self.debugger_impl.send("Debugger.resume", main_thread)
192        ################################################################################################################
193        # worker thread: connect the debugger server
194        ################################################################################################################
195        worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False)
196        logging.info(f'Connect to the debugger server of instance: {worker_thread_2.instance_id}')
197        ################################################################################################################
198        # worker thread: Runtime.enable
199        ################################################################################################################
200        await self.runtime_impl.send("Runtime.enable", worker_thread_1)
201        await self.runtime_impl.send("Runtime.enable", worker_thread_2)
202        ################################################################################################################
203        # worker thread: Debugger.enable
204        ################################################################################################################
205        await self.debugger_impl.send("Debugger.enable", worker_thread_1)
206        await self.debugger_impl.send("Debugger.enable", worker_thread_2)
207        ################################################################################################################
208        # worker thread: Runtime.runIfWaitingForDebugger
209        ################################################################################################################
210        # worker thread 1: Runtime.runIfWaitingForDebugger
211        await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_1)
212        # worker thread 1: Debugger.scriptParsed
213        response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_1)
214        assert response['params']['url'] == self.config['file_path']['worker']
215        assert response['params']['endLine'] == 0
216        # worker thread 1: Debugger.paused
217        response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1)
218        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker']
219        assert response['params']['reason'] == 'Break on start'
220        # worker thread 2: Runtime.runIfWaitingForDebugger
221        await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2)
222        # worker thread 2: Debugger.scriptParsed
223        response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_2)
224        assert response['params']['url'] == self.config['file_path']['worker']
225        assert response['params']['endLine'] == 0
226        # worker thread 2: Debugger.paused
227        response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2)
228        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker']
229        assert response['params']['reason'] == 'Break on start'
230        ################################################################################################################
231        # worker thread: Debugger.removeBreakpointsByUrl
232        ################################################################################################################
233        params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker'])
234        await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_1, params)
235        await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_2, params)
236        ################################################################################################################
237        # worker thread: Debugger.getPossibleAndSetBreakpointByUrl
238        ################################################################################################################
239        locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)]
240        params = debugger.SetBreakpointsLocations(locations)
241        response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl",
242                                                 worker_thread_1, params)
243        assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker']
244        response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl",
245                                                 worker_thread_2, params)
246        assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker']
247        ################################################################################################################
248        # worker thread: Debugger.resume
249        ################################################################################################################
250        await self.debugger_impl.send("Debugger.resume", worker_thread_1)
251        await self.debugger_impl.send("Debugger.resume", worker_thread_2)
252        ################################################################################################################
253        # main thread: click on the screen
254        ################################################################################################################
255        Application.click_on_middle()
256        ################################################################################################################
257        # main thread: Debugger.paused, hit breakpoint
258        ################################################################################################################
259        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
260        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
261        assert response['params']['hitBreakpoints'] == ['id:53:16:' + self.config['file_path']['index']]
262        ################################################################################################################
263        # worker thread: destroy instance
264        ################################################################################################################
265        # worker thread 2 destroyed
266        response = await self.debugger_impl.destroy_instance()
267        assert response['instanceId'] == worker_thread_2.instance_id
268        ################################################################################################################
269        # main thread: Debugger.stepInto
270        ################################################################################################################
271        await self.debugger_impl.send("Debugger.stepInto", main_thread)
272        await self.debugger_impl.recv("Debugger.paused", main_thread)
273        ################################################################################################################
274        # main thread: Runtime.getProperties
275        ################################################################################################################
276        params = runtime.GetPropertiesParams('0', True, False, True)
277        response = await self.runtime_impl.send("Runtime.getProperties", main_thread, params)
278        assert response['result']['result'][0]['name'] == 'set message'
279        assert response['result']['result'][0]['value']['type'] == 'function'
280        assert response['result']['result'][1]['name'] == 'newValue'
281        assert response['result']['result'][1]['value']['type'] == 'string'
282        ################################################################################################################
283        # main thread: Debugger.resume
284        ################################################################################################################
285        await self.debugger_impl.send("Debugger.resume", main_thread)
286        ################################################################################################################
287        # main thread: Debugger.paused, hit breakpoint
288        ################################################################################################################
289        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
290        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
291        assert response['params']['hitBreakpoints'] == ['id:57:20:' + self.config['file_path']['index']]
292        ################################################################################################################
293        # worker thread: connect the debugger server
294        ################################################################################################################
295        worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False)
296        logging.info(f'Connect to the debugger server of instance: {worker_thread_2.instance_id}')
297        ################################################################################################################
298        # worker thread: Runtime.enable
299        ################################################################################################################
300        await self.runtime_impl.send("Runtime.enable", worker_thread_2)
301        ################################################################################################################
302        # worker thread: Debugger.enable
303        ################################################################################################################
304        await self.debugger_impl.send("Debugger.enable", worker_thread_2)
305        ################################################################################################################
306        # worker thread: Runtime.runIfWaitingForDebugger
307        ################################################################################################################
308        await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2)
309        # worker thread: Debugger.scriptParsed
310        response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_2)
311        assert response['params']['url'] == self.config['file_path']['worker']
312        assert response['params']['endLine'] == 0
313        # worker thread: Debugger.paused
314        response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2)
315        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker']
316        assert response['params']['reason'] == 'Break on start'
317        ################################################################################################################
318        # worker thread: Debugger.removeBreakpointsByUrl
319        ################################################################################################################
320        params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker'])
321        await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_2, params)
322        ################################################################################################################
323        # worker thread: Debugger.getPossibleAndSetBreakpointByUrl
324        ################################################################################################################
325        locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)]
326        params = debugger.SetBreakpointsLocations(locations)
327        response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl",
328                                                 worker_thread_2, params)
329        assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker']
330        ################################################################################################################
331        # worker thread: Debugger.resume
332        ################################################################################################################
333        await self.debugger_impl.send("Debugger.resume", worker_thread_2)
334        ################################################################################################################
335        # main thread: Debugger.stepOut
336        ################################################################################################################
337        await self.debugger_impl.send("Debugger.stepOut", main_thread)
338        # main thread: Debugger.paused
339        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
340        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
341        assert response['params']['reason'] == 'other'
342        assert response['params']['hitBreakpoints'] == ['id:57:20:' + self.config['file_path']['index']]
343        # worker thread: Debugger.paused
344        response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1)
345        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker']
346        assert response['params']['reason'] == 'other'
347        assert response['params']['hitBreakpoints'] == ['id:11:4:' + self.config['file_path']['worker']]
348        ################################################################################################################
349        # worker thread: Runtime.getProperties
350        ################################################################################################################
351        params = runtime.GetPropertiesParams('0', True, False, True)
352        response = await self.runtime_impl.send("Runtime.getProperties", worker_thread_1, params)
353        assert response['result']['result'][0]['name'] == ''
354        assert response['result']['result'][0]['value']['type'] == 'function'
355        assert response['result']['result'][1]['name'] == 'e'
356        assert response['result']['result'][1]['value']['type'] == 'object'
357        ################################################################################################################
358        # worker thread: Debugger.stepOut
359        ################################################################################################################
360        await self.debugger_impl.send("Debugger.stepOut", worker_thread_1)
361        ################################################################################################################
362        # worker thread: Debugger.disable
363        ################################################################################################################
364        await self.debugger_impl.send("Debugger.disable", worker_thread_1)
365        ################################################################################################################
366        # main thread: Debugger.stepInto
367        ################################################################################################################
368        await self.debugger_impl.send("Debugger.stepInto", main_thread)
369        # main thread: Debugger.paused
370        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
371        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
372        assert response['params']['reason'] == 'other'
373        assert response['params']['hitBreakpoints'] == []
374        # worker thread: Debugger.paused
375        response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2)
376        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker']
377        assert response['params']['reason'] == 'other'
378        assert response['params']['hitBreakpoints'] == ['id:11:4:' + self.config['file_path']['worker']]
379        ################################################################################################################
380        # worker thread: Debugger.resume
381        ################################################################################################################
382        await self.debugger_impl.send("Debugger.resume", worker_thread_2)
383        ################################################################################################################
384        # worker thread: Debugger.disable
385        ################################################################################################################
386        await self.debugger_impl.send("Debugger.disable", worker_thread_2)
387        ################################################################################################################
388        # main thread: Debugger.resume
389        ################################################################################################################
390        await self.debugger_impl.send("Debugger.resume", main_thread)
391        ################################################################################################################
392        # worker thread: destroy instance
393        ################################################################################################################
394        response = await self.debugger_impl.destroy_instance()
395        assert response['instanceId'] != self.config['pid']
396        response = await self.debugger_impl.destroy_instance()
397        assert response['instanceId'] != self.config['pid']
398        ################################################################################################################
399        # main thread: Debugger.disable
400        ################################################################################################################
401        await self.debugger_impl.send("Debugger.disable", main_thread)
402        ################################################################################################################
403        # close the websocket connections
404        ################################################################################################################
405        await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close')
406        await websocket.send_msg_to_connect_server('close')
407        ################################################################################################################