diff --git a/src/Mod/AddonManager/Addon.py b/src/Mod/AddonManager/Addon.py index 5844bc8b0f..4da5d282f6 100644 --- a/src/Mod/AddonManager/Addon.py +++ b/src/Mod/AddonManager/Addon.py @@ -80,9 +80,9 @@ class Addon: class Dependencies: def __init__(self): - self.required_external_addons = dict() - self.blockers = dict() - self.replaces = dict() + self.required_external_addons = [] # A list of Addons + self.blockers = [] # A list of Addons + self.replaces = [] # A list of Addons self.unrecognized_addons: Set[str] = set() self.python_required: Set[str] = set() self.python_optional: Set[str] = set() @@ -363,13 +363,17 @@ class Addon: return self.cached_icon_filename def walk_dependency_tree(self, all_repos, deps): - """Compute the total dependency tree for this repo (recursive)""" + """Compute the total dependency tree for this repo (recursive) + - all_repos is a dictionary of repos, keyed on the name of the repo + - deps is an Addon.Dependency object encapsulating all the types of dependency + information that may be needed. + """ deps.python_required |= self.python_requires deps.python_optional |= self.python_optional for dep in self.requires: if dep in all_repos: - if not dep in deps.required: + if not dep in deps.required_external_addons: deps.required_external_addons.append(all_repos[dep]) all_repos[dep].walk_dependency_tree(all_repos, deps) else: diff --git a/src/Mod/AddonManager/AddonManager.py b/src/Mod/AddonManager/AddonManager.py index 56bd714301..8062a2008b 100644 --- a/src/Mod/AddonManager/AddonManager.py +++ b/src/Mod/AddonManager/AddonManager.py @@ -934,7 +934,7 @@ class CommandAddonManager: FreeCAD.Console.PrintLog("The following addons are required:\n") for addon in deps.required_external_addons: - FreeCAD.Console.PrintLog(addon + "\n") + FreeCAD.Console.PrintLog(addon.name + "\n") FreeCAD.Console.PrintLog("The following Python modules are required:\n") for pyreq in deps.python_required: diff --git a/src/Mod/AddonManager/AddonManagerTest/app/test_addon.py b/src/Mod/AddonManager/AddonManagerTest/app/test_addon.py index 58aae06af7..2e1677e416 100644 --- a/src/Mod/AddonManager/AddonManagerTest/app/test_addon.py +++ b/src/Mod/AddonManager/AddonManagerTest/app/test_addon.py @@ -27,6 +27,7 @@ import os import FreeCAD from Addon import Addon +from addonmanager_macro import Macro class TestAddon(unittest.TestCase): @@ -99,4 +100,105 @@ class TestAddon(unittest.TestCase): expected_tags.add("TagA") expected_tags.add("TagB") expected_tags.add("TagC") - self.assertEqual(tags, expected_tags) \ No newline at end of file + self.assertEqual(tags, expected_tags) + + def test_contains_functions(self): + + # 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")) + 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")) + 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")) + 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")) + 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.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.repo_type = Addon.Kind.MACRO + self.assertFalse(addon_m.contains_workbench()) + self.assertTrue(addon_m.contains_macro()) + self.assertFalse(addon_m.contains_preference_pack()) + + # There is no equivalent for preference packs, they are always accompanied by a + # metadata file + + def test_create_from_macro(self): + macro_file = os.path.join(self.test_dir, "DoNothing.FCMacro") + macro = Macro("DoNothing") + macro.fill_details_from_file(macro_file) + 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,"") + + def test_cache(self): + 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) + + self.assertTrue(addon.__dict__, second_addon.__dict__) + + 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.requires.add("AddonB") + addonB.requires.add("AddonC") + addonB.requires.add("AddonD") + addonD.requires.add("Path") + + all_addons = { + addonA.name: addonA, + 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) + 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.unrecognized_addons, "Path not in unrecognized dependencies, and it should be.") \ No newline at end of file diff --git a/src/Mod/AddonManager/AddonManagerTest/data/DoNothing.FCMacro b/src/Mod/AddonManager/AddonManagerTest/data/DoNothing.FCMacro new file mode 100644 index 0000000000..dafdb02d2b --- /dev/null +++ b/src/Mod/AddonManager/AddonManagerTest/data/DoNothing.FCMacro @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +__Title__ = 'Do Nothing' +__Author__ = 'Chris Hennes' +__Version__ = '1.0' +__Date__ = '2022-02-28' +__Comment__ = 'Do absolutely nothing. For Addon Manager unit tests.' +__Web__ = 'https://github.com/FreeCAD/FreeCAD' +__Wiki__ = '' +__Icon__ = 'not_real.png' +__Help__ = 'Not much to help with' +__Status__ = 'Very Stable' +__Requires__ = '' +__Communication__ = 'Shout into the void' +__Files__ = 'file1.py, file2.py, file3.py' + +print("Well, not quite *nothing*... it does print this line out.") \ No newline at end of file diff --git a/src/Mod/AddonManager/AddonManagerTest/data/combination.xml b/src/Mod/AddonManager/AddonManagerTest/data/combination.xml new file mode 100644 index 0000000000..8b214a0f17 --- /dev/null +++ b/src/Mod/AddonManager/AddonManagerTest/data/combination.xml @@ -0,0 +1,28 @@ + + + Combination Test + A package.xml file for unit testing. + 1.0.1 + 2022-01-07 + FreeCAD Developer + LGPLv2.1 + https://github.com/chennes/FreeCAD-Package + https://github.com/chennes/FreeCAD-Package/blob/main/README.md + Resources/icons/PackageIcon.svg + Tag0 + Tag1 + + + + MyFirstWorkbench + Resources/icons/PackageIcon.svg + + + MyMacro.FCStd + + + MyFirstPack + + + + \ No newline at end of file diff --git a/src/Mod/AddonManager/AddonManagerTest/data/macro_only.xml b/src/Mod/AddonManager/AddonManagerTest/data/macro_only.xml new file mode 100644 index 0000000000..f28e004041 --- /dev/null +++ b/src/Mod/AddonManager/AddonManagerTest/data/macro_only.xml @@ -0,0 +1,22 @@ + + + Test Macros + A package.xml file for unit testing. + 1.0.1 + 2022-01-07 + FreeCAD Developer + LGPLv2.1 + https://github.com/chennes/FreeCAD-Package + https://github.com/chennes/FreeCAD-Package/blob/main/README.md + Resources/icons/PackageIcon.svg + + + + MyMacro.FCStd + + + MyOtherMacro.FCStd + + + + \ No newline at end of file diff --git a/src/Mod/AddonManager/AddonManagerTest/data/prefpack_only.xml b/src/Mod/AddonManager/AddonManagerTest/data/prefpack_only.xml new file mode 100644 index 0000000000..22731a7792 --- /dev/null +++ b/src/Mod/AddonManager/AddonManagerTest/data/prefpack_only.xml @@ -0,0 +1,24 @@ + + + Test Preference Packs + A package.xml file for unit testing. + 1.0.1 + 2022-01-07 + FreeCAD Developer + LGPLv2.1 + https://github.com/chennes/FreeCAD-Package + https://github.com/chennes/FreeCAD-Package/blob/main/README.md + + + + MyFirstPack + + + MySecondPack + + + MyThirdPack + + + + \ No newline at end of file diff --git a/src/Mod/AddonManager/AddonManagerTest/data/workbench_only.xml b/src/Mod/AddonManager/AddonManagerTest/data/workbench_only.xml new file mode 100644 index 0000000000..38cde82623 --- /dev/null +++ b/src/Mod/AddonManager/AddonManagerTest/data/workbench_only.xml @@ -0,0 +1,27 @@ + + + Test Workbenches + A package.xml file for unit testing. + 1.0.1 + 2022-01-07 + FreeCAD Developer + LGPLv2.1 + https://github.com/chennes/FreeCAD-Package + https://github.com/chennes/FreeCAD-Package/blob/main/README.md + + + + MyFirstWorkbench + Resources/icons/PackageIcon.svg + + + MySecondWorkbench + Resources/icons/PackageIcon.svg + + + MyThirdWorkbench + Resources/icons/PackageIcon.svg + + + + \ No newline at end of file diff --git a/src/Mod/AddonManager/CMakeLists.txt b/src/Mod/AddonManager/CMakeLists.txt index a81c4256e3..3945a93e0f 100644 --- a/src/Mod/AddonManager/CMakeLists.txt +++ b/src/Mod/AddonManager/CMakeLists.txt @@ -56,6 +56,11 @@ SET(AddonManagerTestsFiles_SRCS AddonManagerTest/data/good_macro_metadata.FCStd AddonManagerTest/data/missing_macro_metadata.FCStd AddonManagerTest/data/good_package.xml + AddonManagerTest/data/macro_only.xml + AddonManagerTest/data/workbench_only.xml + AddonManagerTest/data/prefpack_only.xml + AddonManagerTest/data/combination.xml + AddonManagerTest/data/DoNothing.FCMacro ) SET(AddonManagerTests_ALL