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