From 60655ab50db26e9b5706525c361e073468d5c550 Mon Sep 17 00:00:00 2001 From: Tony Coder <407243179@qq.com> Date: Fri, 21 Aug 2026 00:31:18 +0000 Subject: [PATCH] fix: treat only home-action skills as launcher abilities get_app_abilities used `or`, so any non-empty skills list was marked launcher. Empty skills hit skills[0] and the ability was dropped. Use `and` and guard missing or empty actions. Signed-off-by: Tony Coder <407243179@qq.com> --- hmdriver2/driver.py | 2 +- tests/fixtures/bm_dump_app_abilities.json | 52 +++++++++++++++ tests/test_app_abilities.py | 77 +++++++++++++++++++++++ 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/bm_dump_app_abilities.json create mode 100644 tests/test_app_abilities.py diff --git a/hmdriver2/driver.py b/hmdriver2/driver.py index 5d588cc..fe79b48 100644 --- a/hmdriver2/driver.py +++ b/hmdriver2/driver.py @@ -188,7 +188,7 @@ def get_app_abilities(self, package_name: str) -> List[Dict]: try: is_launcher_ability = False skills = ability_info['skills'] - if len(skills) > 0 or "action.system.home" in skills[0]["actions"]: + if len(skills) > 0 and "action.system.home" in (skills[0].get("actions") or []): is_launcher_ability = True icon_ability_info = { "name": ability_info["name"], diff --git a/tests/fixtures/bm_dump_app_abilities.json b/tests/fixtures/bm_dump_app_abilities.json new file mode 100644 index 0000000..4d9152f --- /dev/null +++ b/tests/fixtures/bm_dump_app_abilities.json @@ -0,0 +1,52 @@ +{ + "appId": "com.samples.test.uitest_test_signature", + "compatibleVersion": 40100011, + "cpuAbi": "arm64-v8a", + "hapModuleInfos": [ + { + "mainAbility": "EntryAbility", + "moduleName": "entry", + "abilityInfos": [ + { + "name": "EmptySkillsAbility", + "moduleName": "entry", + "skills": [] + }, + { + "name": "ShareAbility", + "moduleName": "entry", + "skills": [ + { + "actions": ["ohos.want.action.sendData"], + "entities": ["entity.system.default"] + } + ] + }, + { + "name": "NoActionsAbility", + "moduleName": "entry", + "skills": [ + { + "entities": ["entity.system.default"] + } + ] + }, + { + "name": "EntryAbility", + "moduleName": "entry", + "skills": [ + { + "actions": ["action.system.home"], + "entities": ["entity.system.home"] + } + ] + } + ] + } + ], + "mainEntry": "entry", + "name": "com.samples.test.uitest", + "vendor": "samples", + "versionCode": 1000000, + "versionName": "1.0.0" +} diff --git a/tests/test_app_abilities.py b/tests/test_app_abilities.py new file mode 100644 index 0000000..6d7fa13 --- /dev/null +++ b/tests/test_app_abilities.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +import json +from pathlib import Path + +from hmdriver2.driver import Driver +from hmdriver2.proto import CommandResult + + +FIXTURE_PATH = Path(__file__).parent / "fixtures" / "bm_dump_app_abilities.json" + + +def _bm_dump_output() -> str: + """hdc shell `bm dump -n` prefixes the JSON object with the bundle name.""" + payload = FIXTURE_PATH.read_text(encoding="utf-8") + return f"com.samples.test.uitest:\n{payload}\n" + + +def _driver_from_bm_dump(output: str = None) -> Driver: + """Build a Driver that never talks to a Harmony device.""" + if output is None: + output = _bm_dump_output() + + driver = object.__new__(Driver) + + class _FakeHdc: + def shell(self, cmd): + return CommandResult(output, "", 0) + + driver.hdc = _FakeHdc() + return driver + + +def _abilities_by_name(driver: Driver): + abilities = driver.get_app_abilities("com.samples.test.uitest") + return {item["name"]: item for item in abilities} + + +def test_fixture_covers_empty_non_home_and_home_skills(): + dump = json.loads(FIXTURE_PATH.read_text(encoding="utf-8")) + skills_by_name = { + ability["name"]: ability["skills"] + for module in dump["hapModuleInfos"] + for ability in module["abilityInfos"] + } + assert skills_by_name["EmptySkillsAbility"] == [] + assert "action.system.home" not in skills_by_name["ShareAbility"][0].get("actions", []) + assert "action.system.home" in skills_by_name["EntryAbility"][0]["actions"] + + +def test_empty_skills_kept_and_not_launcher(): + by_name = _abilities_by_name(_driver_from_bm_dump()) + assert "EmptySkillsAbility" in by_name + assert by_name["EmptySkillsAbility"]["isLauncherAbility"] is False + + +def test_skills_without_home_action_not_launcher(): + by_name = _abilities_by_name(_driver_from_bm_dump()) + assert "ShareAbility" in by_name + assert by_name["ShareAbility"]["isLauncherAbility"] is False + assert "NoActionsAbility" in by_name + assert by_name["NoActionsAbility"]["isLauncherAbility"] is False + + +def test_skills_with_home_action_is_launcher(): + by_name = _abilities_by_name(_driver_from_bm_dump()) + assert "EntryAbility" in by_name + assert by_name["EntryAbility"]["isLauncherAbility"] is True + + +def test_get_app_main_ability_prefers_home_skill(): + main = _driver_from_bm_dump().get_app_main_ability("com.samples.test.uitest") + assert main["name"] == "EntryAbility" + assert main["isLauncherAbility"] is True + assert main["moduleName"] == "entry" + assert main["moduleMainAbility"] == "EntryAbility" + assert main["mainModule"] == "entry"