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 TestDebug04:
35    """
36    测试用例:多 task 实例 attach 调试
37    测试步骤:
38        1.  连接 connect server 和主线程 debugger server
39        2.  连接子线程 debugger server,用于执行 task 任务
40        3.  所有线程使能 Runtime 和 Debugger
41        4.  主线程 Index.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl)
42        5.  触发点击事件,主线程命中断点
43        6.  子线程 Index.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl)
44        7.  子线程 resume,命中断点(Debugger.resume)
45        8.  子线程 getProperties,返回给定对象的属性(Runtime.getProperties)
46        9.  子线程 stepOut,主线程命中断点(Debugger.stepOut)
47        10. 主线程 getProperties(Runtime.getProperties)
48        11. 主线程 resume(Debugger.resume)
49        11. 子线程命中断点后 resume(Debugger.resume)
50        12. 关闭所有线程 debugger server 和 connect server 连接
51    """
52
53    def setup_method(self):
54        logging.info('Start running TestDebug04: setup')
55
56        self.log_path = rf'{os.path.dirname(__file__)}\..\log'
57        self.hilog_file_name = 'test_debug_04.hilog.txt'
58        self.id_generator = Utils.message_id_generator()
59
60        # receive the hilog before the test start
61        Utils.clear_fault_log()
62        self.hilog_process, self.write_thread = Utils.save_hilog(log_path=self.log_path,
63                                                                 file_name=self.hilog_file_name,
64                                                                 debug_on=True)
65
66    def teardown_method(self):
67        Application.uninstall(self.config['bundle_name'])
68
69        # terminate the hilog receive process after the test done
70        time.sleep(3)
71        self.hilog_process.stdout.close()
72        self.hilog_process.terminate()
73        self.hilog_process.wait()
74        self.write_thread.join()
75
76        Utils.save_fault_log(log_path=self.log_path)
77        logging.info('TestDebug04 done')
78
79    def test(self, test_suite_taskpool_01):
80        logging.info('Start running TestDebug04: test')
81        self.config = test_suite_taskpool_01
82        websocket = self.config['websocket']
83        taskpool = self.config['taskpool']
84        pid = self.config['pid']
85        self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, websocket)
86        self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, websocket)
87
88        Application.attach(self.config['bundle_name'])
89        taskpool.submit(websocket.main_task(taskpool, self.procedure, pid))
90        taskpool.await_taskpool()
91        taskpool.task_join()
92        if taskpool.task_exception:
93            raise taskpool.task_exception
94
95    async def procedure(self, websocket):
96        ################################################################################################################
97        # main thread: connect the debugger server
98        ################################################################################################################
99        main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True)
100        logging.info(f'Connect to the debugger server of instance: {main_thread.instance_id}')
101        ################################################################################################################
102        # worker thread: connect the debugger server
103        ################################################################################################################
104        worker_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False)
105        logging.info(f'Connect to the debugger server of instance: {worker_thread.instance_id}')
106        ################################################################################################################
107        # main thread: Runtime.enable
108        ################################################################################################################
109        await self.runtime_impl.send("Runtime.enable", main_thread)
110        ################################################################################################################
111        # worker thread: Runtime.enable
112        ################################################################################################################
113        await self.runtime_impl.send("Runtime.enable", worker_thread)
114        ################################################################################################################
115        # main thread: Debugger.enable
116        ################################################################################################################
117        await self.debugger_impl.send("Debugger.enable", main_thread)
118        ################################################################################################################
119        # main thread: Runtime.runIfWaitingForDebugger
120        ################################################################################################################
121        await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", main_thread)
122        ################################################################################################################
123        # worker thread: Debugger.enable
124        ################################################################################################################
125        await self.debugger_impl.send("Debugger.enable", worker_thread)
126        ################################################################################################################
127        # worker thread: Runtime.runIfWaitingForDebugger
128        ################################################################################################################
129        await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread)
130        ################################################################################################################
131        # main thread: Debugger.removeBreakpointsByUrl
132        ################################################################################################################
133        params = debugger.RemoveBreakpointsUrl(self.config['file_path']['index'])
134        await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", main_thread, params)
135        ################################################################################################################
136        # main thread: Debugger.getPossibleAndSetBreakpointByUrl
137        ################################################################################################################
138        locations = [debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=10),
139                     debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=17),
140                     debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=25)]
141        params = debugger.SetBreakpointsLocations(locations)
142        response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl",
143                                                 main_thread, params)
144        assert response['result']['locations'][0]['id'] == 'id:10:0:' + self.config['file_path']['index']
145        assert response['result']['locations'][1]['id'] == 'id:17:0:' + self.config['file_path']['index']
146        assert response['result']['locations'][2]['id'] == 'id:25:0:' + self.config['file_path']['index']
147        ################################################################################################################
148        # main thread: click on the screen
149        ################################################################################################################
150        Application.click_on_middle()
151        ################################################################################################################
152        # worker thread: Debugger.scriptParsed
153        ################################################################################################################
154        response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread)
155        assert response['params']['url'] == self.config['file_path']['index']
156        assert response['params']['endLine'] == 0
157        # worker thread: Debugger.paused
158        response = await self.debugger_impl.recv("Debugger.paused", worker_thread)
159        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
160        assert response['params']['reason'] == 'Break on start'
161        ################################################################################################################
162        # worker thread: Debugger.removeBreakpointsByUrl
163        ################################################################################################################
164        params = debugger.RemoveBreakpointsUrl(self.config['file_path']['index'])
165        await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread, params)
166        ################################################################################################################
167        # worker thread: Debugger.getPossibleAndSetBreakpointByUrl
168        ################################################################################################################
169        locations = [debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=10),
170                     debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=17),
171                     debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=25)]
172        params = debugger.SetBreakpointsLocations(locations)
173        response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl",
174                                                 worker_thread, params)
175        assert response['result']['locations'][0]['id'] == 'id:10:0:' + self.config['file_path']['index']
176        assert response['result']['locations'][1]['id'] == 'id:17:0:' + self.config['file_path']['index']
177        assert response['result']['locations'][2]['id'] == 'id:25:0:' + self.config['file_path']['index']
178        ################################################################################################################
179        # worker thread: Debugger.resume
180        ################################################################################################################
181        await self.debugger_impl.send("Debugger.resume", worker_thread)
182        # worker thread: Debugger.paused
183        response = await self.debugger_impl.recv("Debugger.paused", worker_thread)
184        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
185        assert response['params']['reason'] == 'other'
186        assert response['params']['hitBreakpoints'] == ['id:10:14:' + self.config['file_path']['index']]
187        ################################################################################################################
188        # worker thread: Runtime.getProperties
189        ################################################################################################################
190        params = runtime.GetPropertiesParams('0')
191        response = await self.runtime_impl.send("Runtime.getProperties", worker_thread, params)
192        assert response['result']['result'][0]['name'] == 'add'
193        assert response['result']['result'][0]['value']['type'] == 'function'
194        ################################################################################################################
195        # worker thread: Debugger.stepOut
196        ################################################################################################################
197        await self.debugger_impl.send("Debugger.stepOut", worker_thread)
198        ################################################################################################################
199        # main thread: Debugger.paused, hit breakpoint
200        ################################################################################################################
201        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
202        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
203        assert response['params']['hitBreakpoints'] == ['id:25:4:' + self.config['file_path']['index']]
204        ################################################################################################################
205        # main thread: Runtime.getProperties
206        ################################################################################################################
207        params = runtime.GetPropertiesParams('0')
208        response = await self.runtime_impl.send("Runtime.getProperties", main_thread, params)
209        assert response['result']['result'][0]['name'] == 'taskpoolTest'
210        assert response['result']['result'][0]['value']['type'] == 'function'
211        assert response['result']['result'][1]['name'] == 'valueSub'
212        assert response['result']['result'][1]['value']['type'] == 'undefined'
213        assert response['result']['result'][2]['name'] == 'valueAdd'
214        assert response['result']['result'][2]['value']['type'] == 'number'
215        assert response['result']['result'][2]['value']['description'] == '300'
216        ################################################################################################################
217        # main thread: Debugger.resume
218        ################################################################################################################
219        await self.debugger_impl.send("Debugger.resume", main_thread)
220        ################################################################################################################
221        # worker thread: Debugger.paused
222        ################################################################################################################
223        response = await self.debugger_impl.recv("Debugger.paused", worker_thread)
224        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
225        assert response['params']['hitBreakpoints'] == ['id:17:14:' + self.config['file_path']['index']]
226        ################################################################################################################
227        # worker thread: Debugger.resume
228        ################################################################################################################
229        await self.debugger_impl.send("Debugger.resume", worker_thread)
230        ################################################################################################################
231        # worker thread: Debugger.disable
232        ################################################################################################################
233        await self.debugger_impl.send("Debugger.disable", worker_thread)
234        ################################################################################################################
235        # main thread: Debugger.disable
236        ################################################################################################################
237        await self.debugger_impl.send("Debugger.disable", main_thread)
238        ################################################################################################################
239        # close the websocket connections
240        ################################################################################################################
241        await websocket.send_msg_to_debugger_server(worker_thread.instance_id, worker_thread.send_msg_queue, 'close')
242        await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close')
243        await websocket.send_msg_to_connect_server('close')
244        ################################################################################################################