freecad-cam/Mod/AddonManager/AddonManagerTest/app/test_addon.py
2026-02-01 01:59:24 +01:00

400 lines
15 KiB
Python

# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2022-2023 FreeCAD Project Association *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import tempfile
import unittest
import os
import sys
sys.path.append("../../")
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(os.path.dirname(__file__), "..", "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",
)
self.assertEqual(addon.name, "FreeCAD")
self.assertEqual(addon.display_name, "FreeCAD")
# Case 2: Package.xml metadata file sets a display name:
addon.load_metadata_file(os.path.join(self.test_dir, "good_package.xml"))
self.assertEqual(addon.name, "FreeCAD")
self.assertEqual(addon.display_name, "Test Workbench")
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 "]
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.load_metadata_file(os.path.join(self.test_dir, "good_package.xml"))
tags = addon.tags
self.assertEqual(len(tags), 5)
expected_tags = set()
expected_tags.add("Tag0")
expected_tags.add("Tag1")
expected_tags.add("TagA")
expected_tags.add("TagB")
expected_tags.add("TagC")
self.assertEqual(expected_tags, 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 integration 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.assertNotEqual(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("CAM")
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(
"CAM" in deps.internal_workbenches,
"CAM 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"))
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"))
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",
)
def test_try_find_wbname_in_files_empty_dir(self):
with tempfile.TemporaryDirectory() as mod_dir:
# Arrange
test_addon = Addon("test")
test_addon.mod_directory = mod_dir
os.mkdir(os.path.join(mod_dir, test_addon.name))
# Act
wb_name = test_addon.try_find_wbname_in_files()
# Assert
self.assertEqual(wb_name, "")
def test_try_find_wbname_in_files_non_python_ignored(self):
with tempfile.TemporaryDirectory() as mod_dir:
# Arrange
test_addon = Addon("test")
test_addon.mod_directory = mod_dir
base_path = os.path.join(mod_dir, test_addon.name)
os.mkdir(base_path)
file_path = os.path.join(base_path, "test.txt")
with open(file_path, "w", encoding="utf-8") as f:
f.write("Gui.addWorkbench(TestWorkbench())")
# Act
wb_name = test_addon.try_find_wbname_in_files()
# Assert
self.assertEqual(wb_name, "")
def test_try_find_wbname_in_files_simple(self):
with tempfile.TemporaryDirectory() as mod_dir:
# Arrange
test_addon = Addon("test")
test_addon.mod_directory = mod_dir
base_path = os.path.join(mod_dir, test_addon.name)
os.mkdir(base_path)
file_path = os.path.join(base_path, "test.py")
with open(file_path, "w", encoding="utf-8") as f:
f.write("Gui.addWorkbench(TestWorkbench())")
# Act
wb_name = test_addon.try_find_wbname_in_files()
# Assert
self.assertEqual(wb_name, "TestWorkbench")
def test_try_find_wbname_in_files_subdir(self):
with tempfile.TemporaryDirectory() as mod_dir:
# Arrange
test_addon = Addon("test")
test_addon.mod_directory = mod_dir
base_path = os.path.join(mod_dir, test_addon.name)
os.mkdir(base_path)
subdir = os.path.join(base_path, "subdirectory")
os.mkdir(subdir)
file_path = os.path.join(subdir, "test.py")
with open(file_path, "w", encoding="utf-8") as f:
f.write("Gui.addWorkbench(TestWorkbench())")
# Act
wb_name = test_addon.try_find_wbname_in_files()
# Assert
self.assertEqual(wb_name, "TestWorkbench")
def test_try_find_wbname_in_files_variable_used(self):
with tempfile.TemporaryDirectory() as mod_dir:
# Arrange
test_addon = Addon("test")
test_addon.mod_directory = mod_dir
base_path = os.path.join(mod_dir, test_addon.name)
os.mkdir(base_path)
file_path = os.path.join(base_path, "test.py")
with open(file_path, "w", encoding="utf-8") as f:
f.write("Gui.addWorkbench(wb)")
# Act
wb_name = test_addon.try_find_wbname_in_files()
# Assert
self.assertEqual(wb_name, "")
def test_try_find_wbname_in_files_variants(self):
variants = [
"Gui.addWorkbench(TestWorkbench())",
"Gui.addWorkbench (TestWorkbench())",
"Gui.addWorkbench( TestWorkbench() )",
"Gui.addWorkbench(TestWorkbench( ))",
"Gui.addWorkbench( TestWorkbench( ) )",
"Gui.addWorkbench( TestWorkbench ( ) )",
"Gui.addWorkbench ( TestWorkbench ( ) )",
]
for variant in variants:
with self.subTest(variant=variant):
with tempfile.TemporaryDirectory() as mod_dir:
# Arrange
test_addon = Addon("test")
test_addon.mod_directory = mod_dir
base_path = os.path.join(mod_dir, test_addon.name)
os.mkdir(base_path)
file_path = os.path.join(base_path, "test.py")
with open(file_path, "w", encoding="utf-8") as f:
f.write(variant)
# Act
wb_name = test_addon.try_find_wbname_in_files()
# Assert
self.assertEqual(wb_name, "TestWorkbench")