From 6ae6e839c0138b408d9b2465a2eb3c83d4a58b28 Mon Sep 17 00:00:00 2001 From: panmingxuan Date: Wed, 11 Jun 2025 10:33:49 +0800 Subject: [PATCH] =?UTF-8?q?testcases/toolchain=E6=B5=8B=E8=AF=95=E7=94=A8?= =?UTF-8?q?=E4=BE=8B=E5=90=8C=E6=AD=A56?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sync testcases/toolchain from YZ to BZ Issue: https://gitee.com/openharmony/arkcompiler_toolchain/issues/ICAVG8 Signed-off-by: panmingxuan --- .../toolchain/debug/TestWorkerMixedDebug.json | 15 + .../toolchain/debug/TestWorkerMixedDebug.py | 278 +++++++++++ .../debug/TestWorkerRunAfterProfiler.json | 15 + .../debug/TestWorkerRunAfterProfiler.py | 153 ++++++ .../debug/TestWorkerSmartStepInto.json | 15 + .../debug/TestWorkerSmartStepInto.py | 322 +++++++++++++ .../toolchain/debug/TestWorkerStep.json | 15 + .../toolchain/debug/TestWorkerStep.py | 446 ++++++++++++++++++ .../hotreload/TestHotReloadClass01.json | 15 + .../hotreload/TestHotReloadClass01.py | 141 ++++++ .../hotreload/TestHotReloadHarHsp01.json | 15 + .../hotreload/TestHotReloadHarHsp01.py | 120 +++++ .../hotreload/TestHotReloadMultiModule.json | 15 + .../hotreload/TestHotReloadMultiModule.py | 138 ++++++ .../hotreload/TestHotReloadPages01.json | 15 + .../hotreload/TestHotReloadPages01.py | 152 ++++++ 16 files changed, 1870 insertions(+) create mode 100644 test/autotest/testcases/toolchain/debug/TestWorkerMixedDebug.json create mode 100644 test/autotest/testcases/toolchain/debug/TestWorkerMixedDebug.py create mode 100644 test/autotest/testcases/toolchain/debug/TestWorkerRunAfterProfiler.json create mode 100644 test/autotest/testcases/toolchain/debug/TestWorkerRunAfterProfiler.py create mode 100644 test/autotest/testcases/toolchain/debug/TestWorkerSmartStepInto.json create mode 100644 test/autotest/testcases/toolchain/debug/TestWorkerSmartStepInto.py create mode 100644 test/autotest/testcases/toolchain/debug/TestWorkerStep.json create mode 100644 test/autotest/testcases/toolchain/debug/TestWorkerStep.py create mode 100644 test/autotest/testcases/toolchain/hotreload/TestHotReloadClass01.json create mode 100644 test/autotest/testcases/toolchain/hotreload/TestHotReloadClass01.py create mode 100644 test/autotest/testcases/toolchain/hotreload/TestHotReloadHarHsp01.json create mode 100644 test/autotest/testcases/toolchain/hotreload/TestHotReloadHarHsp01.py create mode 100644 test/autotest/testcases/toolchain/hotreload/TestHotReloadMultiModule.json create mode 100644 test/autotest/testcases/toolchain/hotreload/TestHotReloadMultiModule.py create mode 100644 test/autotest/testcases/toolchain/hotreload/TestHotReloadPages01.json create mode 100644 test/autotest/testcases/toolchain/hotreload/TestHotReloadPages01.py diff --git a/test/autotest/testcases/toolchain/debug/TestWorkerMixedDebug.json b/test/autotest/testcases/toolchain/debug/TestWorkerMixedDebug.json new file mode 100644 index 00000000..ad88fbbc --- /dev/null +++ b/test/autotest/testcases/toolchain/debug/TestWorkerMixedDebug.json @@ -0,0 +1,15 @@ +{ + "description": "Config for TestWorkerMixedDebug", + "environment": [ + { + "type": "device", + "label": "phone" + } + ], + "driver": { + "type": "DeviceTest", + "py_file": [ + "toolchain/debug/TestWorkerMixedDebug.py" + ] + } +} \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/debug/TestWorkerMixedDebug.py b/test/autotest/testcases/toolchain/debug/TestWorkerMixedDebug.py new file mode 100644 index 00000000..228974b0 --- /dev/null +++ b/test/autotest/testcases/toolchain/debug/TestWorkerMixedDebug.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 Huawei Device Co., Ltd. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +#================================================================== +#文 件 名: TestWorkerMixedDebug.py +#文件说明: 多实例 debug 混合调试 +#================================================================== +测试步骤: + 1. 连接 connect server 和主线程 debugger server + 2. 主线程使能 Runtime 和 Debugger + 3. 主线程使能混合调试(Debugger.setMixedDebugEnabled) + 4. 触发点击事件,创建子线程,连接子线程 debugger server + 5. 子线程使能 Runtime 和 Debugger + 6. 子线程使能混合调试(Debugger.setMixedDebugEnabled) + 7. 子线程 Worker.ets 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) + 8. 子线程 resume,停在断点处(Debugger.resume) + 9. 子线程 stepInto,进入 native 调试(Debugger.stepInto) + 7. 子线程收到 native 返回的消息后应答,停在下一断点处(Debugger.replyNativeCalling) + 8. 子线程 stepOver,停在下一行(Debugger.stepOver) + 9. 子线程 resume(Debugger.resume) + 10. 所有线程去使能 debugger(Debugger.disable) + 10. 关闭所有线程 debugger server 和 connect server 连接 +#================================================================== +关键代码: + Index.ets + .OnClick(() => { + let myWorker = new worker.ThreadWorker("entry/ets/workers/Worker.ets") + myWorker.onmessage = (e: MessageEvents) => {} + myWorker.postMessage("message from the main thread") + }) + Worker.ets + const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + workerPort.onmessage = (e: MessageEvents) => { + let num = testNapi.add(1, 2) + let ans = testNapi.add(num, 3) + workerPort.postMessage("message from worker thread") + } +#!!================================================================ +""" +import sys +from pathlib import Path + +root_path = Path(__file__).parent.parent.parent.parent +resource_path = root_path / 'resource' +sys.path.append(str(root_path / 'aw')) # add aw path to sys.path + +from devicetest.core.test_case import TestCase, Step +from hypium import UiDriver +from all_utils import CommonUtils, UiUtils +from cdp import debugger +from implement_api import debugger_api, runtime_api + + +class TestWorkerMixedDebug(TestCase): + def __init__(self, controllers): + self.TAG = self.__class__.__name__ + TestCase.__init__(self, self.TAG, controllers) + self.driver = UiDriver(self.device1) + self.ui_utils = UiUtils(self.driver) + self.common_utils = CommonUtils(self.driver) + self.id_generator = CommonUtils.message_id_generator() + self.config = { + 'start_mode': '-D', + 'connect_server_port': 15648, + 'debugger_server_port': 15649, + 'bundle_name': 'com.example.native02', + 'hap_name': 'Native02.hap', + 'hap_path': str(resource_path / 'hap' / 'Native02.hap'), + 'file_path': { + 'entry_ability': 'entry|entry|1.0.0|src/main/ets/entryability/EntryAbility.ts', + 'index': 'entry|entry|1.0.0|src/main/ets/pages/Index.ts', + 'worker': 'entry|entry|1.0.0|src/main/ets/workers/Worker.ts' + } + } + + def setup(self): + Step('1.下载应用') + self.driver.install_app(self.config['hap_path'], "-r") + Step('2.启动应用') + self.driver.start_app(package_name=self.config['bundle_name'], params=self.config['start_mode']) + self.config['pid'] = self.common_utils.get_pid(self.config['bundle_name']) + assert self.config['pid'] != 0, f'Failed to get pid of {self.config["bundle_name"]}' + Step('3.设置屏幕常亮') + self.ui_utils.keep_awake() + Step('4.端口映射,连接server') + self.common_utils.connect_server(self.config) + self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, self.config['websocket']) + self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, self.config['websocket']) + + def process(self): + Step('5.执行测试用例') + websocket = self.config['websocket'] + taskpool = self.config['taskpool'] + taskpool.submit(websocket.main_task(taskpool, self.test, self.config['pid'])) + taskpool.await_taskpool() + taskpool.task_join() + if taskpool.task_exception: + raise taskpool.task_exception + + def teardown(self): + Step('6.关闭应用') + self.driver.stop_app(self.config['bundle_name']) + Step('7.卸载应用') + self.driver.uninstall_app(self.config['bundle_name']) + + async def test(self, websocket): + ################################################################################################################ + # main thread: connect the debugger server + ################################################################################################################ + main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True) + ################################################################################################################ + # main thread: Runtime.enable + ################################################################################################################ + await self.runtime_impl.send("Runtime.enable", main_thread) + ################################################################################################################ + # main thread: Debugger.enable + ################################################################################################################ + await self.debugger_impl.send("Debugger.enable", main_thread) + ################################################################################################################ + # main thread: Debugger.setMixedDebugEnabled + ################################################################################################################ + params = debugger.SetMixedDebugEnabledParams(enabled=True, mixed_stack_enabled=False) + await self.debugger_impl.send("Debugger.setMixedDebugEnabled", main_thread, params) + ################################################################################################################ + # main thread: Runtime.runIfWaitingForDebugger + ################################################################################################################ + await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", main_thread) + ################################################################################################################ + # main thread: Debugger.scriptParsed + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread) + self.common_utils.assert_equal(response['params']['url'], self.config['file_path']['entry_ability']) + self.common_utils.assert_equal(response['params']['endLine'], 0) + ################################################################################################################ + # main thread: Debugger.paused + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], + self.config['file_path']['entry_ability']) + self.common_utils.assert_equal(response['params']['reason'], 'Break on start') + ################################################################################################################ + # main thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", main_thread) + ################################################################################################################ + # main thread: Debugger.scriptParsed + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread) + self.common_utils.assert_equal(response['params']['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['endLine'], 0) + ################################################################################################################ + # main thread: Debugger.paused + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['reason'], 'Break on start') + ################################################################################################################ + # main thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", main_thread) + ################################################################################################################ + # main thread: click on the screen + ################################################################################################################ + self.ui_utils.click_on_middle() + ################################################################################################################ + # worker thread: connect the debugger server + ################################################################################################################ + worker_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) + ################################################################################################################ + # worker thread: Runtime.enable + ################################################################################################################ + await self.runtime_impl.send("Runtime.enable", worker_thread) + ################################################################################################################ + # worker thread: Debugger.enable + ################################################################################################################ + await self.debugger_impl.send("Debugger.enable", worker_thread) + ################################################################################################################ + # worker thread: Debugger.setMixedDebugEnabled + ################################################################################################################ + params = debugger.SetMixedDebugEnabledParams(enabled=True, mixed_stack_enabled=False) + await self.debugger_impl.send("Debugger.setMixedDebugEnabled", worker_thread, params) + ################################################################################################################ + # worker thread: Runtime.runIfWaitingForDebugger + ################################################################################################################ + await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread) + ################################################################################################################ + # worker thread: Debugger.scriptParsed + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread) + self.common_utils.assert_equal(response['params']['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['endLine'], 0) + ################################################################################################################ + # worker thread: Debugger.paused + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.paused", worker_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['reason'], 'Break on start') + ################################################################################################################ + # worker thread: Debugger.removeBreakpointsByUrl + ################################################################################################################ + params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker']) + await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread, params) + ################################################################################################################ + # worker thread: Debugger.getPossibleAndSetBreakpointByUrl + ################################################################################################################ + locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=12), + debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=13)] + params = debugger.SetBreakpointsLocations(locations) + response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", worker_thread, params) + self.common_utils.assert_equal(response['result']['locations'][0]['id'], + 'id:12:0:' + self.config['file_path']['worker']) + self.common_utils.assert_equal(response['result']['locations'][1]['id'], + 'id:13:0:' + self.config['file_path']['worker']) + ################################################################################################################ + # worker thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", worker_thread) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['reason'], 'other') + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:12:14:' + self.config['file_path']['worker']]) + ################################################################################################################ + # worker thread: Debugger.stepInto + ################################################################################################################ + await self.debugger_impl.send("Debugger.stepInto", worker_thread) + ################################################################################################################ + # worker thread: Debugger.nativeCalling + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.nativeCalling", worker_thread) + self.common_utils.assert_equal(response['params']['isStepInto'], True) + ################################################################################################################ + # worker thread: Debugger.replyNativeCalling + ################################################################################################################ + params = debugger.ReplyNativeCallingParams() + await self.debugger_impl.send("Debugger.replyNativeCalling", worker_thread, params) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:13:14:' + self.config['file_path']['worker']]) + ################################################################################################################ + # worker thread: Debugger.stepOver + ################################################################################################################ + await self.debugger_impl.send("Debugger.stepOver", worker_thread) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['hitBreakpoints'], []) + ################################################################################################################ + # worker thread: Debugger.disable + ################################################################################################################ + await self.debugger_impl.send("Debugger.disable", worker_thread) + ################################################################################################################ + # main thread: Debugger.disable + ################################################################################################################ + await self.debugger_impl.send("Debugger.disable", main_thread) + ################################################################################################################ + # close the websocket connections + ################################################################################################################ + await websocket.send_msg_to_debugger_server(worker_thread.instance_id, worker_thread.send_msg_queue, 'close') + await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close') + await websocket.send_msg_to_connect_server('close') + ################################################################################################################ \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/debug/TestWorkerRunAfterProfiler.json b/test/autotest/testcases/toolchain/debug/TestWorkerRunAfterProfiler.json new file mode 100644 index 00000000..4dcdee5d --- /dev/null +++ b/test/autotest/testcases/toolchain/debug/TestWorkerRunAfterProfiler.json @@ -0,0 +1,15 @@ +{ + "description": "Config for TestWorkerRunAfterProfiler", + "environment": [ + { + "type": "device", + "label": "phone" + } + ], + "driver": { + "type": "DeviceTest", + "py_file": [ + "toolchain/debug/TestWorkerRunAfterProfiler.py" + ] + } +} \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/debug/TestWorkerRunAfterProfiler.py b/test/autotest/testcases/toolchain/debug/TestWorkerRunAfterProfiler.py new file mode 100644 index 00000000..11a5d5ff --- /dev/null +++ b/test/autotest/testcases/toolchain/debug/TestWorkerRunAfterProfiler.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 Huawei Device Co., Ltd. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +#================================================================== +#文 件 名: TestWorkerRunAfterProfiler.py +#文件说明: 测试调优后 worker 是否正常运行 +#================================================================== +测试步骤: + 1. 连接 connect server 和主线程 debugger server + 2. 主线程使能 Runtime(Runtime.enable) + 3. 主线程设置采样间隔(Profiler.setSamplingInterval) + 4. 主线程启动调优(Profiler.start) + 5. 主线程去使能 Debugger(Debugger.disable) + 6. 等待 5 秒后关闭 CPU 调优,获取调优数据(Profiler.stop) + 7. 关闭主线程 debugger server 和 connect server 连接 + 8. 触发点击事件,创建 worker 线程并与主线程通信,更新屏幕文字 +#!!================================================================ +关键代码: + Index.ets + .OnClick(() => { + let myWorker = new worker.ThreadWorker("entry/ets/workers/Worker.ets") + myWorker.onmessage = (e: MessageEvents) => { + this.message = e.data + } + }) + Worker.ets + const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + workerPort.onmessage = (e: MessageEvents) => { + workerPort.postMessage(‘worker’) + } +#!!================================================================ +""" +import sys +import time +from pathlib import Path + +root_path = Path(__file__).parent.parent.parent.parent +resource_path = root_path / 'resource' +sys.path.append(str(root_path / 'aw')) # add aw path to sys.path + +from devicetest.core.test_case import TestCase, Step +from hypium import UiDriver +from all_utils import CommonUtils, UiUtils +from cdp import profiler +from implement_api import debugger_api, runtime_api, profiler_api + + +class TestWorkerRunAfterProfiler(TestCase): + def __init__(self, controllers): + self.TAG = self.__class__.__name__ + TestCase.__init__(self, self.TAG, controllers) + self.driver = UiDriver(self.device1) + self.ui_utils = UiUtils(self.driver) + self.common_utils = CommonUtils(self.driver) + self.id_generator = CommonUtils.message_id_generator() + self.config = { + 'start_mode': '', + 'connect_server_port': 15600, + 'debugger_server_port': 15601, + 'bundle_name': 'com.example.multiWorker06', + 'hap_name': 'MultiWorker06.hap', + 'hap_path': str(resource_path / 'hap' / 'MultiWorker06.hap') + } + + def setup(self): + Step('1.下载应用') + self.driver.install_app(self.config['hap_path'], "-r") + Step('2.启动应用') + self.driver.start_app(package_name=self.config['bundle_name'], params=self.config['start_mode']) + self.config['pid'] = self.common_utils.get_pid(self.config['bundle_name']) + assert self.config['pid'] != 0, f'Failed to get pid of {self.config["bundle_name"]}' + Step('3.设置屏幕常亮') + self.ui_utils.keep_awake() + Step('4.端口映射,连接server') + self.common_utils.connect_server(self.config) + self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, self.config['websocket']) + self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, self.config['websocket']) + self.profiler_impl = profiler_api.ProfilerImpl(self.id_generator, self.config['websocket']) + + def process(self): + Step('5.进行Profiler录制') + websocket = self.config['websocket'] + taskpool = self.config['taskpool'] + taskpool.submit(websocket.main_task(taskpool, self.test, self.config['pid'])) + taskpool.await_taskpool() + taskpool.task_join() + if taskpool.task_exception: + raise taskpool.task_exception + Step('6.点击屏幕,判断屏幕文字是否更新') + self.ui_utils.click_on_middle() + time.sleep(1) + contain = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + self.common_utils.assert_equal(contain.text, 'worker') + + def teardown(self): + Step('7.关闭应用') + self.driver.stop_app(self.config['bundle_name']) + Step('8.卸载应用') + self.driver.uninstall_app(self.config['bundle_name']) + + async def test(self, websocket): + ################################################################################################################ + # main thread: connect the debugger server + ################################################################################################################ + main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True) + ################################################################################################################ + # main thread: Runtime.enable + ################################################################################################################ + await self.runtime_impl.send("Runtime.enable", main_thread) + ################################################################################################################ + # main thread: Profiler.setSamplingInterval + ################################################################################################################ + params = profiler.SamplingInterval(500) + await self.profiler_impl.send("Profiler.setSamplingInterval", main_thread, params) + ################################################################################################################ + # main thread: Profiler.start + ################################################################################################################ + await self.profiler_impl.send("Profiler.start", main_thread) + ################################################################################################################ + # main thread: Debugger.disable + ################################################################################################################ + await self.debugger_impl.send("Debugger.disable", main_thread) + ################################################################################################################ + # main thread: sleep 5 seconds + ################################################################################################################ + time.sleep(5) + ################################################################################################################ + # main thread: Profiler.stop + ################################################################################################################ + await self.profiler_impl.send("Profiler.stop", main_thread) + ################################################################################################################ + # connect server: stopDebugger + ################################################################################################################ + await websocket.send_msg_to_connect_server('stopDebugger') + ################################################################################################################ + # close the websocket connections + ################################################################################################################ + await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close') + await websocket.send_msg_to_connect_server('close') + ################################################################################################################ \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/debug/TestWorkerSmartStepInto.json b/test/autotest/testcases/toolchain/debug/TestWorkerSmartStepInto.json new file mode 100644 index 00000000..435a7c96 --- /dev/null +++ b/test/autotest/testcases/toolchain/debug/TestWorkerSmartStepInto.json @@ -0,0 +1,15 @@ +{ + "description": "Config for TestWorkerSmartStepInto", + "environment": [ + { + "type": "device", + "label": "phone" + } + ], + "driver": { + "type": "DeviceTest", + "py_file": [ + "toolchain/debug/TestWorkerSmartStepInto.py" + ] + } +} \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/debug/TestWorkerSmartStepInto.py b/test/autotest/testcases/toolchain/debug/TestWorkerSmartStepInto.py new file mode 100644 index 00000000..925a000d --- /dev/null +++ b/test/autotest/testcases/toolchain/debug/TestWorkerSmartStepInto.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 Huawei Device Co., Ltd. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +#================================================================== +#文 件 名: TestWorkerSmartStepInto.py +#文件说明: 多实例 debug 调试 smartStepInto +#================================================================== +测试步骤: + 1. 连接 connect server 和主线程 debugger server + 2. 主线程使能 Runtime 和 Debugger + 3. 主线程 resume(Debugger.resume) + 4. 触发点击事件,创建子线程,连接子线程 debugger server + 5. 子线程使能 Runtime 和 Debugger + 6. 子线程 Worker.ets 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) + 7. 子线程 resume,停在断点处(Debugger.resume) + 8. 子线程 smartStepInto 进入 introduce 方法(Debugger.smartStepInto) + 9. 子线程 stepOut 跳出 introduce 方法(Debugger.stepOut) + 10. 子线程 smartStepInto 进入 add 方法(Debugger.smartStepInto) + 11. 子线程 stepOut 跳出 add 方法(Debugger.stepOut) + 12. 子线程 smartStepInto 进入 sub 方法(Debugger.smartStepInto) + 13. 子线程 resume(Debugger.resume) + 14. 所有线程去使能 debugger(Debugger.disable) + 15. 关闭所有线程 debugger server 和 connect server 连接 +#================================================================== +关键代码: + Index.ets + .OnClick(() => { + let myWorker = new worker.ThreadWorker("entry/ets/workers/Worker.ets") + myWorker.onmessage = (e: MessageEvents) => {} + myWorker.postMessage("hello world") + }) + Worker.ets + const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + workerPort.onmessage = (e: MessageEvents) => { + let person = new Person('Tim', add(10, 11)) + person.introduce() + let age = add(3, 6) + sub(4, 1) + workerPort.postMessage(e.data) + } + function add(args1, args2) { + return args1 + args2 + } + function sub(args1, args2) { + return args1 - args2 + } + class Person { + name: string + age: number + constructor(name, age){} + introduce() {} + } +#!!================================================================ +""" +import sys +from pathlib import Path + +root_path = Path(__file__).parent.parent.parent.parent +resource_path = root_path / 'resource' +sys.path.append(str(root_path / 'aw')) # add aw path to sys.path + +from devicetest.core.test_case import TestCase, Step +from hypium import UiDriver +from all_utils import CommonUtils, UiUtils +from cdp import debugger +from implement_api import debugger_api, runtime_api + + +class TestWorkerSmartStepInto(TestCase): + def __init__(self, controllers): + self.TAG = self.__class__.__name__ + TestCase.__init__(self, self.TAG, controllers) + self.driver = UiDriver(self.device1) + self.ui_utils = UiUtils(self.driver) + self.common_utils = CommonUtils(self.driver) + self.id_generator = CommonUtils.message_id_generator() + self.config = { + 'start_mode': '-D', + 'connect_server_port': 15650, + 'debugger_server_port': 15651, + 'bundle_name': 'com.example.multiWorker06', + 'hap_name': 'MultiWorker06.hap', + 'hap_path': str(resource_path / 'hap' / 'MultiWorker06.hap'), + 'file_path': { + 'entry_ability': 'entry|entry|1.0.0|src/main/ets/entryability/EntryAbility.ts', + 'index': 'entry|entry|1.0.0|src/main/ets/pages/Index.ts', + 'worker': 'entry|entry|1.0.0|src/main/ets/workers/Worker.ts' + } + } + + def setup(self): + Step('1.下载应用') + self.driver.install_app(self.config['hap_path'], "-r") + Step('2.启动应用') + self.driver.start_app(package_name=self.config['bundle_name'], params=self.config['start_mode']) + self.config['pid'] = self.common_utils.get_pid(self.config['bundle_name']) + assert self.config['pid'] != 0, f'Failed to get pid of {self.config["bundle_name"]}' + Step('3.设置屏幕常亮') + self.ui_utils.keep_awake() + Step('4.端口映射,连接server') + self.common_utils.connect_server(self.config) + self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, self.config['websocket']) + self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, self.config['websocket']) + + def process(self): + Step('5.执行测试用例') + websocket = self.config['websocket'] + taskpool = self.config['taskpool'] + taskpool.submit(websocket.main_task(taskpool, self.test, self.config['pid'])) + taskpool.await_taskpool() + taskpool.task_join() + if taskpool.task_exception: + raise taskpool.task_exception + + def teardown(self): + Step('6.关闭应用') + self.driver.stop_app(self.config['bundle_name']) + Step('7.卸载应用') + self.driver.uninstall_app(self.config['bundle_name']) + + async def test(self, websocket): + ################################################################################################################ + # main thread: connect the debugger server + ################################################################################################################ + main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True) + ################################################################################################################ + # main thread: Runtime.enable + ################################################################################################################ + await self.runtime_impl.send("Runtime.enable", main_thread) + ################################################################################################################ + # main thread: Debugger.enable + ################################################################################################################ + await self.debugger_impl.send("Debugger.enable", main_thread) + ################################################################################################################ + # main thread: Runtime.runIfWaitingForDebugger + ################################################################################################################ + await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", main_thread) + ################################################################################################################ + # main thread: Debugger.scriptParsed + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread) + self.common_utils.assert_equal(response['params']['url'], self.config['file_path']['entry_ability']) + self.common_utils.assert_equal(response['params']['endLine'], 0) + ################################################################################################################ + # main thread: Debugger.paused + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], + self.config['file_path']['entry_ability']) + self.common_utils.assert_equal(response['params']['reason'], 'Break on start') + ################################################################################################################ + # main thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", main_thread) + ################################################################################################################ + # main thread: Debugger.scriptParsed + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread) + self.common_utils.assert_equal(response['params']['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['endLine'], 0) + ################################################################################################################ + # main thread: Debugger.paused + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['reason'], 'Break on start') + ################################################################################################################ + # main thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", main_thread) + ################################################################################################################ + # main thread: click on the screen + ################################################################################################################ + self.ui_utils.click_on_middle() + ################################################################################################################ + # worker thread: connect the debugger server + ################################################################################################################ + worker_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) + ################################################################################################################ + # worker thread: Runtime.enable + ################################################################################################################ + await self.runtime_impl.send("Runtime.enable", worker_thread) + ################################################################################################################ + # worker thread: Debugger.enable + ################################################################################################################ + await self.debugger_impl.send("Debugger.enable", worker_thread) + ################################################################################################################ + # worker thread: Runtime.runIfWaitingForDebugger + ################################################################################################################ + await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread) + ################################################################################################################ + # worker thread: Debugger.scriptParsed + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread) + self.common_utils.assert_equal(response['params']['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['endLine'], 0) + ################################################################################################################ + # worker thread: Debugger.paused + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.paused", worker_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['reason'], 'Break on start') + ################################################################################################################ + # worker thread: Debugger.removeBreakpointsByUrl + ################################################################################################################ + params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker']) + await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread, params) + ################################################################################################################ + # worker thread: Debugger.getPossibleAndSetBreakpointByUrl + ################################################################################################################ + locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=14)] + params = debugger.SetBreakpointsLocations(locations) + response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", worker_thread, params) + self.common_utils.assert_equal(response['result']['locations'][0]['id'], + 'id:14:0:' + self.config['file_path']['worker']) + ################################################################################################################ + # worker thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", worker_thread) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['reason'], 'other') + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:14:4:' + self.config['file_path']['worker']]) + ################################################################################################################ + # worker thread: Debugger.smartStepInto + ################################################################################################################ + params = debugger.SmartStepIntoParams(url=self.config['file_path']['worker'], line_number=61) + await self.debugger_impl.send("Debugger.smartStepInto", worker_thread, params) + ################################################################################################################ + # worker thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", worker_thread) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['callFrames'][0]['functionName'], 'introduce') + self.common_utils.assert_equal(response['params']['reason'], 'other') + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:61:8:' + self.config['file_path']['worker']]) + ################################################################################################################ + # worker thread: Debugger.stepOut + ################################################################################################################ + await self.debugger_impl.send("Debugger.stepOut", worker_thread) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['reason'], 'other') + self.common_utils.assert_equal(response['params']['hitBreakpoints'], []) + ################################################################################################################ + # worker thread: Debugger.smartStepInto + ################################################################################################################ + params = debugger.SmartStepIntoParams(url=self.config['file_path']['worker'], line_number=37) + await self.debugger_impl.send("Debugger.smartStepInto", worker_thread, params) + ################################################################################################################ + # worker thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", worker_thread) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['callFrames'][0]['functionName'], 'add') + self.common_utils.assert_equal(response['params']['reason'], 'other') + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:37:17:' + self.config['file_path']['worker']]) + ################################################################################################################ + # worker thread: Debugger.stepOut + ################################################################################################################ + await self.debugger_impl.send("Debugger.stepOut", worker_thread) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['reason'], 'other') + self.common_utils.assert_equal(response['params']['hitBreakpoints'], []) + ################################################################################################################ + # worker thread: Debugger.smartStepInto + ################################################################################################################ + params = debugger.SmartStepIntoParams(url=self.config['file_path']['worker'], line_number=41) + await self.debugger_impl.send("Debugger.smartStepInto", worker_thread, params) + ################################################################################################################ + # worker thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", worker_thread) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['callFrames'][0]['functionName'], 'sub') + self.common_utils.assert_equal(response['params']['reason'], 'other') + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:41:17:' + self.config['file_path']['worker']]) + ################################################################################################################ + # worker thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", worker_thread) + ################################################################################################################ + # worker thread: Debugger.disable + ################################################################################################################ + await self.debugger_impl.send("Debugger.disable", worker_thread) + ################################################################################################################ + # main thread: Debugger.disable + ################################################################################################################ + await self.debugger_impl.send("Debugger.disable", main_thread) + ################################################################################################################ + # close the websocket connections + ################################################################################################################ + await websocket.send_msg_to_debugger_server(worker_thread.instance_id, worker_thread.send_msg_queue, 'close') + await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close') + await websocket.send_msg_to_connect_server('close') + ################################################################################################################ \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/debug/TestWorkerStep.json b/test/autotest/testcases/toolchain/debug/TestWorkerStep.json new file mode 100644 index 00000000..fecc231d --- /dev/null +++ b/test/autotest/testcases/toolchain/debug/TestWorkerStep.json @@ -0,0 +1,15 @@ +{ + "description": "Config for TestWorkerStep", + "environment": [ + { + "type": "device", + "label": "phone" + } + ], + "driver": { + "type": "DeviceTest", + "py_file": [ + "toolchain/debug/TestWorkerStep.py" + ] + } +} \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/debug/TestWorkerStep.py b/test/autotest/testcases/toolchain/debug/TestWorkerStep.py new file mode 100644 index 00000000..74c03b63 --- /dev/null +++ b/test/autotest/testcases/toolchain/debug/TestWorkerStep.py @@ -0,0 +1,446 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 Huawei Device Co., Ltd. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +#================================================================== +#文 件 名: TestWorkerStep.py +#文件说明: 多实例 debug 调试,执行单步操作进行跨线程通信 +#================================================================== +测试步骤: + 1. 连接 connect server 和主线程 debugger server + 2. 主线程使能 Runtime 和 Debugger + 3. 主线程 Index.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) + 4. 主线程 stepOut,暂停在下一断点(Debugger.stepOut) + 5. 创建第一个子线程,连接子线程 debugger server + 6. 主线程 resume,暂停在下一断点(Debugger.resume) + 7. 创建另一个子线程,连接子线程 debugger server + 8. 所有子线程使能 Runtime 和 Debugger + 9. 所有子线程 Worker.ts 文件设置断点(Debugger.getPossibleAndSetBreakpointByUrl) + 10. 触发点击事件,主线程命中断点 + 11. 销毁其中一个子线程 + 12. 主线程 stepInto,暂停在下一行(Debugger.stepInto) + 13. 主线程 getProperties,返回给定对象的属性(Runtime.getProperties) + 14. 主线程 resume,暂停在下一断点(Debugger.resume) + 15. 重新创建一个子线程,使能并设置断点 + 16. 主线程 stepOut,发送消息给子线程,主线程暂停在下一断点(Debugger.stepOut) + 17. 子线程命中断点后 getProperties(Runtime.getProperties) + 18. 子线程 stepOut 发消息给主线程(Debugger.stepOut) + 19. 主线程 stepInto,发送消息给另一子线程,主线程暂停在下一行(Debugger.stepInto) + 20. 子线程命中断点后 resume,发消息给主线程(Debugger.resume) + 21. 销毁所有子线程,对应的 debugger server 连接断开 + 22. 关闭主线程 debugger server 和 connect server 连接 +#================================================================== +关键代码: + Index.ets + let workerIndex = 0 + function newWorker() {} // 创建一个子线程, workerIndex++ + function terminateWorker() {} // 销毁一个子线程, workerIndex-- + for (let i = 0; i < 2; i++) { + newWorker() + } + .onClick(() => { + terminateWorker() + newWorker() + for (let i = 0; i < workerIndex; i++) { + workers[i].postMessage("hello world") + } + while (workerIndex) { + terminateWorker() + } + }) + Worker.ets + const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + workerPort.onmessage = (e: MessageEvents) => { + workerPort.postMessage(e.data); + } +#!!================================================================ +""" +import sys +from pathlib import Path + +root_path = Path(__file__).parent.parent.parent.parent +resource_path = root_path / 'resource' +sys.path.append(str(root_path / 'aw')) # add aw path to sys.path + +from devicetest.core.test_case import TestCase, Step +from hypium import UiDriver +from all_utils import CommonUtils, UiUtils +from cdp import debugger, runtime +from implement_api import debugger_api, runtime_api + + +class TestWorkerStep(TestCase): + def __init__(self, controllers): + self.TAG = self.__class__.__name__ + TestCase.__init__(self, self.TAG, controllers) + self.driver = UiDriver(self.device1) + self.ui_utils = UiUtils(self.driver) + self.common_utils = CommonUtils(self.driver) + self.id_generator = CommonUtils.message_id_generator() + self.config = { + 'start_mode': '-D', + 'connect_server_port': 15652, + 'debugger_server_port': 15653, + 'bundle_name': 'com.example.multiWorker01', + 'hap_name': 'MultiWorker01.hap', + 'hap_path': str(resource_path / 'hap' / 'MultiWorker01.hap'), + 'file_path': { + 'entry_ability': 'entry|entry|1.0.0|src/main/ets/entryability/EntryAbility.ts', + 'index': 'entry|entry|1.0.0|src/main/ets/pages/Index.ts', + 'worker': 'entry|entry|1.0.0|src/main/ets/workers/Worker.ts' + } + } + + def setup(self): + Step('1.下载应用') + self.driver.install_app(self.config['hap_path'], "-r") + Step('2.启动应用') + self.driver.start_app(package_name=self.config['bundle_name'], params=self.config['start_mode']) + self.config['pid'] = self.common_utils.get_pid(self.config['bundle_name']) + assert self.config['pid'] != 0, f'Failed to get pid of {self.config["bundle_name"]}' + Step('3.设置屏幕常亮') + self.ui_utils.keep_awake() + Step('4.端口映射,连接server') + self.common_utils.connect_server(self.config) + self.debugger_impl = debugger_api.DebuggerImpl(self.id_generator, self.config['websocket']) + self.runtime_impl = runtime_api.RuntimeImpl(self.id_generator, self.config['websocket']) + + def process(self): + Step('5.执行测试用例') + websocket = self.config['websocket'] + taskpool = self.config['taskpool'] + taskpool.submit(websocket.main_task(taskpool, self.test, self.config['pid'])) + taskpool.await_taskpool() + taskpool.task_join() + if taskpool.task_exception: + raise taskpool.task_exception + + def teardown(self): + Step('6.关闭应用') + self.driver.stop_app(self.config['bundle_name']) + Step('7.卸载应用') + self.driver.uninstall_app(self.config['bundle_name']) + + async def test(self, websocket): + ################################################################################################################ + # main thread: connect the debugger server + ################################################################################################################ + main_thread = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], True) + ################################################################################################################ + # main thread: Runtime.enable + ################################################################################################################ + await self.runtime_impl.send("Runtime.enable", main_thread) + ################################################################################################################ + # main thread: Debugger.enable + ################################################################################################################ + await self.debugger_impl.send("Debugger.enable", main_thread) + ################################################################################################################ + # main thread: Runtime.runIfWaitingForDebugger + ################################################################################################################ + await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", main_thread) + ################################################################################################################ + # main thread: Debugger.scriptParsed + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread) + self.common_utils.assert_equal(response['params']['url'], self.config['file_path']['entry_ability']) + self.common_utils.assert_equal(response['params']['endLine'], 0) + ################################################################################################################ + # main thread: Debugger.paused + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], + self.config['file_path']['entry_ability']) + self.common_utils.assert_equal(response['params']['reason'], 'Break on start') + ################################################################################################################ + # main thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", main_thread) + ################################################################################################################ + # main thread: Debugger.scriptParsed + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.scriptParsed", main_thread) + self.common_utils.assert_equal(response['params']['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['endLine'], 0) + ################################################################################################################ + # main thread: Debugger.paused + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['reason'], 'Break on start') + ################################################################################################################ + # main thread: Debugger.removeBreakpointsByUrl + ################################################################################################################ + params = debugger.RemoveBreakpointsUrl(self.config['file_path']['index']) + await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", main_thread, params) + ################################################################################################################ + # main thread: Debugger.getPossibleAndSetBreakpointByUrl + ################################################################################################################ + locations = [debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=12), + debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=53), + debugger.BreakLocationUrl(url=self.config['file_path']['index'], line_number=57)] + params = debugger.SetBreakpointsLocations(locations) + response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", main_thread, params) + self.common_utils.assert_equal(response['result']['locations'][0]['id'], + 'id:12:0:' + self.config['file_path']['index']) + self.common_utils.assert_equal(response['result']['locations'][1]['id'], + 'id:53:0:' + self.config['file_path']['index']) + self.common_utils.assert_equal(response['result']['locations'][2]['id'], + 'id:57:0:' + self.config['file_path']['index']) + ################################################################################################################ + # main thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", main_thread) + ################################################################################################################ + # main thread: Debugger.paused, hit breakpoint + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:12:4:' + self.config['file_path']['index']]) + ################################################################################################################ + # main thread: Debugger.stepOut + ################################################################################################################ + await self.debugger_impl.send("Debugger.stepOut", main_thread) + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:12:4:' + self.config['file_path']['index']]) + ################################################################################################################ + # worker thread: connect the debugger server + ################################################################################################################ + worker_thread_1 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) + ################################################################################################################ + # main thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", main_thread) + ################################################################################################################ + # worker thread: connect the debugger server + ################################################################################################################ + worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) + ################################################################################################################ + # worker thread: Runtime.enable + ################################################################################################################ + await self.runtime_impl.send("Runtime.enable", worker_thread_1) + await self.runtime_impl.send("Runtime.enable", worker_thread_2) + ################################################################################################################ + # worker thread: Debugger.enable + ################################################################################################################ + await self.debugger_impl.send("Debugger.enable", worker_thread_1) + await self.debugger_impl.send("Debugger.enable", worker_thread_2) + ################################################################################################################ + # worker thread: Runtime.runIfWaitingForDebugger + ################################################################################################################ + # worker thread 1: Runtime.runIfWaitingForDebugger + await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_1) + # worker thread 1: Debugger.scriptParsed + response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_1) + self.common_utils.assert_equal(response['params']['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['endLine'], 0) + # worker thread 1: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['reason'], 'Break on start') + # worker thread 2: Runtime.runIfWaitingForDebugger + await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2) + # worker thread 2: Debugger.scriptParsed + response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_2) + self.common_utils.assert_equal(response['params']['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['endLine'], 0) + # worker thread 2: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['reason'], 'Break on start') + ################################################################################################################ + # worker thread: Debugger.removeBreakpointsByUrl + ################################################################################################################ + params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker']) + await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_1, params) + await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_2, params) + ################################################################################################################ + # worker thread: Debugger.getPossibleAndSetBreakpointByUrl + ################################################################################################################ + locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)] + params = debugger.SetBreakpointsLocations(locations) + response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", worker_thread_1, params) + self.common_utils.assert_equal(response['result']['locations'][0]['id'], + 'id:11:0:' + self.config['file_path']['worker']) + response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", worker_thread_2, params) + self.common_utils.assert_equal(response['result']['locations'][0]['id'], + 'id:11:0:' + self.config['file_path']['worker']) + ################################################################################################################ + # worker thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", worker_thread_1) + await self.debugger_impl.send("Debugger.resume", worker_thread_2) + ################################################################################################################ + # main thread: click on the screen + ################################################################################################################ + self.ui_utils.click_on_middle() + ################################################################################################################ + # main thread: Debugger.paused, hit breakpoint + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:53:16:' + self.config['file_path']['index']]) + ################################################################################################################ + # worker thread: destroy instance + ################################################################################################################ + # worker thread 2 destroyed + response = await self.debugger_impl.destroy_instance() + self.common_utils.assert_equal(response['instanceId'], worker_thread_2.instance_id) + ################################################################################################################ + # main thread: Debugger.stepInto + ################################################################################################################ + await self.debugger_impl.send("Debugger.stepInto", main_thread) + await self.debugger_impl.recv("Debugger.paused", main_thread) + ################################################################################################################ + # main thread: Runtime.getProperties + ################################################################################################################ + params = runtime.GetPropertiesParams('0', True, False, True) + response = await self.runtime_impl.send("Runtime.getProperties", main_thread, params) + self.common_utils.assert_equal(response['result']['result'][0]['name'], 'set message') + self.common_utils.assert_equal(response['result']['result'][0]['value']['type'], 'function') + self.common_utils.assert_equal(response['result']['result'][1]['name'], 'newValue') + self.common_utils.assert_equal(response['result']['result'][1]['value']['type'], 'string') + ################################################################################################################ + # main thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", main_thread) + ################################################################################################################ + # main thread: Debugger.paused, hit breakpoint + ################################################################################################################ + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:57:20:' + self.config['file_path']['index']]) + ################################################################################################################ + # worker thread: connect the debugger server + ################################################################################################################ + worker_thread_2 = await self.debugger_impl.connect_to_debugger_server(self.config['pid'], False) + ################################################################################################################ + # worker thread: Runtime.enable + ################################################################################################################ + await self.runtime_impl.send("Runtime.enable", worker_thread_2) + ################################################################################################################ + # worker thread: Debugger.enable + ################################################################################################################ + await self.debugger_impl.send("Debugger.enable", worker_thread_2) + ################################################################################################################ + # worker thread: Runtime.runIfWaitingForDebugger + ################################################################################################################ + await self.runtime_impl.send("Runtime.runIfWaitingForDebugger", worker_thread_2) + # worker thread: Debugger.scriptParsed + response = await self.debugger_impl.recv("Debugger.scriptParsed", worker_thread_2) + self.common_utils.assert_equal(response['params']['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['endLine'], 0) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['reason'], 'Break on start') + ################################################################################################################ + # worker thread: Debugger.removeBreakpointsByUrl + ################################################################################################################ + params = debugger.RemoveBreakpointsUrl(self.config['file_path']['worker']) + await self.debugger_impl.send("Debugger.removeBreakpointsByUrl", worker_thread_2, params) + ################################################################################################################ + # worker thread: Debugger.getPossibleAndSetBreakpointByUrl + ################################################################################################################ + locations = [debugger.BreakLocationUrl(url=self.config['file_path']['worker'], line_number=11)] + params = debugger.SetBreakpointsLocations(locations) + response = await self.debugger_impl.send("Debugger.getPossibleAndSetBreakpointsByUrl", worker_thread_2, params) + self.common_utils.assert_equal(response['result']['locations'][0]['id'], + 'id:11:0:' + self.config['file_path']['worker']) + ################################################################################################################ + # worker thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", worker_thread_2) + ################################################################################################################ + # main thread: Debugger.stepOut + ################################################################################################################ + await self.debugger_impl.send("Debugger.stepOut", main_thread) + # main thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['reason'], 'other') + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:57:20:' + self.config['file_path']['index']]) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread_1) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['reason'], 'other') + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:11:4:' + self.config['file_path']['worker']]) + ################################################################################################################ + # worker thread: Runtime.getProperties + ################################################################################################################ + params = runtime.GetPropertiesParams('0', True, False, True) + response = await self.runtime_impl.send("Runtime.getProperties", worker_thread_1, params) + self.common_utils.assert_equal(response['result']['result'][0]['name'], '') + self.common_utils.assert_equal(response['result']['result'][0]['value']['type'], 'function') + self.common_utils.assert_equal(response['result']['result'][1]['name'], 'e') + self.common_utils.assert_equal(response['result']['result'][1]['value']['type'], 'object') + ################################################################################################################ + # worker thread: Debugger.stepOut + ################################################################################################################ + await self.debugger_impl.send("Debugger.stepOut", worker_thread_1) + ################################################################################################################ + # worker thread: Debugger.disable + ################################################################################################################ + await self.debugger_impl.send("Debugger.disable", worker_thread_1) + ################################################################################################################ + # main thread: Debugger.stepInto + ################################################################################################################ + await self.debugger_impl.send("Debugger.stepInto", main_thread) + # main thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", main_thread) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['index']) + self.common_utils.assert_equal(response['params']['reason'], 'other') + self.common_utils.assert_equal(response['params']['hitBreakpoints'], []) + # worker thread: Debugger.paused + response = await self.debugger_impl.recv("Debugger.paused", worker_thread_2) + self.common_utils.assert_equal(response['params']['callFrames'][0]['url'], self.config['file_path']['worker']) + self.common_utils.assert_equal(response['params']['reason'], 'other') + self.common_utils.assert_equal(response['params']['hitBreakpoints'], + ['id:11:4:' + self.config['file_path']['worker']]) + ################################################################################################################ + # worker thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", worker_thread_2) + ################################################################################################################ + # worker thread: Debugger.disable + ################################################################################################################ + await self.debugger_impl.send("Debugger.disable", worker_thread_2) + ################################################################################################################ + # main thread: Debugger.resume + ################################################################################################################ + await self.debugger_impl.send("Debugger.resume", main_thread) + ################################################################################################################ + # worker thread: destroy instance + ################################################################################################################ + response = await self.debugger_impl.destroy_instance() + assert response['instanceId'] != self.config['pid'], f"Worker instanceId can not be {self.config['pid']}" + response = await self.debugger_impl.destroy_instance() + assert response['instanceId'] != self.config['pid'], f"Worker instanceId can not be {self.config['pid']}" + ################################################################################################################ + # main thread: Debugger.disable + ################################################################################################################ + await self.debugger_impl.send("Debugger.disable", main_thread) + ################################################################################################################ + # close the websocket connections + ################################################################################################################ + await websocket.send_msg_to_debugger_server(main_thread.instance_id, main_thread.send_msg_queue, 'close') + await websocket.send_msg_to_connect_server('close') + ################################################################################################################ \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/hotreload/TestHotReloadClass01.json b/test/autotest/testcases/toolchain/hotreload/TestHotReloadClass01.json new file mode 100644 index 00000000..1e69160c --- /dev/null +++ b/test/autotest/testcases/toolchain/hotreload/TestHotReloadClass01.json @@ -0,0 +1,15 @@ +{ + "description": "Config for TestHotReloadClass01", + "environment": [ + { + "type": "device", + "label": "phone" + } + ], + "driver": { + "type": "DeviceTest", + "py_file": [ + "toolchain/hotreload/TestHotReloadClass01.py" + ] + } +} \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/hotreload/TestHotReloadClass01.py b/test/autotest/testcases/toolchain/hotreload/TestHotReloadClass01.py new file mode 100644 index 00000000..cb9c3166 --- /dev/null +++ b/test/autotest/testcases/toolchain/hotreload/TestHotReloadClass01.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 Huawei Device Co., Ltd. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +#================================================================== +#文 件 名: TestHotReloadClass01.py +#文件说明: 测试class中方法修改的热重载,覆盖DEFINECLASSWITHBUFFER_IMM8和DEFINECLASSWITHBUFFER_IMM16 +#================================================================== +测试步骤: + 1、启动应用,加载Index页面,页面有Button0和Button1 + 2、点击Button0 + 3、进行第一次热重载(IMM8),再次点击Button0,按钮文本产生对应变化 + 4、点击Button1 + 5、进行第二次热重载(IMM16),再次点击Button1,按钮文本产生相应变化 +#================================================================== +关键代码: + 修改前: + Person.ets + getName(): string { + return this.name + ' ' + this.surname; + } + Point.ets + getName() : string { + return this.name + } + 第一次修改: + Person.ets + getName(): string { + return this.name + '-' + this.surname; + } + 第二次修改: + Point.ets + getName() : string { + return this.name + '-' +this.x + '-' +this.y + } +#!!================================================================ +""" +import os +import sys +from pathlib import Path + +root_path = Path(__file__).parent.parent.parent.parent +resource_path = root_path / 'resource' +sys.path.append(str(root_path / 'aw')) # add aw path to sys.path + +from devicetest.core.test_case import TestCase, Step, CheckPoint +from hypium import UiDriver +from all_utils import CommonUtils, UiUtils + + +class TestHotReloadClass01(TestCase): + def __init__(self, controllers): + self.TAG = self.__class__.__name__ + TestCase.__init__(self, self.TAG, controllers) + self.driver = UiDriver(self.device1) + self.ui_utils = UiUtils(self.driver) + self.common_utils = CommonUtils(self.driver) + self.pid = -1 + self.config = { + 'bundle_name': 'com.example.HotReloadClass01', + 'hap_path': str(resource_path / 'hap' / 'HotReloadClass01.hap'), + + 'local_hqf_01_path': str(resource_path / 'hqf' / 'HotReloadClass01.01.hqf'), + 'remote_hqf_01_path': '/data/HotReloadClass01.01.hqf', + + 'local_hqf_02_path': str(resource_path / 'hqf' / 'HotReloadClass01.02.hqf'), + 'remote_hqf_02_path': '/data/HotReloadClass01.01.hqf', + } + + def setup(self): + Step('1.安装应用') + self.driver.install_app(self.config['hap_path'], "-r") + Step('2.启动应用') + self.driver.start_app(package_name=self.config['bundle_name']) + self.pid = self.common_utils.get_pid(self.config['bundle_name']) + assert self.pid != 0, f'Failed to get pid of {self.config["bundle_name"]}' + Step('3.设置屏幕常亮') + self.ui_utils.keep_awake() + + def process(self): + Step('4.获取主页Button0和Button1的控件') + button_0 = self.driver.UiTree.find_component_by_path('/root/Row/Column/Button[0]/Text') + CheckPoint('检查button_0的文本内容') + self.common_utils.assert_equal(button_0.text, 'class IMM8') + button_1 = self.driver.UiTree.find_component_by_path('/root/Row/Column/Button[1]/Text') + CheckPoint('检查button_1的文本内容') + self.common_utils.assert_equal(button_1.text, 'class IMM16') + + Step('5.点击Button0控件') + button_0.click() + CheckPoint('检查点击Button0后的文本') + button_0 = self.driver.UiTree.find_component_by_path('/root/Row/Column/Button[0]/Text') + self.common_utils.assert_equal(button_0.text, 'class IMM8 John Smith') + + Step('6.第一次触发热重载: DEFINECLASSWITHBUFFER_IMM8') + self.driver.Storage.push_file(local_path=self.config['local_hqf_01_path'], + device_path=self.config['remote_hqf_01_path']) + self.common_utils.hot_reload(self.config['remote_hqf_01_path']) + + Step('7.点击Button0控件') + button_0.click() + CheckPoint('检查点击后Button0的文本,是否符合热重载的修改') + button_0 = self.driver.UiTree.find_component_by_path('/root/Row/Column/Button[0]/Text') + self.common_utils.assert_equal(button_0.text, 'class IMM8 John Smith John-Smith') + + Step('8.点击Button1控件') + button_1.click() + CheckPoint('检查点击button_1后的文本') + button_1 = self.driver.UiTree.find_component_by_path('/root/Row/Column/Button[1]/Text') + self.common_utils.assert_equal(button_1.text, 'class IMM16 Singularity_255') + + Step('9.第二次触发热重载: DEFINECLASSWITHBUFFER_IMM16') + self.driver.Storage.push_file(local_path=self.config['local_hqf_02_path'], + device_path=self.config['remote_hqf_02_path']) + self.common_utils.hot_reload(self.config['remote_hqf_02_path']) + + Step('10.点击Button1控件') + button_1.click() + CheckPoint('检查点击后Button1的文本,是否符合热重载的修改') + button_1 = self.driver.UiTree.find_component_by_path('/root/Row/Column/Button[1]/Text') + self.common_utils.assert_equal(button_1.text, 'class IMM16 Singularity_255 Singularity_255-255-255') + + CheckPoint('11.确保应用正常运行') + pid = self.common_utils.get_pid(self.config['bundle_name']) + assert pid == self.pid, f'App is no longer running with pid: {pid}' + + def teardown(self): + Step("12.卸载应用") + self.driver.uninstall_app(self.config['bundle_name']) \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/hotreload/TestHotReloadHarHsp01.json b/test/autotest/testcases/toolchain/hotreload/TestHotReloadHarHsp01.json new file mode 100644 index 00000000..6900c78e --- /dev/null +++ b/test/autotest/testcases/toolchain/hotreload/TestHotReloadHarHsp01.json @@ -0,0 +1,15 @@ +{ + "description": "Config for TestHotReloadHarHsp01", + "environment": [ + { + "type": "device", + "label": "phone" + } + ], + "driver": { + "type": "DeviceTest", + "py_file": [ + "toolchain/hotreload/TestHotReloadHarHsp01.py" + ] + } +} \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/hotreload/TestHotReloadHarHsp01.py b/test/autotest/testcases/toolchain/hotreload/TestHotReloadHarHsp01.py new file mode 100644 index 00000000..3d7003bd --- /dev/null +++ b/test/autotest/testcases/toolchain/hotreload/TestHotReloadHarHsp01.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 Huawei Device Co., Ltd. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +#================================================================== +#文 件 名: TestHotReloadHarHsp01.py +#文件说明: 在import Har和import Hsp的应用页面测试热重载 +#================================================================== +测试步骤: + 1、启动应用 + 2、点击屏幕中央,this.message被修改,并调用calc函数 + 3、触发热重载 + 4、再次点击屏幕中央,this.message被修改,并调用calc函数 +#================================================================== +关键代码: + 修改前: + import {Sum} from '@ohos/HarLibrary' + import {Multiply} from '@ohos/HspDiscovery' + .onClick(() => { + this.message += '_' + this.message += calc(1, 2) + }) + function calc(x: number, y: number): number { + console.log('TestHotReloadHarHsp01::calc(), args:', x, y) + return Multiply(Sum(x, x), Sum(y, y)) + } + 修改后: + import {Sum} from '@ohos/HarLibrary' + import {Multiply} from '@ohos/HspDiscovery' + .onClick(() => { + this.message += '_' + this.message += calc(3, 4) + }) + function calc(x: number, y: number): number { + console.log('TestHotReloadHarHsp01::calc(), args:', x, y) + return Multiply(Sum(x, x), Sum(y, y)) + } +#!!================================================================ +""" +import os +import sys +from pathlib import Path + +root_path = Path(__file__).parent.parent.parent.parent +resource_path = root_path / 'resource' +sys.path.append(str(root_path / 'aw')) # add aw path to sys.path + +from devicetest.core.test_case import TestCase, Step, CheckPoint +from hypium import UiDriver +from all_utils import CommonUtils, UiUtils + + +class TestHotReloadHarHsp01(TestCase): + def __init__(self, controllers): + self.TAG = self.__class__.__name__ + TestCase.__init__(self, self.TAG, controllers) + self.driver = UiDriver(self.device1) + self.ui_utils = UiUtils(self.driver) + self.common_utils = CommonUtils(self.driver) + self.pid = -1 + self.config = { + 'bundle_name': 'com.example.HotReloadHarHsp01', + 'hap_path': [str(resource_path / 'hap' / 'HotReloadHarHsp01' / 'HotReloadHarHsp01.hap'), + str(resource_path / 'hap' / 'HotReloadHarHsp01' / 'HspDiscovery.hsp')], + 'local_hqf_entry_path': str(resource_path / 'hqf' / 'HotReloadHarHsp01.hqf'), + 'remote_hqf_entry_path': '/data/HotReloadHarHsp01.hqf' + } + + def setup(self): + Step('1.安装应用') + self.driver.install_app(self.config['hap_path'], "-r") + Step('2.启动应用') + self.driver.start_app(package_name=self.config['bundle_name']) + self.pid = self.common_utils.get_pid(self.config['bundle_name']) + assert self.pid != 0, f'Failed to get pid of {self.config["bundle_name"]}' + Step('3.设置屏幕常亮') + self.ui_utils.keep_awake() + + def process(self): + Step('4.获取主页Text控件') + text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + CheckPoint('检查主页的文本内容') + self.common_utils.assert_equal(text_component.text, 'Hello World') + + Step('5.点击Text控件') + text_component.click() + CheckPoint('检查点击后主页的文本') + text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + self.common_utils.assert_equal(text_component.text, 'Hello World_8') + + Step('6.触发热重载') + self.driver.Storage.push_file(local_path=self.config['local_hqf_entry_path'], + device_path=self.config['remote_hqf_entry_path']) + self.common_utils.hot_reload(self.config['remote_hqf_entry_path']) + + Step('7.点击Text控件') + text_component.click() + CheckPoint('检查点击后主页的文本,是否符合热重载的修改') + text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + self.common_utils.assert_equal(text_component.text, 'Hello World_8_48') + + CheckPoint('8.确保应用正常运行') + pid = self.common_utils.get_pid(self.config['bundle_name']) + assert pid == self.pid, f'App is no longer running with pid: {pid}' + + def teardown(self): + Step("9.卸载应用") + self.driver.uninstall_app(self.config['bundle_name']) \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/hotreload/TestHotReloadMultiModule.json b/test/autotest/testcases/toolchain/hotreload/TestHotReloadMultiModule.json new file mode 100644 index 00000000..f115b42e --- /dev/null +++ b/test/autotest/testcases/toolchain/hotreload/TestHotReloadMultiModule.json @@ -0,0 +1,15 @@ +{ + "description": "Config for TestHotReloadMultiModule", + "environment": [ + { + "type": "device", + "label": "phone" + } + ], + "driver": { + "type": "DeviceTest", + "py_file": [ + "toolchain/hotreload/TestHotReloadMultiModule.py" + ] + } +} \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/hotreload/TestHotReloadMultiModule.py b/test/autotest/testcases/toolchain/hotreload/TestHotReloadMultiModule.py new file mode 100644 index 00000000..b2b8680e --- /dev/null +++ b/test/autotest/testcases/toolchain/hotreload/TestHotReloadMultiModule.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 Huawei Device Co., Ltd. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +#================================================================== +#文 件 名: TestHotReloadMultiModule.py +#文件说明: 同时修改hqf和hsp两个module中的文件,触发热重载后,再触发冷补丁 +#================================================================== +测试步骤: + 1、启动应用,加载Index页面 + 2、同时修改hqf和hsp中的文件(hqf引用了hsp),触发热重载 + 3、退出应用后重启,触发冷补丁 +#================================================================== +关键代码: + 修改前: + hqf => Index.ets: + import {separator} from './Styles' + import {add} from "hsplibrary" + .onClick(()=>{ + this.message += separator + this.message += add(1, 2) + }) + hsp => Calc.ets: + import {factor} from './Factors' + export function add(a: number, b: number) { + return (a + b) * factor(a, b); + } + 修改后: + hqf => Index.ets: + import {separator} from './Styles' + import {add} from "hsplibrary" + .onClick(()=>{ + this.message += separator + this.message += add(2, 2) + }) + hsp => Calc.ets: + import {factor} from './Factors' + export function add(a: number, b: number) { + return (a + b) + factor(a, b); + } +#!!================================================================ +""" +import os +import sys +from pathlib import Path + +root_path = Path(__file__).parent.parent.parent.parent +resource_path = root_path / 'resource' +sys.path.append(str(root_path / 'aw')) # add aw path to sys.path + +from devicetest.core.test_case import TestCase, Step, CheckPoint +from hypium import UiDriver +from all_utils import CommonUtils, UiUtils + + +class TestHotReloadMultiModule(TestCase): + def __init__(self, controllers): + self.TAG = self.__class__.__name__ + TestCase.__init__(self, self.TAG, controllers) + self.driver = UiDriver(self.device1) + self.ui_utils = UiUtils(self.driver) + self.common_utils = CommonUtils(self.driver) + self.pid = -1 + self.config = { + 'bundle_name': 'com.example.HotReloadHsp01', + 'hap_path': [str(resource_path / 'hap' / 'HotReloadHsp01' / 'HotReloadHsp01.hap'), + str(resource_path / 'hap' / 'HotReloadHsp01' / 'HspLibrary.hsp')], + 'local_hqf_path': [str(resource_path / 'hqf' / 'HotReloadHsp01' / 'HotReloadHsp01.hqf'), + str(resource_path / 'hqf' / 'HotReloadHsp01' / 'HspLibrary.hqf')], + 'remote_hqf_path': ['/data/HotReloadHsp01.hqf', '/data/HspLibrary.hqf'] + } + + def setup(self): + Step('1.安装应用') + self.driver.install_app(self.config['hap_path'], "-r") + Step('2.启动应用') + self.driver.start_app(package_name=self.config['bundle_name']) + self.pid = self.common_utils.get_pid(self.config['bundle_name']) + assert self.pid != 0, f'Failed to get pid of {self.config["bundle_name"]}' + Step('3.设置屏幕常亮') + self.ui_utils.keep_awake() + + def process(self): + Step('3.获取主页Text控件') + text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + CheckPoint('检查主页的文本内容') + self.common_utils.assert_equal(text_component.text, 'Hello World') + + Step('4.点击主页Text控件') + text_component.click() + CheckPoint('检查点击后的文本') + text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + self.common_utils.assert_equal(text_component.text, 'Hello World_3') + + Step('5.同时修改hap和hsp,触发热重载') + self.driver.Storage.push_file(local_path=self.config['local_hqf_path'][0], + device_path=self.config['remote_hqf_path'][0]) + self.driver.Storage.push_file(local_path=self.config['local_hqf_path'][1], + device_path=self.config['remote_hqf_path'][1]) + self.common_utils.hot_reload(self.config['remote_hqf_path']) + + Step('6.点击主页Text控件') + text_component.click() + CheckPoint('检查点击后的文本') + text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + self.common_utils.assert_equal(text_component.text, 'Hello World_3_6') + + Step('7.退出应用') + self.driver.stop_app(package_name=self.config['bundle_name']) + + Step('8.重新启动应用,触发冷补丁') + self.driver.start_app(package_name=self.config['bundle_name']) + + foreground_apps = self.driver.AppManager.get_foreground_apps() + assert any(app['bundleName'] == self.config['bundle_name'] for app in foreground_apps), \ + 'Cold patch: app start failed!' + + Step('9.点击主页Text控件') + text_component.click() + CheckPoint('检查点击后的文本') + text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + self.common_utils.assert_equal(text_component.text, 'Hello World_6') + + def teardown(self): + Step("10.卸载应用") + self.driver.uninstall_app(self.config['bundle_name']) \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/hotreload/TestHotReloadPages01.json b/test/autotest/testcases/toolchain/hotreload/TestHotReloadPages01.json new file mode 100644 index 00000000..54ca2722 --- /dev/null +++ b/test/autotest/testcases/toolchain/hotreload/TestHotReloadPages01.json @@ -0,0 +1,15 @@ +{ + "description": "Config for TestHotReloadPages01", + "environment": [ + { + "type": "device", + "label": "phone" + } + ], + "driver": { + "type": "DeviceTest", + "py_file": [ + "toolchain/hotreload/TestHotReloadPages01.py" + ] + } +} \ No newline at end of file diff --git a/test/autotest/testcases/toolchain/hotreload/TestHotReloadPages01.py b/test/autotest/testcases/toolchain/hotreload/TestHotReloadPages01.py new file mode 100644 index 00000000..a7054904 --- /dev/null +++ b/test/autotest/testcases/toolchain/hotreload/TestHotReloadPages01.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Copyright (c) 2025 Huawei Device Co., Ltd. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +#================================================================== +#文 件 名: TestHotReloadPages01.py +#文件说明: 跳转页面未启动时,对其进行热重载 +#================================================================== +测试步骤: + 1、启动应用,加载Index.ets页面 + 2、修改Index.ets,触发热重载 + 3、修改Index.ets和跳转页面Mine.ets,触发热重载 + 4、点击Index.ets屏幕中央,跳转到Mine.ets页面,点击Mine.ets页面屏幕中央,this.message被修改 +#================================================================== +关键代码: + 修改前: + Index.ets + .onClick(() => { + router.pushUrl({'url': 'pages/Mine'}) + }) + .buttonText(); + + @Extend(Text) + function buttonText() { + ... + .backgroundColor(Color.Red) + ... + } + Mine.ets + import {add} from './Utils' + .onClick(() => { + this.message += '_' + this.message += add(1, 2) + }) + 第一次修改: + Index.ets + @Extend(Text) + function buttonText() { + ... + .backgroundColor(Color.Green) + ... + } + 第二次修改: + Index.ets + @Extend(Text) + function buttonText() { + ... + .backgroundColor(Color.Green) + ... + } + Mine.ets + .onClick(() => { + this.message += '_' + this.message += add(3, 4) + }) +#!!================================================================ +""" +import os +import sys +from pathlib import Path + +root_path = Path(__file__).parent.parent.parent.parent +resource_path = root_path / 'resource' +sys.path.append(str(root_path / 'aw')) # add aw path to sys.path + +from devicetest.core.test_case import TestCase, Step, CheckPoint +from hypium import UiDriver +from all_utils import CommonUtils, UiUtils + + +class TestHotReloadPages01(TestCase): + def __init__(self, controllers): + self.TAG = self.__class__.__name__ + TestCase.__init__(self, self.TAG, controllers) + self.driver = UiDriver(self.device1) + self.ui_utils = UiUtils(self.driver) + self.common_utils = CommonUtils(self.driver) + self.pid = -1 + self.config = { + 'bundle_name': 'com.example.HotReloadPages01', + 'hap_path': str(resource_path / 'hap' / 'HotReloadPages01.hap'), + + 'local_hqf_01_path': str(resource_path / 'hqf' / 'HotReloadPages01.01.hqf'), + 'remote_hqf_01_path': '/data/HotReloadPages01.01.hqf', + + 'local_hqf_02_path': str(resource_path / 'hqf' / 'HotReloadPages01.02.hqf'), + 'remote_hqf_02_path': '/data/HotReloadPages01.02.hqf', + } + + def setup(self): + Step('1.安装应用') + self.driver.install_app(self.config['hap_path'], "-r") + Step('2.启动应用') + self.driver.start_app(package_name=self.config['bundle_name']) + self.pid = self.common_utils.get_pid(self.config['bundle_name']) + assert self.pid != 0, f'Failed to get pid of {self.config["bundle_name"]}' + Step('3.设置屏幕常亮') + self.ui_utils.keep_awake() + + def process(self): + Step('4.获取Index.ets页面Text控件') + index_text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + CheckPoint('检查主页的文本和背景颜色') + self.common_utils.assert_equal(index_text_component.text, 'Click Me') + self.common_utils.assert_equal(index_text_component.attr['backgroundColor'], '#FFFF0000') + + Step('5.修改Index.ets,第一次触发热重载') + self.driver.Storage.push_file(local_path=self.config['local_hqf_01_path'], + device_path=self.config['remote_hqf_01_path']) + self.common_utils.hot_reload(self.config['remote_hqf_01_path']) + CheckPoint('检查热重载后,Index.ets的文本背景颜色') + index_text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + self.common_utils.assert_equal(index_text_component.attr['backgroundColor'], '#FF008000') + + Step('6.修改Index.ets和Mine.ets,第二次触发热重载') + self.driver.Storage.push_file(local_path=self.config['local_hqf_02_path'], + device_path=self.config['remote_hqf_02_path']) + self.common_utils.hot_reload(self.config['remote_hqf_02_path']) + CheckPoint('检查热重载后,Index.ets的文本背景颜色') + index_text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + self.common_utils.assert_equal(index_text_component.attr['backgroundColor'], '#FFFFFF00') + + Step('7.点击Index.ets,跳转到Mine.ets') + index_text_component.click() + CheckPoint('检查点击后Mine.ets的文本') + mine_text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + self.common_utils.assert_equal(mine_text_component.text, 'Mine View') + + Step('8.点击Mine.ets,检查文本变化') + mine_text_component.click() + mine_text_component = self.driver.UiTree.find_component_by_path('/root/RelativeContainer/Text') + self.common_utils.assert_equal(mine_text_component.text, 'Mine View_3') + + CheckPoint('9.确保应用正常运行') + pid = self.common_utils.get_pid(self.config['bundle_name']) + assert pid == self.pid, self.driver.log_error(f'App is no longer running with pid: {pid}') + + def teardown(self): + Step("10.卸载应用") + self.driver.uninstall_app(self.config['bundle_name']) \ No newline at end of file -- Gitee