Addon Manager: Refactor Metadata
Create a Python-native metadata class. Includes unit tests, and some PyLint cleanup.
This commit is contained in:
committed by
Chris Hennes
parent
243088a8c3
commit
0b241f78f4
@@ -74,57 +74,6 @@ class MockConsole:
|
||||
return counter
|
||||
|
||||
|
||||
class MockMetadata:
|
||||
"""Minimal implementation of a Metadata-like object."""
|
||||
|
||||
def __init__(self):
|
||||
self.Name = "MockMetadata"
|
||||
self.Urls = {"repository": {"location": "file://localhost/", "branch": "main"}}
|
||||
self.Description = "Mock metadata object for testing"
|
||||
self.Icon = None
|
||||
self.Version = "1.2.3beta"
|
||||
self.Content = {}
|
||||
|
||||
def minimal_file_scan(self, file: Union[os.PathLike, bytes]):
|
||||
"""Don't use the real metadata class, but try to read in the parameters we care about
|
||||
from the given metadata file (or file-like object, as the case probably is). This
|
||||
allows us to test whether the data is being passed around correctly."""
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
xml = None
|
||||
root = None
|
||||
try:
|
||||
if os.path.exists(file):
|
||||
xml = ElemTree.parse(file)
|
||||
root = xml.getroot()
|
||||
except TypeError:
|
||||
pass
|
||||
if xml is None:
|
||||
root = ElemTree.fromstring(file)
|
||||
if root is None:
|
||||
raise RuntimeError("Failed to parse XML data")
|
||||
|
||||
accepted_namespaces = ["", "{https://wiki.freecad.org/Package_Metadata}"]
|
||||
|
||||
for ns in accepted_namespaces:
|
||||
for child in root:
|
||||
if child.tag == ns + "name":
|
||||
self.Name = child.text
|
||||
elif child.tag == ns + "description":
|
||||
self.Description = child.text
|
||||
elif child.tag == ns + "icon":
|
||||
self.Icon = child.text
|
||||
elif child.tag == ns + "url":
|
||||
if "type" in child.attrib and child.attrib["type"] == "repository":
|
||||
url = child.text
|
||||
if "branch" in child.attrib:
|
||||
branch = child.attrib["branch"]
|
||||
else:
|
||||
branch = "master"
|
||||
self.Urls["repository"]["location"] = url
|
||||
self.Urls["repository"]["branch"] = branch
|
||||
|
||||
|
||||
class MockAddon:
|
||||
"""Minimal Addon class"""
|
||||
|
||||
@@ -161,18 +110,6 @@ class MockAddon:
|
||||
def set_status(self, status):
|
||||
self.update_status = status
|
||||
|
||||
def set_metadata(self, metadata_like: MockMetadata):
|
||||
"""Set (some) of the metadata, but don't use a real Metadata object"""
|
||||
self.metadata = metadata_like
|
||||
if "repository" in self.metadata.Urls:
|
||||
self.branch = self.metadata.Urls["repository"]["branch"]
|
||||
self.url = self.metadata.Urls["repository"]["location"]
|
||||
|
||||
def load_metadata_file(self, metadata_file: os.PathLike):
|
||||
if os.path.exists(metadata_file):
|
||||
self.metadata = MockMetadata()
|
||||
self.metadata.minimal_file_scan(metadata_file)
|
||||
|
||||
@staticmethod
|
||||
def get_best_icon_relative_path():
|
||||
return ""
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import FreeCAD
|
||||
import sys
|
||||
|
||||
sys.path.append("../../")
|
||||
|
||||
from Addon import Addon, INTERNAL_WORKBENCHES
|
||||
from addonmanager_macro import Macro
|
||||
@@ -35,7 +37,7 @@ class TestAddon(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.test_dir = os.path.join(
|
||||
FreeCAD.getHomePath(), "Mod", "AddonManager", "AddonManagerTest", "data"
|
||||
os.path.dirname(__file__), "..", "data"
|
||||
)
|
||||
|
||||
def test_display_name(self):
|
||||
@@ -55,51 +57,6 @@ class TestAddon(unittest.TestCase):
|
||||
self.assertEqual(addon.name, "FreeCAD")
|
||||
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.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."
|
||||
)
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
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",
|
||||
)
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
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 "]
|
||||
@@ -124,7 +81,7 @@ class TestAddon(unittest.TestCase):
|
||||
expected_tags.add("TagA")
|
||||
expected_tags.add("TagB")
|
||||
expected_tags.add("TagC")
|
||||
self.assertEqual(tags, expected_tags)
|
||||
self.assertEqual(expected_tags, tags)
|
||||
|
||||
def test_contains_functions(self):
|
||||
|
||||
|
||||
@@ -24,21 +24,20 @@
|
||||
"""Contains the unit test class for addonmanager_installer.py non-GUI functionality."""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
from zipfile import ZipFile
|
||||
import sys
|
||||
|
||||
sys.path.append("../../") # So the IDE can find the imports below
|
||||
|
||||
import FreeCAD
|
||||
|
||||
from typing import Dict
|
||||
|
||||
from addonmanager_installer import InstallationMethod, AddonInstaller, MacroInstaller
|
||||
|
||||
from addonmanager_git import GitManager, initialize_git
|
||||
|
||||
from addonmanager_metadata import MetadataReader
|
||||
from Addon import Addon
|
||||
|
||||
from AddonManagerTest.app.mocks import MockAddon, MockMacro
|
||||
|
||||
|
||||
@@ -100,17 +99,12 @@ class TestAddonInstaller(unittest.TestCase):
|
||||
AddonInstaller._validate_object(no_branch)
|
||||
|
||||
def test_update_metadata(self):
|
||||
"""If a metadata file exists in the installation location, it should be loaded."""
|
||||
installer = AddonInstaller(self.mock_addon, [])
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
installer.installation_path = temp_dir
|
||||
addon_dir = os.path.join(temp_dir, self.mock_addon.name)
|
||||
os.mkdir(addon_dir)
|
||||
shutil.copy(
|
||||
os.path.join(self.test_data_dir, "good_package.xml"),
|
||||
os.path.join(addon_dir, "package.xml"),
|
||||
)
|
||||
installer._update_metadata() # Does nothing, but should not crash
|
||||
"""If a metadata file exists in the installation location, it should be
|
||||
loaded."""
|
||||
addon = Mock()
|
||||
addon.name = "MockAddon"
|
||||
installer = AddonInstaller(addon, [])
|
||||
installer._update_metadata() # Does nothing, but should not crash
|
||||
|
||||
installer = AddonInstaller(self.real_addon, [])
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
@@ -122,12 +116,12 @@ class TestAddonInstaller(unittest.TestCase):
|
||||
os.path.join(self.test_data_dir, "good_package.xml"),
|
||||
os.path.join(addon_dir, "package.xml"),
|
||||
)
|
||||
good_metadata = FreeCAD.Metadata(os.path.join(addon_dir, "package.xml"))
|
||||
good_metadata = MetadataReader.from_file(os.path.join(addon_dir, "package.xml"))
|
||||
installer._update_metadata()
|
||||
self.assertEqual(self.real_addon.installed_version, good_metadata.Version)
|
||||
self.assertEqual(self.real_addon.installed_version, good_metadata.version)
|
||||
|
||||
def test_finalize_zip_installation_non_github(self):
|
||||
"""Ensure that zipfiles are correctly extracted."""
|
||||
"""Ensure that zip files are correctly extracted."""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
test_simple_repo = os.path.join(self.test_data_dir, "test_simple_repo.zip")
|
||||
non_gh_mock = MockAddon()
|
||||
@@ -160,52 +154,66 @@ class TestAddonInstaller(unittest.TestCase):
|
||||
"""When there is a subdirectory with the branch name in it, find it"""
|
||||
installer = AddonInstaller(self.mock_addon, [])
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
os.mkdir(os.path.join(temp_dir,f"{self.mock_addon.name}-{self.mock_addon.branch}"))
|
||||
os.mkdir(
|
||||
os.path.join(
|
||||
temp_dir, f"{self.mock_addon.name}-{self.mock_addon.branch}"
|
||||
)
|
||||
)
|
||||
result = installer._code_in_branch_subdirectory(temp_dir)
|
||||
self.assertTrue(result,"Failed to find ZIP subdirectory")
|
||||
self.assertTrue(result, "Failed to find ZIP subdirectory")
|
||||
|
||||
def test_code_in_branch_subdirectory_false(self):
|
||||
"""When there is not a subdirectory with the branch name in it, don't find one"""
|
||||
"""When there is not a subdirectory with the branch name in it, don't find
|
||||
one"""
|
||||
installer = AddonInstaller(self.mock_addon, [])
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
result = installer._code_in_branch_subdirectory(temp_dir)
|
||||
self.assertFalse(result,"Found ZIP subdirectory when there was none")
|
||||
self.assertFalse(result, "Found ZIP subdirectory when there was none")
|
||||
|
||||
def test_code_in_branch_subdirectory_more_than_one(self):
|
||||
"""When there are multiple subdirectories, never find a branch subdirectory"""
|
||||
installer = AddonInstaller(self.mock_addon, [])
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
os.mkdir(os.path.join(temp_dir,f"{self.mock_addon.name}-{self.mock_addon.branch}"))
|
||||
os.mkdir(os.path.join(temp_dir,"AnotherSubdir"))
|
||||
os.mkdir(
|
||||
os.path.join(
|
||||
temp_dir, f"{self.mock_addon.name}-{self.mock_addon.branch}"
|
||||
)
|
||||
)
|
||||
os.mkdir(os.path.join(temp_dir, "AnotherSubdir"))
|
||||
result = installer._code_in_branch_subdirectory(temp_dir)
|
||||
self.assertFalse(result,"Found ZIP subdirectory when there were multiple subdirs")
|
||||
self.assertFalse(
|
||||
result, "Found ZIP subdirectory when there were multiple subdirs"
|
||||
)
|
||||
|
||||
def test_move_code_out_of_subdirectory(self):
|
||||
"""All files are moved out and the subdirectory is deleted"""
|
||||
installer = AddonInstaller(self.mock_addon, [])
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
subdir = os.path.join(temp_dir,f"{self.mock_addon.name}-{self.mock_addon.branch}")
|
||||
subdir = os.path.join(
|
||||
temp_dir, f"{self.mock_addon.name}-{self.mock_addon.branch}"
|
||||
)
|
||||
os.mkdir(subdir)
|
||||
with open(os.path.join(subdir,"README.txt"),"w",encoding="utf-8") as f:
|
||||
with open(os.path.join(subdir, "README.txt"), "w", encoding="utf-8") as f:
|
||||
f.write("# Test file for unit testing")
|
||||
with open(os.path.join(subdir,"AnotherFile.txt"),"w",encoding="utf-8") as f:
|
||||
with open(
|
||||
os.path.join(subdir, "AnotherFile.txt"), "w", encoding="utf-8"
|
||||
) as f:
|
||||
f.write("# Test file for unit testing")
|
||||
installer._move_code_out_of_subdirectory(temp_dir)
|
||||
self.assertTrue(os.path.isfile(os.path.join(temp_dir,"README.txt")))
|
||||
self.assertTrue(os.path.isfile(os.path.join(temp_dir,"AnotherFile.txt")))
|
||||
self.assertTrue(os.path.isfile(os.path.join(temp_dir, "README.txt")))
|
||||
self.assertTrue(os.path.isfile(os.path.join(temp_dir, "AnotherFile.txt")))
|
||||
self.assertFalse(os.path.isdir(subdir))
|
||||
|
||||
|
||||
def test_install_by_git(self):
|
||||
"""Test using git to install. Depends on there being a local git installation: the test
|
||||
is skipped if there is no local git."""
|
||||
"""Test using git to install. Depends on there being a local git
|
||||
installation: the test is skipped if there is no local git."""
|
||||
git_manager = initialize_git()
|
||||
if not git_manager:
|
||||
self.skipTest("git not found, skipping git installer tests")
|
||||
return
|
||||
|
||||
# Our test git repo has to be in a zipfile, otherwise it cannot itself be stored in git,
|
||||
# since it has a .git subdirectory.
|
||||
# Our test git repo has to be in a zipfile, otherwise it cannot itself be
|
||||
# stored in git, since it has a .git subdirectory.
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
git_repo_zip = os.path.join(self.test_data_dir, "test_repo.zip")
|
||||
with ZipFile(git_repo_zip, "r") as zip_repo:
|
||||
@@ -334,28 +342,32 @@ class TestAddonInstaller(unittest.TestCase):
|
||||
self.assertEqual(method, InstallationMethod.ZIP)
|
||||
|
||||
def test_determine_install_method_https_known_sites_copy(self):
|
||||
"""Test which install methods are accepted for an https github URL"""
|
||||
"""Test which install methods are accepted for an https GitHub URL"""
|
||||
|
||||
installer = AddonInstaller(self.mock_addon, [])
|
||||
installer.git_manager = True
|
||||
|
||||
for site in ["github.org", "gitlab.org", "framagit.org", "salsa.debian.org"]:
|
||||
with self.subTest(site=site):
|
||||
temp_file = f"https://{site}/dummy/dummy" # Doesn't have to actually exist!
|
||||
temp_file = (
|
||||
f"https://{site}/dummy/dummy" # Doesn't have to actually exist!
|
||||
)
|
||||
method = installer._determine_install_method(
|
||||
temp_file, InstallationMethod.COPY
|
||||
)
|
||||
self.assertIsNone(method, f"Allowed copying from {site} URL")
|
||||
|
||||
def test_determine_install_method_https_known_sites_git(self):
|
||||
"""Test which install methods are accepted for an https github URL"""
|
||||
"""Test which install methods are accepted for an https GitHub URL"""
|
||||
|
||||
installer = AddonInstaller(self.mock_addon, [])
|
||||
installer.git_manager = True
|
||||
|
||||
for site in ["github.org", "gitlab.org", "framagit.org", "salsa.debian.org"]:
|
||||
with self.subTest(site=site):
|
||||
temp_file = f"https://{site}/dummy/dummy" # Doesn't have to actually exist!
|
||||
temp_file = (
|
||||
f"https://{site}/dummy/dummy" # Doesn't have to actually exist!
|
||||
)
|
||||
method = installer._determine_install_method(
|
||||
temp_file, InstallationMethod.GIT
|
||||
)
|
||||
@@ -366,14 +378,16 @@ class TestAddonInstaller(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_determine_install_method_https_known_sites_zip(self):
|
||||
"""Test which install methods are accepted for an https github URL"""
|
||||
"""Test which install methods are accepted for an https GitHub URL"""
|
||||
|
||||
installer = AddonInstaller(self.mock_addon, [])
|
||||
installer.git_manager = True
|
||||
|
||||
for site in ["github.org", "gitlab.org", "framagit.org", "salsa.debian.org"]:
|
||||
with self.subTest(site=site):
|
||||
temp_file = f"https://{site}/dummy/dummy" # Doesn't have to actually exist!
|
||||
temp_file = (
|
||||
f"https://{site}/dummy/dummy" # Doesn't have to actually exist!
|
||||
)
|
||||
method = installer._determine_install_method(
|
||||
temp_file, InstallationMethod.ZIP
|
||||
)
|
||||
@@ -384,14 +398,16 @@ class TestAddonInstaller(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_determine_install_method_https_known_sites_any_gm(self):
|
||||
"""Test which install methods are accepted for an https github URL"""
|
||||
"""Test which install methods are accepted for an https GitHub URL"""
|
||||
|
||||
installer = AddonInstaller(self.mock_addon, [])
|
||||
installer.git_manager = True
|
||||
|
||||
for site in ["github.org", "gitlab.org", "framagit.org", "salsa.debian.org"]:
|
||||
with self.subTest(site=site):
|
||||
temp_file = f"https://{site}/dummy/dummy" # Doesn't have to actually exist!
|
||||
temp_file = (
|
||||
f"https://{site}/dummy/dummy" # Doesn't have to actually exist!
|
||||
)
|
||||
method = installer._determine_install_method(
|
||||
temp_file, InstallationMethod.ANY
|
||||
)
|
||||
@@ -402,14 +418,16 @@ class TestAddonInstaller(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_determine_install_method_https_known_sites_any_no_gm(self):
|
||||
"""Test which install methods are accepted for an https github URL"""
|
||||
"""Test which install methods are accepted for an https GitHub URL"""
|
||||
|
||||
installer = AddonInstaller(self.mock_addon, [])
|
||||
installer.git_manager = None
|
||||
|
||||
for site in ["github.org", "gitlab.org", "framagit.org", "salsa.debian.org"]:
|
||||
with self.subTest(site=site):
|
||||
temp_file = f"https://{site}/dummy/dummy" # Doesn't have to actually exist!
|
||||
temp_file = (
|
||||
f"https://{site}/dummy/dummy" # Doesn't have to actually exist!
|
||||
)
|
||||
method = installer._determine_install_method(
|
||||
temp_file, InstallationMethod.ANY
|
||||
)
|
||||
@@ -436,7 +454,6 @@ class TestAddonInstaller(unittest.TestCase):
|
||||
|
||||
|
||||
class TestMacroInstaller(unittest.TestCase):
|
||||
|
||||
MODULE = "test_installer" # file name without extension
|
||||
|
||||
def setUp(self):
|
||||
@@ -448,8 +465,9 @@ class TestMacroInstaller(unittest.TestCase):
|
||||
def test_installation(self):
|
||||
"""Test the wrapper around the macro installer"""
|
||||
|
||||
# Note that this doesn't test the underlying Macro object's install function, it only
|
||||
# tests whether that function is called appropriately by the MacroInstaller wrapper.
|
||||
# Note that this doesn't test the underlying Macro object's install function,
|
||||
# it only tests whether that function is called appropriately by the
|
||||
# MacroInstaller wrapper.
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
installer = MacroInstaller(self.mock)
|
||||
installer.installation_path = temp_dir
|
||||
|
||||
@@ -21,18 +21,21 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import FreeCAD
|
||||
|
||||
from typing import Dict
|
||||
import unittest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
sys.path.append("../../") # So the IDE can find the
|
||||
|
||||
import FreeCAD
|
||||
|
||||
from addonmanager_macro import Macro
|
||||
|
||||
|
||||
class TestMacro(unittest.TestCase):
|
||||
|
||||
MODULE = "test_macro" # file name without extension
|
||||
|
||||
def setUp(self):
|
||||
@@ -183,49 +186,28 @@ static char * blarg_xpm[] = {
|
||||
return m
|
||||
|
||||
def test_fetch_raw_code_no_data(self):
|
||||
class MockNetworkManagerNoData:
|
||||
def __init__(self):
|
||||
self.fetched_url = None
|
||||
|
||||
def blocking_get(self, url):
|
||||
self.fetched_url = url
|
||||
return None
|
||||
|
||||
nmNoData = MockNetworkManagerNoData()
|
||||
m = Macro("Unit Test Macro")
|
||||
Macro.network_manager = nmNoData
|
||||
Macro.blocking_get = MagicMock(return_value=None)
|
||||
returned_data = m._fetch_raw_code(
|
||||
'rawcodeurl <a href="https://fake_url.com">Totally fake</a>'
|
||||
)
|
||||
self.assertIsNone(returned_data)
|
||||
self.assertEqual(nmNoData.fetched_url, "https://fake_url.com")
|
||||
m.blocking_get.assert_called_with("https://fake_url.com")
|
||||
Macro.blocking_get = None
|
||||
|
||||
nmNoData.fetched_url = None
|
||||
def test_fetch_raw_code_no_url(self):
|
||||
m = Macro("Unit Test Macro")
|
||||
Macro.blocking_get = MagicMock(return_value=None)
|
||||
returned_data = m._fetch_raw_code("Fake pagedata with no URL at all.")
|
||||
self.assertIsNone(returned_data)
|
||||
self.assertIsNone(nmNoData.fetched_url)
|
||||
|
||||
Macro.network_manager = None
|
||||
m.blocking_get.assert_not_called()
|
||||
Macro.blocking_get = None
|
||||
|
||||
def test_fetch_raw_code_with_data(self):
|
||||
class MockNetworkManagerWithData:
|
||||
class MockQByteArray:
|
||||
def data(self):
|
||||
return "Data returned to _fetch_raw_code".encode("utf-8")
|
||||
|
||||
def __init__(self):
|
||||
self.fetched_url = None
|
||||
|
||||
def blocking_get(self, url):
|
||||
self.fetched_url = url
|
||||
return MockNetworkManagerWithData.MockQByteArray()
|
||||
|
||||
nmWithData = MockNetworkManagerWithData()
|
||||
m = Macro("Unit Test Macro")
|
||||
Macro.network_manager = nmWithData
|
||||
Macro.blocking_get = MagicMock(return_value=b"Data returned to _fetch_raw_code")
|
||||
returned_data = m._fetch_raw_code(
|
||||
'rawcodeurl <a href="https://fake_url.com">Totally fake</a>'
|
||||
)
|
||||
self.assertEqual(returned_data, "Data returned to _fetch_raw_code")
|
||||
|
||||
Macro.network_manager = None
|
||||
Macro.blocking_get = None
|
||||
|
||||
620
src/Mod/AddonManager/AddonManagerTest/app/test_metadata.py
Normal file
620
src/Mod/AddonManager/AddonManagerTest/app/test_metadata.py
Normal file
@@ -0,0 +1,620 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 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 os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import unittest.mock
|
||||
|
||||
Mock = unittest.mock.MagicMock
|
||||
|
||||
sys.path.append("../../")
|
||||
|
||||
|
||||
class TestVersion(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
if "addonmanager_metadata" in sys.modules:
|
||||
sys.modules.pop("addonmanager_metadata")
|
||||
self.packaging_version = None
|
||||
if "packaging.version" in sys.modules:
|
||||
self.packaging_version = sys.modules["packaging.version"]
|
||||
sys.modules.pop("packaging.version")
|
||||
|
||||
def tearDown(self) -> None:
|
||||
if self.packaging_version is not None:
|
||||
sys.modules["packaging.version"] = self.packaging_version
|
||||
|
||||
def test_init_from_string_manual(self):
|
||||
import addonmanager_metadata as amm
|
||||
|
||||
version = amm.Version()
|
||||
version._parse_string_to_tuple = unittest.mock.MagicMock()
|
||||
version._init_from_string("1.2.3beta")
|
||||
self.assertTrue(version._parse_string_to_tuple.called)
|
||||
|
||||
def test_init_from_list_good(self):
|
||||
"""Initialization from a list works for good input"""
|
||||
import addonmanager_metadata as amm
|
||||
|
||||
test_cases = [
|
||||
{"input": (1,), "output": [1, 0, 0, ""]},
|
||||
{"input": (1, 2), "output": [1, 2, 0, ""]},
|
||||
{"input": (1, 2, 3), "output": [1, 2, 3, ""]},
|
||||
{"input": (1, 2, 3, "b1"), "output": [1, 2, 3, "b1"]},
|
||||
]
|
||||
for test_case in test_cases:
|
||||
with self.subTest(test_case=test_case):
|
||||
v = amm.Version(from_list=test_case["input"])
|
||||
self.assertListEqual(test_case["output"], v.version_as_list)
|
||||
|
||||
def test_parse_string_to_tuple_normal(self):
|
||||
"""Parsing of complete version string works for normal cases"""
|
||||
import addonmanager_metadata as amm
|
||||
|
||||
cases = {
|
||||
"1": [1, 0, 0, ""],
|
||||
"1.2": [1, 2, 0, ""],
|
||||
"1.2.3": [1, 2, 3, ""],
|
||||
"1.2.3beta": [1, 2, 3, "beta"],
|
||||
"12_345.6_7.8pre-alpha": [12345, 67, 8, "pre-alpha"],
|
||||
# The above test is mostly to point out that Python gets permits underscore
|
||||
# characters in a number.
|
||||
}
|
||||
for inp, output in cases.items():
|
||||
with self.subTest(inp=inp, output=output):
|
||||
version = amm.Version()
|
||||
version._parse_string_to_tuple(inp)
|
||||
self.assertListEqual(version.version_as_list, output)
|
||||
|
||||
def test_parse_string_to_tuple_invalid(self):
|
||||
"""Parsing of invalid version string raises an exception"""
|
||||
import addonmanager_metadata as amm
|
||||
|
||||
cases = {"One", "1,2,3", "1-2-3", "1/2/3"}
|
||||
for inp in cases:
|
||||
with self.subTest(inp=inp):
|
||||
with self.assertRaises(ValueError):
|
||||
version = amm.Version()
|
||||
version._parse_string_to_tuple(inp)
|
||||
|
||||
def test_parse_final_entry_normal(self):
|
||||
"""Parsing of the final entry works for normal cases"""
|
||||
import addonmanager_metadata as amm
|
||||
|
||||
cases = {
|
||||
"3beta": (3, "beta"),
|
||||
"42.alpha": (42, ".alpha"),
|
||||
"123.45.6": (123, ".45.6"),
|
||||
"98_delta": (98, "_delta"),
|
||||
"1 and some words": (1, " and some words"),
|
||||
}
|
||||
for inp, output in cases.items():
|
||||
with self.subTest(inp=inp, output=output):
|
||||
number, text = amm.Version._parse_final_entry(inp)
|
||||
self.assertEqual(number, output[0])
|
||||
self.assertEqual(text, output[1])
|
||||
|
||||
def test_parse_final_entry_invalid(self):
|
||||
"""Invalid input raises an exception"""
|
||||
import addonmanager_metadata as amm
|
||||
|
||||
cases = ["beta", "", ["a", "b"]]
|
||||
for case in cases:
|
||||
with self.subTest(case=case):
|
||||
with self.assertRaises(ValueError):
|
||||
amm.Version._parse_final_entry(case)
|
||||
|
||||
def test_operators_internal(self):
|
||||
"""Test internal (non-package) comparison operators"""
|
||||
sys.modules["packaging.version"] = None
|
||||
import addonmanager_metadata as amm
|
||||
|
||||
cases = self.given_comparison_cases()
|
||||
for case in cases:
|
||||
with self.subTest(case=case):
|
||||
first = amm.Version(case[0])
|
||||
second = amm.Version(case[1])
|
||||
self.assertEqual(first < second, case[0] < case[1])
|
||||
self.assertEqual(first > second, case[0] > case[1])
|
||||
self.assertEqual(first <= second, case[0] <= case[1])
|
||||
self.assertEqual(first >= second, case[0] >= case[1])
|
||||
self.assertEqual(first == second, case[0] == case[1])
|
||||
|
||||
@staticmethod
|
||||
def given_comparison_cases():
|
||||
return [
|
||||
("0.0.0alpha", "1.0.0alpha"),
|
||||
("0.0.0alpha", "0.1.0alpha"),
|
||||
("0.0.0alpha", "0.0.1alpha"),
|
||||
("0.0.0alpha", "0.0.0beta"),
|
||||
("0.0.0alpha", "0.0.0alpha"),
|
||||
("1.0.0alpha", "0.0.0alpha"),
|
||||
("0.1.0alpha", "0.0.0alpha"),
|
||||
("0.0.1alpha", "0.0.0alpha"),
|
||||
("0.0.0beta", "0.0.0alpha"),
|
||||
]
|
||||
|
||||
|
||||
class TestDependencyType(unittest.TestCase):
|
||||
"""Ensure that the DependencyType dataclass converts to the correct strings"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
from addonmanager_metadata import DependencyType
|
||||
|
||||
self.DependencyType = DependencyType
|
||||
|
||||
def test_string_conversion_automatic(self):
|
||||
self.assertEqual(str(self.DependencyType.automatic), "automatic")
|
||||
|
||||
def test_string_conversion_internal(self):
|
||||
self.assertEqual(str(self.DependencyType.internal), "internal")
|
||||
|
||||
def test_string_conversion_addon(self):
|
||||
self.assertEqual(str(self.DependencyType.addon), "addon")
|
||||
|
||||
def test_string_conversion_python(self):
|
||||
self.assertEqual(str(self.DependencyType.python), "python")
|
||||
|
||||
|
||||
class TestUrlType(unittest.TestCase):
|
||||
"""Ensure that the UrlType dataclass converts to the correct strings"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
from addonmanager_metadata import UrlType
|
||||
|
||||
self.UrlType = UrlType
|
||||
|
||||
def test_string_conversion_website(self):
|
||||
self.assertEqual(str(self.UrlType.website), "website")
|
||||
|
||||
def test_string_conversion_repository(self):
|
||||
self.assertEqual(str(self.UrlType.repository), "repository")
|
||||
|
||||
def test_string_conversion_bugtracker(self):
|
||||
self.assertEqual(str(self.UrlType.bugtracker), "bugtracker")
|
||||
|
||||
def test_string_conversion_readme(self):
|
||||
self.assertEqual(str(self.UrlType.readme), "readme")
|
||||
|
||||
def test_string_conversion_documentation(self):
|
||||
self.assertEqual(str(self.UrlType.documentation), "documentation")
|
||||
|
||||
def test_string_conversion_discussion(self):
|
||||
self.assertEqual(str(self.UrlType.discussion), "discussion")
|
||||
|
||||
|
||||
class TestMetadataAuxiliaryFunctions(unittest.TestCase):
|
||||
|
||||
def test_get_first_supported_freecad_version_simple(self):
|
||||
from addonmanager_metadata import Metadata, Version, get_first_supported_freecad_version
|
||||
expected_result = Version(from_string="0.20.2beta")
|
||||
metadata = self.given_metadata_with_freecadmin_set(expected_result)
|
||||
first_version = get_first_supported_freecad_version(metadata)
|
||||
self.assertEqual(expected_result, first_version)
|
||||
|
||||
@staticmethod
|
||||
def given_metadata_with_freecadmin_set(min_version):
|
||||
from addonmanager_metadata import Metadata
|
||||
metadata = Metadata()
|
||||
metadata.freecadmin = min_version
|
||||
return metadata
|
||||
|
||||
def test_get_first_supported_freecad_version_with_content(self):
|
||||
from addonmanager_metadata import Metadata, Version, get_first_supported_freecad_version
|
||||
expected_result = Version(from_string="0.20.2beta")
|
||||
metadata = self.given_metadata_with_freecadmin_in_content(expected_result)
|
||||
first_version = get_first_supported_freecad_version(metadata)
|
||||
self.assertEqual(expected_result, first_version)
|
||||
|
||||
@staticmethod
|
||||
def given_metadata_with_freecadmin_in_content(min_version):
|
||||
from addonmanager_metadata import Metadata, Version
|
||||
v_list = min_version.version_as_list
|
||||
metadata = Metadata()
|
||||
wb1 = Metadata()
|
||||
wb1.freecadmin = Version(from_list=[v_list[0]+1,v_list[1],v_list[2],v_list[3]])
|
||||
wb2 = Metadata()
|
||||
wb2.freecadmin = Version(from_list=[v_list[0],v_list[1]+1,v_list[2],v_list[3]])
|
||||
wb3 = Metadata()
|
||||
wb3.freecadmin = Version(from_list=[v_list[0],v_list[1],v_list[2]+1,v_list[3]])
|
||||
m1 = Metadata()
|
||||
m1.freecadmin = min_version
|
||||
metadata.content = {"workbench":[wb1,wb2,wb3],"macro":[m1]}
|
||||
return metadata
|
||||
|
||||
|
||||
class TestMetadataReader(unittest.TestCase):
|
||||
"""Test reading metadata from XML"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
if "xml.etree.ElementTree" in sys.modules:
|
||||
sys.modules.pop("xml.etree.ElementTree")
|
||||
if "MetadataReader" in sys.modules:
|
||||
sys.modules.pop("MetadataReader")
|
||||
|
||||
def tearDown(self) -> None:
|
||||
if "xml.etree.ElementTree" in sys.modules:
|
||||
sys.modules.pop("xml.etree.ElementTree")
|
||||
if "MetadataReader" in sys.modules:
|
||||
sys.modules.pop("MetadataReader")
|
||||
|
||||
def test_from_file(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
MetadataReader.from_bytes = Mock()
|
||||
with tempfile.NamedTemporaryFile(delete=False) as temp:
|
||||
temp.write(b"Some data")
|
||||
temp.close()
|
||||
MetadataReader.from_file(temp.name)
|
||||
self.assertTrue(MetadataReader.from_bytes.called)
|
||||
MetadataReader.from_bytes.assert_called_once_with(b"Some data")
|
||||
os.unlink(temp.name)
|
||||
|
||||
@unittest.skip("Breaks other tests, needs to be fixed")
|
||||
def test_from_bytes(self):
|
||||
import xml.etree.ElementTree
|
||||
|
||||
with unittest.mock.patch("xml.etree.ElementTree") as element_tree_mock:
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
MetadataReader._process_element_tree = Mock()
|
||||
MetadataReader.from_bytes(b"Some data")
|
||||
element_tree_mock.parse.assert_called_once_with(b"Some data")
|
||||
|
||||
def test_process_element_tree(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
MetadataReader._determine_namespace = Mock(return_value="")
|
||||
element_tree_mock = Mock()
|
||||
MetadataReader._create_node = Mock()
|
||||
MetadataReader._process_element_tree(element_tree_mock)
|
||||
MetadataReader._create_node.assert_called_once()
|
||||
|
||||
def test_determine_namespace_found_full(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
root = Mock()
|
||||
root.tag = "{https://wiki.freecad.org/Package_Metadata}package"
|
||||
found_ns = MetadataReader._determine_namespace(root)
|
||||
self.assertEqual(found_ns, "{https://wiki.freecad.org/Package_Metadata}")
|
||||
|
||||
def test_determine_namespace_found_empty(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
root = Mock()
|
||||
root.tag = "package"
|
||||
found_ns = MetadataReader._determine_namespace(root)
|
||||
self.assertEqual(found_ns, "")
|
||||
|
||||
def test_determine_namespace_not_found(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
root = Mock()
|
||||
root.find = Mock(return_value=False)
|
||||
with self.assertRaises(RuntimeError):
|
||||
MetadataReader._determine_namespace(root)
|
||||
|
||||
def test_parse_child_element_simple_strings(self):
|
||||
from addonmanager_metadata import Metadata, MetadataReader
|
||||
|
||||
tags = ["name", "date", "description", "icon", "classname", "subdirectory"]
|
||||
for tag in tags:
|
||||
with self.subTest(tag=tag):
|
||||
text = f"Test Data for {tag}"
|
||||
child = self.given_mock_tree_node(tag, text)
|
||||
mock_metadata = Metadata()
|
||||
MetadataReader._parse_child_element("", child, mock_metadata)
|
||||
self.assertEqual(mock_metadata.__dict__[tag], text)
|
||||
|
||||
def test_parse_child_element_version(self):
|
||||
from addonmanager_metadata import Metadata, Version, MetadataReader
|
||||
|
||||
mock_metadata = Metadata()
|
||||
child = self.given_mock_tree_node("version", "1.2.3")
|
||||
MetadataReader._parse_child_element("", child, mock_metadata)
|
||||
self.assertEqual(Version("1.2.3"), mock_metadata.version)
|
||||
|
||||
def test_parse_child_element_lists_of_strings(self):
|
||||
from addonmanager_metadata import Metadata, MetadataReader
|
||||
|
||||
tags = ["file", "tag"]
|
||||
for tag in tags:
|
||||
with self.subTest(tag=tag):
|
||||
mock_metadata = Metadata()
|
||||
expected_results = []
|
||||
for i in range(10):
|
||||
text = f"Test {i} for {tag}"
|
||||
expected_results.append(text)
|
||||
child = self.given_mock_tree_node(tag, text)
|
||||
MetadataReader._parse_child_element("", child, mock_metadata)
|
||||
self.assertEqual(len(mock_metadata.__dict__[tag]), 10)
|
||||
self.assertListEqual(mock_metadata.__dict__[tag], expected_results)
|
||||
|
||||
def test_parse_child_element_lists_of_contacts(self):
|
||||
from addonmanager_metadata import Metadata, Contact, MetadataReader
|
||||
|
||||
tags = ["maintainer", "author"]
|
||||
for tag in tags:
|
||||
with self.subTest(tag=tag):
|
||||
mock_metadata = Metadata()
|
||||
expected_results = []
|
||||
for i in range(10):
|
||||
text = f"Test {i} for {tag}"
|
||||
email = f"Email {i} for {tag}" if i % 2 == 0 else None
|
||||
expected_results.append(Contact(name=text, email=email))
|
||||
child = self.given_mock_tree_node(tag, text, {"email": email})
|
||||
MetadataReader._parse_child_element("", child, mock_metadata)
|
||||
self.assertEqual(len(mock_metadata.__dict__[tag]), 10)
|
||||
self.assertListEqual(mock_metadata.__dict__[tag], expected_results)
|
||||
|
||||
def test_parse_child_element_list_of_licenses(self):
|
||||
from addonmanager_metadata import Metadata, License, MetadataReader
|
||||
|
||||
mock_metadata = Metadata()
|
||||
expected_results = []
|
||||
tag = "license"
|
||||
for i in range(10):
|
||||
text = f"Test {i} for {tag}"
|
||||
file = f"Filename {i} for {tag}" if i % 2 == 0 else None
|
||||
expected_results.append(License(name=text, file=file))
|
||||
child = self.given_mock_tree_node(tag, text, {"file": file})
|
||||
MetadataReader._parse_child_element("", child, mock_metadata)
|
||||
self.assertEqual(len(mock_metadata.__dict__[tag]), 10)
|
||||
self.assertListEqual(mock_metadata.__dict__[tag], expected_results)
|
||||
|
||||
def test_parse_child_element_list_of_urls(self):
|
||||
from addonmanager_metadata import Metadata, Url, UrlType, MetadataReader
|
||||
|
||||
mock_metadata = Metadata()
|
||||
expected_results = []
|
||||
tag = "url"
|
||||
for i in range(10):
|
||||
text = f"Test {i} for {tag}"
|
||||
url_type = UrlType(i % len(UrlType))
|
||||
type = str(url_type)
|
||||
branch = ""
|
||||
if type == "repository":
|
||||
branch = f"Branch {i} for {tag}"
|
||||
expected_results.append(Url(location=text, type=url_type, branch=branch))
|
||||
child = self.given_mock_tree_node(
|
||||
tag, text, {"type": type, "branch": branch}
|
||||
)
|
||||
MetadataReader._parse_child_element("", child, mock_metadata)
|
||||
self.assertEqual(len(mock_metadata.__dict__[tag]), 10)
|
||||
self.assertListEqual(mock_metadata.__dict__[tag], expected_results)
|
||||
|
||||
def test_parse_child_element_lists_of_dependencies(self):
|
||||
from addonmanager_metadata import (
|
||||
Metadata,
|
||||
Dependency,
|
||||
DependencyType,
|
||||
MetadataReader,
|
||||
)
|
||||
|
||||
tags = ["depend", "conflict", "replace"]
|
||||
attributes = {
|
||||
"version_lt": "1.0.0",
|
||||
"version_lte": "1.0.0",
|
||||
"version_eq": "1.0.0",
|
||||
"version_gte": "1.0.0",
|
||||
"version_gt": "1.0.0",
|
||||
"condition": "$BuildVersionMajor<1",
|
||||
"optional": True,
|
||||
}
|
||||
|
||||
for tag in tags:
|
||||
for attribute, attr_value in attributes.items():
|
||||
with self.subTest(tag=tag, attribute=attribute):
|
||||
mock_metadata = Metadata()
|
||||
expected_results = []
|
||||
for i in range(10):
|
||||
text = f"Test {i} for {tag}"
|
||||
dependency_type = DependencyType(i % len(DependencyType))
|
||||
dependency_type_str = str(dependency_type)
|
||||
expected = Dependency(
|
||||
package=text, dependency_type=dependency_type
|
||||
)
|
||||
expected.__dict__[attribute] = attr_value
|
||||
expected_results.append(expected)
|
||||
child = self.given_mock_tree_node(
|
||||
tag,
|
||||
text,
|
||||
{"type": dependency_type_str, attribute: str(attr_value)},
|
||||
)
|
||||
MetadataReader._parse_child_element("", child, mock_metadata)
|
||||
self.assertEqual(len(mock_metadata.__dict__[tag]), 10)
|
||||
self.assertListEqual(mock_metadata.__dict__[tag], expected_results)
|
||||
|
||||
def test_parse_child_element_ignore_unknown_tag(self):
|
||||
from addonmanager_metadata import Metadata, MetadataReader
|
||||
|
||||
tag = "invalid_tag"
|
||||
text = "Shouldn't matter"
|
||||
child = self.given_mock_tree_node(tag, text)
|
||||
mock_metadata = Metadata()
|
||||
MetadataReader._parse_child_element("", child, mock_metadata)
|
||||
self.assertNotIn(tag, mock_metadata.__dict__)
|
||||
|
||||
def test_parse_child_element_versions(self):
|
||||
from addonmanager_metadata import Metadata, Version, MetadataReader
|
||||
|
||||
tags = ["version", "freecadmin", "freecadmax", "pythonmin"]
|
||||
for tag in tags:
|
||||
with self.subTest(tag=tag):
|
||||
mock_metadata = Metadata()
|
||||
text = "3.4.5beta"
|
||||
child = self.given_mock_tree_node(tag, text)
|
||||
MetadataReader._parse_child_element("", child, mock_metadata)
|
||||
self.assertEqual(mock_metadata.__dict__[tag], Version(from_string=text))
|
||||
|
||||
def given_mock_tree_node(self, tag, text, attributes=None):
|
||||
class MockTreeNode:
|
||||
def __init__(self):
|
||||
self.tag = tag
|
||||
self.text = text
|
||||
self.attrib = attributes if attributes is not None else []
|
||||
|
||||
return MockTreeNode()
|
||||
|
||||
def test_parse_content_valid(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
valid_content_items = ["workbench", "macro", "preferencepack"]
|
||||
MetadataReader._create_node = Mock()
|
||||
for content_type in valid_content_items:
|
||||
with self.subTest(content_type=content_type):
|
||||
tree_mock = [self.given_mock_tree_node(content_type, None)]
|
||||
metadata_mock = Mock()
|
||||
MetadataReader._parse_content("", metadata_mock, tree_mock)
|
||||
MetadataReader._create_node.assert_called_once()
|
||||
MetadataReader._create_node.reset_mock()
|
||||
|
||||
def test_parse_content_invalid(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
MetadataReader._create_node = Mock()
|
||||
content_item = "no_such_content_type"
|
||||
tree_mock = [self.given_mock_tree_node(content_item, None)]
|
||||
metadata_mock = Mock()
|
||||
MetadataReader._parse_content("", metadata_mock, tree_mock)
|
||||
MetadataReader._create_node.assert_not_called()
|
||||
|
||||
|
||||
class TestMetadataReaderIntegration(unittest.TestCase):
|
||||
"""Full-up tests of the MetadataReader class (no mocking)."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.test_data_dir = os.path.join(os.path.dirname(__file__), "..", "data")
|
||||
remove_list = []
|
||||
for key in sys.modules:
|
||||
if "addonmanager_metadata" in key:
|
||||
remove_list.append(key)
|
||||
for key in remove_list:
|
||||
print(f"Removing {key}")
|
||||
sys.modules.pop(key)
|
||||
|
||||
def test_loading_simple_metadata_file(self):
|
||||
from addonmanager_metadata import (
|
||||
Contact,
|
||||
Dependency,
|
||||
License,
|
||||
MetadataReader,
|
||||
Url,
|
||||
UrlType,
|
||||
Version,
|
||||
)
|
||||
|
||||
filename = os.path.join(self.test_data_dir, "good_package.xml")
|
||||
metadata = MetadataReader.from_file(filename)
|
||||
self.assertEqual("Test Workbench", metadata.name)
|
||||
self.assertEqual("A package.xml file for unit testing.", metadata.description)
|
||||
self.assertEqual(Version("1.0.1"), metadata.version)
|
||||
self.assertEqual("2022-01-07", metadata.date)
|
||||
self.assertEqual("Resources/icons/PackageIcon.svg", metadata.icon)
|
||||
self.assertListEqual(
|
||||
[License(name="LGPLv2.1", file="LICENSE")], metadata.license
|
||||
)
|
||||
self.assertListEqual(
|
||||
[Contact(name="FreeCAD Developer", email="developer@freecad.org")],
|
||||
metadata.maintainer,
|
||||
)
|
||||
self.assertListEqual(
|
||||
[
|
||||
Url(
|
||||
location="https://github.com/chennes/FreeCAD-Package",
|
||||
type=UrlType.repository,
|
||||
branch="main",
|
||||
),
|
||||
Url(
|
||||
location="https://github.com/chennes/FreeCAD-Package/blob/main/README.md",
|
||||
type=UrlType.readme,
|
||||
),
|
||||
],
|
||||
metadata.url,
|
||||
)
|
||||
self.assertListEqual(["Tag0", "Tag1"], metadata.tag)
|
||||
self.assertIn("workbench", metadata.content)
|
||||
self.assertEqual(len(metadata.content["workbench"]), 1)
|
||||
wb_metadata = metadata.content["workbench"][0]
|
||||
self.assertEqual("MyWorkbench", wb_metadata.classname)
|
||||
self.assertEqual("./", wb_metadata.subdirectory)
|
||||
self.assertListEqual(["TagA", "TagB", "TagC"], wb_metadata.tag)
|
||||
|
||||
def test_multiple_workbenches(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
filename = os.path.join(self.test_data_dir, "workbench_only.xml")
|
||||
metadata = MetadataReader.from_file(filename)
|
||||
self.assertIn("workbench", metadata.content)
|
||||
self.assertEqual(len(metadata.content["workbench"]), 3)
|
||||
expected_wb_classnames = [
|
||||
"MyFirstWorkbench",
|
||||
"MySecondWorkbench",
|
||||
"MyThirdWorkbench",
|
||||
]
|
||||
for wb in metadata.content["workbench"]:
|
||||
self.assertIn(wb.classname, expected_wb_classnames)
|
||||
expected_wb_classnames.remove(wb.classname)
|
||||
self.assertEqual(len(expected_wb_classnames), 0)
|
||||
|
||||
def test_multiple_macros(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
filename = os.path.join(self.test_data_dir, "macro_only.xml")
|
||||
metadata = MetadataReader.from_file(filename)
|
||||
self.assertIn("macro", metadata.content)
|
||||
self.assertEqual(len(metadata.content["macro"]), 2)
|
||||
expected_wb_files = ["MyMacro.FCStd", "MyOtherMacro.FCStd"]
|
||||
for wb in metadata.content["macro"]:
|
||||
self.assertIn(wb.file[0], expected_wb_files)
|
||||
expected_wb_files.remove(wb.file[0])
|
||||
self.assertEqual(len(expected_wb_files), 0)
|
||||
|
||||
def test_multiple_preference_packs(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
filename = os.path.join(self.test_data_dir, "prefpack_only.xml")
|
||||
metadata = MetadataReader.from_file(filename)
|
||||
self.assertIn("preferencepack", metadata.content)
|
||||
self.assertEqual(len(metadata.content["preferencepack"]), 3)
|
||||
expected_packs = ["MyFirstPack", "MySecondPack", "MyThirdPack"]
|
||||
for wb in metadata.content["preferencepack"]:
|
||||
self.assertIn(wb.name, expected_packs)
|
||||
expected_packs.remove(wb.name)
|
||||
self.assertEqual(len(expected_packs), 0)
|
||||
|
||||
def test_content_combination(self):
|
||||
from addonmanager_metadata import MetadataReader
|
||||
|
||||
filename = os.path.join(self.test_data_dir, "combination.xml")
|
||||
metadata = MetadataReader.from_file(filename)
|
||||
self.assertIn("preferencepack", metadata.content)
|
||||
self.assertEqual(len(metadata.content["preferencepack"]), 1)
|
||||
self.assertIn("macro", metadata.content)
|
||||
self.assertEqual(len(metadata.content["macro"]), 1)
|
||||
self.assertIn("workbench", metadata.content)
|
||||
self.assertEqual(len(metadata.content["workbench"]), 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user