Addon Manager: Refactor installation code

Improve testability of installation code by refactoring it to completely
separate the GUI and non-GUI code, and to provide more robust support
for non-GUI access to some type of Addon Manager activity.
This commit is contained in:
Chris Hennes
2022-11-18 18:51:04 -06:00
parent 403e0dc477
commit 89c191e160
46 changed files with 4012 additions and 1666 deletions

View File

@@ -29,17 +29,25 @@ import FreeCAD
from Addon import Addon, INTERNAL_WORKBENCHES
from addonmanager_macro import Macro
class TestAddon(unittest.TestCase):
MODULE = "test_addon" # file name without extension
def setUp(self):
self.test_dir = os.path.join(FreeCAD.getHomePath(), "Mod", "AddonManager", "AddonManagerTest", "data")
self.test_dir = os.path.join(
FreeCAD.getHomePath(), "Mod", "AddonManager", "AddonManagerTest", "data"
)
def test_display_name(self):
# Case 1: No display name set elsewhere: name == display_name
addon = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
self.assertEqual(addon.name, "FreeCAD")
self.assertEqual(addon.display_name, "FreeCAD")
@@ -49,51 +57,68 @@ class TestAddon(unittest.TestCase):
self.assertEqual(addon.display_name, "Test Workbench")
def test_metadata_loading(self):
addon = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
addon.load_metadata_file(os.path.join(self.test_dir, "good_package.xml"))
# Generic tests:
self.assertIsNotNone(addon.metadata)
self.assertEqual(addon.metadata.Version, "1.0.1")
self.assertEqual(addon.metadata.Description, "A package.xml file for unit testing.")
self.assertEqual(
addon.metadata.Description, "A package.xml file for unit testing."
)
maintainer_list = addon.metadata.Maintainer
self.assertEqual(len(maintainer_list),1,"Wrong number of maintainers found")
self.assertEqual(maintainer_list[0]["name"],"FreeCAD Developer")
self.assertEqual(maintainer_list[0]["email"],"developer@freecad.org")
self.assertEqual(len(maintainer_list), 1, "Wrong number of maintainers found")
self.assertEqual(maintainer_list[0]["name"], "FreeCAD Developer")
self.assertEqual(maintainer_list[0]["email"], "developer@freecad.org")
license_list = addon.metadata.License
self.assertEqual(len(license_list),1,"Wrong number of licenses found")
self.assertEqual(license_list[0]["name"],"LGPLv2.1")
self.assertEqual(license_list[0]["file"],"LICENSE")
self.assertEqual(len(license_list), 1, "Wrong number of licenses found")
self.assertEqual(license_list[0]["name"], "LGPLv2.1")
self.assertEqual(license_list[0]["file"], "LICENSE")
url_list = addon.metadata.Urls
self.assertEqual(len(url_list),2,"Wrong number of urls found")
self.assertEqual(url_list[0]["type"],"repository")
self.assertEqual(url_list[0]["location"],"https://github.com/chennes/FreeCAD-Package")
self.assertEqual(url_list[0]["branch"],"main")
self.assertEqual(url_list[1]["type"],"readme")
self.assertEqual(url_list[1]["location"],"https://github.com/chennes/FreeCAD-Package/blob/main/README.md")
self.assertEqual(len(url_list), 2, "Wrong number of urls found")
self.assertEqual(url_list[0]["type"], "repository")
self.assertEqual(
url_list[0]["location"], "https://github.com/chennes/FreeCAD-Package"
)
self.assertEqual(url_list[0]["branch"], "main")
self.assertEqual(url_list[1]["type"], "readme")
self.assertEqual(
url_list[1]["location"],
"https://github.com/chennes/FreeCAD-Package/blob/main/README.md",
)
contents = addon.metadata.Content
self.assertEqual(len(contents),1,"Wrong number of content catetories found")
self.assertEqual(len(contents["workbench"]),1,"Wrong number of workbenches found")
self.assertEqual(len(contents), 1, "Wrong number of content catetories found")
self.assertEqual(
len(contents["workbench"]), 1, "Wrong number of workbenches found"
)
def test_git_url_cleanup(self):
base_url = "https://github.com/FreeCAD/FreeCAD"
test_urls = [f" {base_url} ",
f"{base_url}.git",
f" {base_url}.git "]
test_urls = [f" {base_url} ", f"{base_url}.git", f" {base_url}.git "]
for url in test_urls:
addon = Addon("FreeCAD", url, Addon.Status.NOT_INSTALLED, "master")
self.assertEqual(addon.url, base_url)
def test_tag_extraction(self):
addon = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
addon.load_metadata_file(os.path.join(self.test_dir, "good_package.xml"))
tags = addon.tags
self.assertEqual(len(tags),5)
self.assertEqual(len(tags), 5)
expected_tags = set()
expected_tags.add("Tag0")
expected_tags.add("Tag1")
@@ -107,41 +132,79 @@ class TestAddon(unittest.TestCase):
# Test package.xml combinations:
# Workbenches
addon_with_workbench = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon_with_workbench.load_metadata_file(os.path.join(self.test_dir, "workbench_only.xml"))
addon_with_workbench = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
addon_with_workbench.load_metadata_file(
os.path.join(self.test_dir, "workbench_only.xml")
)
self.assertTrue(addon_with_workbench.contains_workbench())
self.assertFalse(addon_with_workbench.contains_macro())
self.assertFalse(addon_with_workbench.contains_preference_pack())
# Macros
addon_with_macro = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon_with_macro.load_metadata_file(os.path.join(self.test_dir, "macro_only.xml"))
addon_with_macro = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
addon_with_macro.load_metadata_file(
os.path.join(self.test_dir, "macro_only.xml")
)
self.assertFalse(addon_with_macro.contains_workbench())
self.assertTrue(addon_with_macro.contains_macro())
self.assertFalse(addon_with_macro.contains_preference_pack())
# Preference Packs
addon_with_prefpack = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon_with_prefpack.load_metadata_file(os.path.join(self.test_dir, "prefpack_only.xml"))
addon_with_prefpack = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
addon_with_prefpack.load_metadata_file(
os.path.join(self.test_dir, "prefpack_only.xml")
)
self.assertFalse(addon_with_prefpack.contains_workbench())
self.assertFalse(addon_with_prefpack.contains_macro())
self.assertTrue(addon_with_prefpack.contains_preference_pack())
# Combination
addon_with_all = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon_with_all.load_metadata_file(os.path.join(self.test_dir, "combination.xml"))
addon_with_all = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
addon_with_all.load_metadata_file(
os.path.join(self.test_dir, "combination.xml")
)
self.assertTrue(addon_with_all.contains_workbench())
self.assertTrue(addon_with_all.contains_macro())
self.assertTrue(addon_with_all.contains_preference_pack())
# Now do the simple, explicitly-set cases
addon_wb = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon_wb = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
addon_wb.repo_type = Addon.Kind.WORKBENCH
self.assertTrue(addon_wb.contains_workbench())
self.assertFalse(addon_wb.contains_macro())
self.assertFalse(addon_wb.contains_preference_pack())
addon_m = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon_m = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
addon_m.repo_type = Addon.Kind.MACRO
self.assertFalse(addon_m.contains_workbench())
self.assertTrue(addon_m.contains_macro())
@@ -157,18 +220,25 @@ class TestAddon(unittest.TestCase):
addon = Addon.from_macro(macro)
self.assertEqual(addon.repo_type, Addon.Kind.MACRO)
self.assertEqual(addon.name,"DoNothing")
self.assertEqual(addon.macro.comment,"Do absolutely nothing. For Addon Manager unit tests.")
self.assertEqual(addon.url,"https://github.com/FreeCAD/FreeCAD")
self.assertEqual(addon.macro.version,"1.0")
self.assertEqual(len(addon.macro.other_files),3)
self.assertEqual(addon.macro.author,"Chris Hennes")
self.assertEqual(addon.macro.date,"2022-02-28")
self.assertEqual(addon.macro.icon,"not_real.png")
self.assertEqual(addon.macro.xpm,"")
self.assertEqual(addon.name, "DoNothing")
self.assertEqual(
addon.macro.comment, "Do absolutely nothing. For Addon Manager unit tests."
)
self.assertEqual(addon.url, "https://github.com/FreeCAD/FreeCAD")
self.assertEqual(addon.macro.version, "1.0")
self.assertEqual(len(addon.macro.other_files), 3)
self.assertEqual(addon.macro.author, "Chris Hennes")
self.assertEqual(addon.macro.date, "2022-02-28")
self.assertEqual(addon.macro.icon, "not_real.png")
self.assertEqual(addon.macro.xpm, "")
def test_cache(self):
addon = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
cache_data = addon.to_cache()
second_addon = Addon.from_cache(cache_data)
@@ -176,10 +246,30 @@ class TestAddon(unittest.TestCase):
def test_dependency_resolution(self):
addonA = Addon("AddonA","https://github.com/FreeCAD/FakeAddonA", Addon.Status.NOT_INSTALLED, "master")
addonB = Addon("AddonB","https://github.com/FreeCAD/FakeAddonB", Addon.Status.NOT_INSTALLED, "master")
addonC = Addon("AddonC","https://github.com/FreeCAD/FakeAddonC", Addon.Status.NOT_INSTALLED, "master")
addonD = Addon("AddonD","https://github.com/FreeCAD/FakeAddonD", Addon.Status.NOT_INSTALLED, "master")
addonA = Addon(
"AddonA",
"https://github.com/FreeCAD/FakeAddonA",
Addon.Status.NOT_INSTALLED,
"master",
)
addonB = Addon(
"AddonB",
"https://github.com/FreeCAD/FakeAddonB",
Addon.Status.NOT_INSTALLED,
"master",
)
addonC = Addon(
"AddonC",
"https://github.com/FreeCAD/FakeAddonC",
Addon.Status.NOT_INSTALLED,
"master",
)
addonD = Addon(
"AddonD",
"https://github.com/FreeCAD/FakeAddonD",
Addon.Status.NOT_INSTALLED,
"master",
)
addonA.requires.add("AddonB")
addonB.requires.add("AddonC")
@@ -191,30 +281,69 @@ class TestAddon(unittest.TestCase):
addonB.name: addonB,
addonC.name: addonC,
addonD.name: addonD,
}
}
deps = Addon.Dependencies()
addonA.walk_dependency_tree(all_addons, deps)
self.assertEqual(len(deps.required_external_addons),3)
self.assertEqual(len(deps.required_external_addons), 3)
addon_strings = [addon.name for addon in deps.required_external_addons]
self.assertTrue("AddonB" in addon_strings, "AddonB not in required dependencies, and it should be.")
self.assertTrue("AddonC" in addon_strings, "AddonC not in required dependencies, and it should be.")
self.assertTrue("AddonD" in addon_strings, "AddonD not in required dependencies, and it should be.")
self.assertTrue("Path" in deps.internal_workbenches, "Path not in workbench dependencies, and it should be.")
self.assertTrue(
"AddonB" in addon_strings,
"AddonB not in required dependencies, and it should be.",
)
self.assertTrue(
"AddonC" in addon_strings,
"AddonC not in required dependencies, and it should be.",
)
self.assertTrue(
"AddonD" in addon_strings,
"AddonD not in required dependencies, and it should be.",
)
self.assertTrue(
"Path" in deps.internal_workbenches,
"Path not in workbench dependencies, and it should be.",
)
def test_internal_workbench_list(self):
addon = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon.load_metadata_file(os.path.join(self.test_dir, "depends_on_all_workbenches.xml"))
addon = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
addon.load_metadata_file(
os.path.join(self.test_dir, "depends_on_all_workbenches.xml")
)
deps = Addon.Dependencies()
addon.walk_dependency_tree({}, deps)
self.assertEqual(len(deps.internal_workbenches), len(INTERNAL_WORKBENCHES))
def test_version_check(self):
addon = Addon("FreeCAD","https://github.com/FreeCAD/FreeCAD", Addon.Status.NOT_INSTALLED, "master")
addon.load_metadata_file(os.path.join(self.test_dir, "test_version_detection.xml"))
addon = Addon(
"FreeCAD",
"https://github.com/FreeCAD/FreeCAD",
Addon.Status.NOT_INSTALLED,
"master",
)
addon.load_metadata_file(
os.path.join(self.test_dir, "test_version_detection.xml")
)
self.assertEqual(len(addon.tags),1, "Wrong number of tags found: version requirements should have restricted to only one")
self.assertFalse("TagA" in addon.tags, "Found 'TagA' in tags, it should have been excluded by version requirement")
self.assertTrue("TagB" in addon.tags, "Failed to find 'TagB' in tags, it should have been included")
self.assertFalse("TagC" in addon.tags, "Found 'TagA' in tags, it should have been excluded by version requirement")
self.assertEqual(
len(addon.tags),
1,
"Wrong number of tags found: version requirements should have restricted to only one",
)
self.assertFalse(
"TagA" in addon.tags,
"Found 'TagA' in tags, it should have been excluded by version requirement",
)
self.assertTrue(
"TagB" in addon.tags,
"Failed to find 'TagB' in tags, it should have been included",
)
self.assertFalse(
"TagC" in addon.tags,
"Found 'TagA' in tags, it should have been excluded by version requirement",
)