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