• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 TestAttachWorkerStep:
35    """
36    测试用例:多实例 attach 调试,执行单步操作进行跨线程通信
37    测试步骤:
38        1.  拉起应用,attach 主线程
39        2.  连接 connect server 和主线程 debugger server
40        3.  创建两个子线程,连接子线程 debugger server
41        4.  所有线程使能 Runtime 和 Debugger
42        5.  主线程 Index.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl)
43        6.  触发点击事件,主线程命中断点
44        7.  销毁其中一个子线程
45        8.  子线程 Worker.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl)
46        9.  主线程 resume,暂停在下一断点(Debugger.resume)
47        10. 重新创建一个子线程,使能并设置断点
48        11. 主线程 resume,发送消息给子线程,主线程暂停在下一断点(Debugger.resume)
49        12. 子线程命中断点后 stepOut,发消息给主线程(Debugger.stepOut)
50        13. 主线程 stepOver,发送消息给另一子线程,主线程暂停在下一行(Debugger.stepOver)
51        14. 子线程命中断点后 resume,发消息给主线程(Debugger.resume)
52        15. 销毁所有子线程,对应的 debugger server 连接断开
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            .OnClick(() => {
63                terminateWorker()
64                newWorker()
65                for (let i = 0; i < workerIndex; i++) {
66                    workers[i].postMessage("hello world")
67                }
68                while (workerIndex) {
69                    terminateWorker()
70                }
71            })
72        Worker.ets
73            const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
74            workerPort.onmessage = (e: MessageEvents) => {
75                workerPort.postMessage(e.data);
76            }
77    """
78
79    def setup_method(self):
80        logging.info('Start running TestAttachWorkerStep: setup')
81
82        self.log_path = rf'{os.path.dirname(__file__)}\..\log'
83        self.hilog_file_name = 'test_attach_worker_step.hilog.txt'
84        self.id_generator = Utils.message_id_generator()
85
86        # receive the hilog before the test start
87        Utils.clear_fault_log()
88        self.hilog_process, self.write_thread = Utils.save_hilog(log_path=self.log_path,
89                                                                 file_name=self.hilog_file_name,
90                                                                 debug_on=True)
91
92    def teardown_method(self):
93        Application.uninstall(self.config['bundle_name'])
94
95        # terminate the hilog receive process after the test done
96        time.sleep(3)
97        self.hilog_process.stdout.close()
98        self.hilog_process.terminate()
99        self.hilog_process.wait()
100        self.write_thread.join()
101
102        Utils.save_fault_log(log_path=self.log_path)
103        logging.info('TestAttachWorkerStep done')
104
105    def test(self, test_suite_worker_01):
106        logging.info('Start running TestAttachWorkerStep: test')
107        self.config = test_suite_worker_01
108        websocket = self.config['websocket']
109        taskpool = self.config['taskpool']
110        pid = self.config['pid']
111        self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, websocket)
112        self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, websocket)
113
114        Application.attach(self.config['bundle_name'])
115        taskpool.submit(websocket.main_task(taskpool, self.procedure, pid))
116        taskpool.await_taskpool()
117        taskpool.task_join()
118        if taskpool.task_exception:
119            raise taskpool.task_exception
120
121    async def procedure(self, websocket):
122        ################################################################################################################
123        # main thread: connect the debugger server
124        ################################################################################################################
125        main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True)
126        logging.info(f'Connect to the debugger server of instance: {main_thread.instance_id}')
127        ################################################################################################################
128        # worker thread: connect the debugger server
129        ################################################################################################################
130        worker_thread_1 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False)
131        logging.info(f'Connect to the debugger server of instance: {worker_thread_1.instance_id}')
132        worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False)
133        logging.info(f'Connect to the debugger server of instance: {worker_thread_2.instance_id}')
134        ################################################################################################################
135        # worker thread: Runtime.enable
136        ################################################################################################################
137        await self.runtime_impl.send("Runtime.enable", worker_thread_1)
138        await self.runtime_impl.send("Runtime.enable", worker_thread_2)
139        ################################################################################################################
140        # worker thread: Debugger.enable
141        ################################################################################################################
142        await self.debugger_impl.send("Debugger.enable", worker_thread_1)
143        await self.debugger_impl.send("Debugger.enable", worker_thread_2)
144        ################################################################################################################
145        # worker thread: Runtime.runIfWaitingForDebugger
146        ################################################################################################################
147        await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_1)
148        await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2)
149        ################################################################################################################
150        # main thread: Runtime.enable
151        ################################################################################################################
152        await self.runtime_impl.send("Runtime.enable", main_thread)
153        ################################################################################################################
154        # main thread: Debugger.enable
155        ################################################################################################################
156        await self.debugger_impl.send("Debugger.enable", main_thread)
157        ################################################################################################################
158        # main thread: Runtime.runIfWaitingForDebugger
159        ################################################################################################################
160        await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", main_thread)
161        ################################################################################################################
162        # main thread: Debugger.removeBreakpointsByUrl
163        ################################################################################################################
164        params = debugger.RemoveBreakpointsUrl(self.config['file_path']['index'])
165        await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", main_thread, params)
166        ################################################################################################################
167        # main thread: Debugger.getPossibleAndSetBreakpointByUrl
168        ################################################################################################################
169        locations = [debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=53),
170                     debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=57)]
171        params = debugger.SetBreakpointsLocations(locations)
172        response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl",
173                                                 main_thread, params)
174        assert response['result']['locations'][0]['id'] == 'id:53:0:' + self.config['file_path']['index']
175        assert response['result']['locations'][1]['id'] == 'id:57:0:' + self.config['file_path']['index']
176        ################################################################################################################
177        # main thread: click on the screen
178        ################################################################################################################
179        Application.click_on_middle()
180        ################################################################################################################
181        # main thread: Debugger.paused, hit breakpoint
182        ################################################################################################################
183        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
184        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
185        assert response['params']['hitBreakpoints'] == ['id:53:16:' + self.config['file_path']['index']]
186        ################################################################################################################
187        # worker thread: destroy instance
188        ################################################################################################################
189        response = await self.debugger_impl.destroy_instance()
190        if response['instanceId'] == worker_thread_1.instance_id:
191            worker_thread_1 = worker_thread_2
192        ################################################################################################################
193        # worker thread: Debugger.removeBreakpointsByUrl
194        ################################################################################################################
195        params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker'])
196        await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_1, params)
197        ################################################################################################################
198        # worker thread: Debugger.getPossibleAndSetBreakpointByUrl
199        ################################################################################################################
200        locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)]
201        params = debugger.SetBreakpointsLocations(locations)
202        response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl",
203                                                 worker_thread_1, params)
204        assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker']
205        ################################################################################################################
206        # main thread: Debugger.resume
207        ################################################################################################################
208        await self.debugger_impl.send("Debugger.resume", main_thread)
209        # main thread: Debugger.paused, hit breakpoint
210        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
211        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
212        assert response['params']['hitBreakpoints'] == ['id:57:20:' + self.config['file_path']['index']]
213        ################################################################################################################
214        # worker thread: connect the debugger server
215        ################################################################################################################
216        worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False)
217        logging.info(f'Connect to the debugger server of instance: {worker_thread_2.instance_id}')
218        ################################################################################################################
219        # worker thread: Runtime.enable
220        ################################################################################################################
221        await self.runtime_impl.send("Runtime.enable", worker_thread_2)
222        ################################################################################################################
223        # worker thread: Debugger.enable
224        ################################################################################################################
225        await self.debugger_impl.send("Debugger.enable", worker_thread_2)
226        ################################################################################################################
227        # worker thread: Runtime.runIfWaitingForDebugger
228        ################################################################################################################
229        await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2)
230        # worker thread: Debugger.scriptParsed
231        response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_2)
232        assert response['params']['url'] == self.config['file_path']['worker']
233        assert response['params']['endLine'] == 0
234        # worker thread: Debugger.paused
235        response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2)
236        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker']
237        assert response['params']['reason'] == 'Break on start'
238        ################################################################################################################
239        # worker thread: Debugger.removeBreakpointsByUrl
240        ################################################################################################################
241        params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker'])
242        await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_2, params)
243        ################################################################################################################
244        # worker thread: Debugger.getPossibleAndSetBreakpointByUrl
245        ################################################################################################################
246        locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)]
247        params = debugger.SetBreakpointsLocations(locations)
248        response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl",
249                                                 worker_thread_2, params)
250        assert response['result']['locations'][0]['id'] == 'id:11:0:' + self.config['file_path']['worker']
251        ################################################################################################################
252        # worker thread: Debugger.resume
253        ################################################################################################################
254        await self.debugger_impl.send("Debugger.resume", worker_thread_2)
255        ################################################################################################################
256        # main thread: Debugger.resume
257        ################################################################################################################
258        await self.debugger_impl.send("Debugger.resume", main_thread)
259        # main thread: Debugger.paused
260        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
261        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
262        assert response['params']['reason'] == 'other'
263        assert response['params']['hitBreakpoints'] == ['id:57:20:' + self.config['file_path']['index']]
264        # worker thread: Debugger.paused
265        response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1)
266        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker']
267        assert response['params']['reason'] == 'Break on start'
268        assert response['params']['hitBreakpoints'] == []
269        ################################################################################################################
270        # worker thread: Debugger.resume
271        ################################################################################################################
272        await self.debugger_impl.send("Debugger.resume", worker_thread_1)
273        # worker thread: Debugger.paused
274        response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1)
275        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker']
276        assert response['params']['reason'] == 'other'
277        assert response['params']['hitBreakpoints'] == ['id:11:4:' + self.config['file_path']['worker']]
278        ################################################################################################################
279        # worker thread: Debugger.stepOut
280        ################################################################################################################
281        await self.debugger_impl.send("Debugger.stepOut", worker_thread_1)
282        ################################################################################################################
283        # worker thread: Debugger.disable
284        ################################################################################################################
285        await self.debugger_impl.send("Debugger.disable", worker_thread_1)
286        ################################################################################################################
287        # main thread: Debugger.stepOver
288        ################################################################################################################
289        await self.debugger_impl.send("Debugger.stepOver", main_thread)
290        # main thread: Debugger.paused
291        response = await self.debugger_impl.recv("Debugger.paused", main_thread)
292        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['index']
293        assert response['params']['reason'] == 'other'
294        assert response['params']['hitBreakpoints'] == []
295        # worker thread: Debugger.paused
296        response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2)
297        assert response['params']['callFrames'][0]['url'] == self.config['file_path']['worker']
298        assert response['params']['reason'] == 'other'
299        assert response['params']['hitBreakpoints'] == ['id:11:4:' + self.config['file_path']['worker']]
300        ################################################################################################################
301        # worker thread: Debugger.resume
302        ################################################################################################################
303        await self.debugger_impl.send("Debugger.resume", worker_thread_2)
304        ################################################################################################################
305        # worker thread: Debugger.disable
306        ################################################################################################################
307        await self.debugger_impl.send("Debugger.disable", worker_thread_2)
308        ################################################################################################################
309        # main thread: Debugger.resume
310        ################################################################################################################
311        await self.debugger_impl.send("Debugger.resume", main_thread)
312        ################################################################################################################
313        # worker thread: destroy instance
314        ################################################################################################################
315        response = await self.debugger_impl.destroy_instance()
316        assert response['instanceId'] != self.config['pid']
317        response = await self.debugger_impl.destroy_instance()
318        assert response['instanceId'] != self.config['pid']
319        ################################################################################################################
320        # main thread: Debugger.disable
321        ################################################################################################################
322        await self.debugger_impl.send("Debugger.disable", main_thread)
323        ################################################################################################################
324        # close the websocket connections
325        ################################################################################################################
326        await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close')
327        await websocket.send_msg_to_connect_server('close')
328        ################################################################################################################