300 lines
13 KiB
Python
300 lines
13 KiB
Python
# 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/>. *
|
|
# * *
|
|
# ***************************************************************************
|
|
|
|
"""Tests for the Addon Manager's FreeCAD interface classes."""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
# pylint: disable=protected-access,import-outside-toplevel
|
|
|
|
|
|
class TestConsole(unittest.TestCase):
|
|
"""Tests for the Console"""
|
|
|
|
def setUp(self) -> None:
|
|
self.saved_freecad = None
|
|
if "FreeCAD" in sys.modules:
|
|
self.saved_freecad = sys.modules["FreeCAD"]
|
|
sys.modules.pop("FreeCAD")
|
|
if "addonmanager_freecad_interface" in sys.modules:
|
|
sys.modules.pop("addonmanager_freecad_interface")
|
|
sys.path.append("../../")
|
|
|
|
def tearDown(self) -> None:
|
|
if "FreeCAD" in sys.modules:
|
|
sys.modules.pop("FreeCAD")
|
|
if self.saved_freecad is not None:
|
|
sys.modules["FreeCAD"] = self.saved_freecad
|
|
|
|
def test_log_with_freecad(self):
|
|
"""Ensure that if FreeCAD exists, the appropriate function is called"""
|
|
sys.modules["FreeCAD"] = unittest.mock.MagicMock()
|
|
import addonmanager_freecad_interface as fc
|
|
|
|
fc.Console.PrintLog("Test output")
|
|
self.assertTrue(isinstance(fc.Console, unittest.mock.MagicMock))
|
|
self.assertTrue(fc.Console.PrintLog.called)
|
|
|
|
def test_log_no_freecad(self):
|
|
"""Test that if the FreeCAD import fails, the logger is set up correctly, and
|
|
implements PrintLog"""
|
|
sys.modules["FreeCAD"] = None
|
|
with patch("addonmanager_freecad_interface.logging", new=MagicMock()) as mock_logging:
|
|
import addonmanager_freecad_interface as fc
|
|
|
|
fc.Console.PrintLog("Test output")
|
|
self.assertTrue(isinstance(fc.Console, fc.ConsoleReplacement))
|
|
self.assertTrue(mock_logging.log.called)
|
|
|
|
def test_message_no_freecad(self):
|
|
"""Test that if the FreeCAD import fails the logger implements PrintMessage"""
|
|
sys.modules["FreeCAD"] = None
|
|
with patch("addonmanager_freecad_interface.logging", new=MagicMock()) as mock_logging:
|
|
import addonmanager_freecad_interface as fc
|
|
|
|
fc.Console.PrintMessage("Test output")
|
|
self.assertTrue(mock_logging.info.called)
|
|
|
|
def test_warning_no_freecad(self):
|
|
"""Test that if the FreeCAD import fails the logger implements PrintWarning"""
|
|
sys.modules["FreeCAD"] = None
|
|
with patch("addonmanager_freecad_interface.logging", new=MagicMock()) as mock_logging:
|
|
import addonmanager_freecad_interface as fc
|
|
|
|
fc.Console.PrintWarning("Test output")
|
|
self.assertTrue(mock_logging.warning.called)
|
|
|
|
def test_error_no_freecad(self):
|
|
"""Test that if the FreeCAD import fails the logger implements PrintError"""
|
|
sys.modules["FreeCAD"] = None
|
|
with patch("addonmanager_freecad_interface.logging", new=MagicMock()) as mock_logging:
|
|
import addonmanager_freecad_interface as fc
|
|
|
|
fc.Console.PrintError("Test output")
|
|
self.assertTrue(mock_logging.error.called)
|
|
|
|
|
|
class TestParameters(unittest.TestCase):
|
|
"""Tests for the Parameters"""
|
|
|
|
def setUp(self) -> None:
|
|
self.saved_freecad = None
|
|
if "FreeCAD" in sys.modules:
|
|
self.saved_freecad = sys.modules["FreeCAD"]
|
|
sys.modules.pop("FreeCAD")
|
|
if "addonmanager_freecad_interface" in sys.modules:
|
|
sys.modules.pop("addonmanager_freecad_interface")
|
|
sys.path.append("../../")
|
|
|
|
def tearDown(self) -> None:
|
|
if "FreeCAD" in sys.modules:
|
|
sys.modules.pop("FreeCAD")
|
|
if self.saved_freecad is not None:
|
|
sys.modules["FreeCAD"] = self.saved_freecad
|
|
|
|
def test_param_get_with_freecad(self):
|
|
"""Ensure that if FreeCAD exists, the built-in FreeCAD function is called"""
|
|
sys.modules["FreeCAD"] = unittest.mock.MagicMock()
|
|
import addonmanager_freecad_interface as fc
|
|
|
|
prefs = fc.ParamGet("some/fake/path")
|
|
self.assertTrue(isinstance(prefs, unittest.mock.MagicMock))
|
|
|
|
def test_param_get_no_freecad(self):
|
|
"""Test that if the FreeCAD import fails, param_get returns a ParametersReplacement"""
|
|
sys.modules["FreeCAD"] = None
|
|
import addonmanager_freecad_interface as fc
|
|
|
|
prefs = fc.ParamGet("some/fake/path")
|
|
self.assertTrue(isinstance(prefs, fc.ParametersReplacement))
|
|
|
|
def test_replacement_getters_and_setters(self):
|
|
"""Test that ParameterReplacement's getters, setters, and deleters work"""
|
|
sys.modules["FreeCAD"] = None
|
|
import addonmanager_freecad_interface as fc
|
|
|
|
prf = fc.ParamGet("some/fake/path")
|
|
gs_types = [
|
|
("Bool", prf.GetBool, prf.SetBool, prf.RemBool, True, False),
|
|
("Int", prf.GetInt, prf.SetInt, prf.RemInt, 42, 0),
|
|
("Float", prf.GetFloat, prf.SetFloat, prf.RemFloat, 1.2, 3.4),
|
|
("String", prf.GetString, prf.SetString, prf.RemString, "test", "other"),
|
|
]
|
|
for gs_type in gs_types:
|
|
with self.subTest(msg=f"Testing {gs_type[0]}", gs_type=gs_type):
|
|
getter = gs_type[1]
|
|
setter = gs_type[2]
|
|
deleter = gs_type[3]
|
|
value_1 = gs_type[4]
|
|
value_2 = gs_type[5]
|
|
self.assertEqual(getter("test", value_1), value_1)
|
|
self.assertEqual(getter("test", value_2), value_2)
|
|
self.assertNotIn("test", prf.parameters)
|
|
setter("test", value_1)
|
|
self.assertIn("test", prf.parameters)
|
|
self.assertEqual(getter("test", value_2), value_1)
|
|
deleter("test")
|
|
self.assertNotIn("test", prf.parameters)
|
|
|
|
|
|
class TestDataPaths(unittest.TestCase):
|
|
"""Tests for the data paths"""
|
|
|
|
def setUp(self) -> None:
|
|
self.saved_freecad = None
|
|
if "FreeCAD" in sys.modules:
|
|
self.saved_freecad = sys.modules["FreeCAD"]
|
|
sys.modules.pop("FreeCAD")
|
|
if "addonmanager_freecad_interface" in sys.modules:
|
|
sys.modules.pop("addonmanager_freecad_interface")
|
|
sys.path.append("../../")
|
|
|
|
def tearDown(self) -> None:
|
|
if "FreeCAD" in sys.modules:
|
|
sys.modules.pop("FreeCAD")
|
|
if self.saved_freecad is not None:
|
|
sys.modules["FreeCAD"] = self.saved_freecad
|
|
|
|
def test_init_with_freecad(self):
|
|
"""Ensure that if FreeCAD exists, the appropriate functions are called"""
|
|
sys.modules["FreeCAD"] = unittest.mock.MagicMock()
|
|
import addonmanager_freecad_interface as fc
|
|
|
|
data_paths = fc.DataPaths()
|
|
self.assertTrue(sys.modules["FreeCAD"].getUserAppDataDir.called)
|
|
self.assertTrue(sys.modules["FreeCAD"].getUserMacroDir.called)
|
|
self.assertTrue(sys.modules["FreeCAD"].getUserCachePath.called)
|
|
self.assertIsNotNone(data_paths.mod_dir)
|
|
self.assertIsNotNone(data_paths.cache_dir)
|
|
self.assertIsNotNone(data_paths.macro_dir)
|
|
|
|
def test_init_without_freecad(self):
|
|
"""Ensure that if FreeCAD does not exist, the appropriate functions are called"""
|
|
sys.modules["FreeCAD"] = None
|
|
import addonmanager_freecad_interface as fc
|
|
|
|
data_paths = fc.DataPaths()
|
|
self.assertIsNotNone(data_paths.mod_dir)
|
|
self.assertIsNotNone(data_paths.cache_dir)
|
|
self.assertIsNotNone(data_paths.macro_dir)
|
|
self.assertNotEqual(data_paths.mod_dir, data_paths.cache_dir)
|
|
self.assertNotEqual(data_paths.mod_dir, data_paths.macro_dir)
|
|
self.assertNotEqual(data_paths.cache_dir, data_paths.macro_dir)
|
|
|
|
|
|
class TestPreferences(unittest.TestCase):
|
|
"""Tests for the preferences wrapper"""
|
|
|
|
def setUp(self) -> None:
|
|
sys.path.append("../../")
|
|
import addonmanager_freecad_interface as fc
|
|
|
|
self.fc = fc
|
|
|
|
def tearDown(self) -> None:
|
|
pass
|
|
|
|
def test_load_preferences_defaults(self):
|
|
"""Preferences are loaded from a given file"""
|
|
defaults = self.given_defaults()
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
json_file = os.path.join(temp_dir, "defaults.json")
|
|
with open(json_file, "w", encoding="utf-8") as f:
|
|
f.write(json.dumps(defaults))
|
|
self.fc.Preferences._load_preferences_defaults(json_file)
|
|
self.assertDictEqual(defaults, self.fc.Preferences.preferences_defaults)
|
|
|
|
def test_in_memory_defaults(self):
|
|
"""Preferences are loaded from memory"""
|
|
defaults = self.given_defaults()
|
|
prefs = self.fc.Preferences(defaults)
|
|
self.assertDictEqual(defaults, prefs.preferences_defaults)
|
|
|
|
def test_get_good(self):
|
|
"""Get returns results when matching an existing preference"""
|
|
defaults = self.given_defaults()
|
|
prefs = self.fc.Preferences(defaults)
|
|
self.assertEqual(prefs.get("TestBool"), defaults["TestBool"])
|
|
self.assertEqual(prefs.get("TestInt"), defaults["TestInt"])
|
|
self.assertEqual(prefs.get("TestFloat"), defaults["TestFloat"])
|
|
self.assertEqual(prefs.get("TestString"), defaults["TestString"])
|
|
|
|
def test_get_nonexistent(self):
|
|
"""Get raises an exception when asked for a non-existent preference"""
|
|
defaults = self.given_defaults()
|
|
prefs = self.fc.Preferences(defaults)
|
|
with self.assertRaises(RuntimeError):
|
|
prefs.get("No_such_thing")
|
|
|
|
def test_get_bad_type(self):
|
|
"""Get raises an exception when getting an unsupported type"""
|
|
defaults = self.given_defaults()
|
|
defaults["TestArray"] = ["This", "Is", "Legal", "JSON"]
|
|
prefs = self.fc.Preferences(defaults)
|
|
with self.assertRaises(RuntimeError):
|
|
prefs.get("TestArray")
|
|
|
|
def test_set_good(self):
|
|
"""Set works when matching an existing preference"""
|
|
defaults = self.given_defaults()
|
|
prefs = self.fc.Preferences(defaults)
|
|
prefs.set("TestBool", False)
|
|
self.assertEqual(prefs.get("TestBool"), False)
|
|
prefs.set("TestInt", 4321)
|
|
self.assertEqual(prefs.get("TestInt"), 4321)
|
|
prefs.set("TestFloat", 3.14159)
|
|
self.assertEqual(prefs.get("TestFloat"), 3.14159)
|
|
prefs.set("TestString", "Forty two")
|
|
self.assertEqual(prefs.get("TestString"), "Forty two")
|
|
|
|
def test_set_nonexistent(self):
|
|
"""Set raises an exception when asked for a non-existent preference"""
|
|
defaults = self.given_defaults()
|
|
prefs = self.fc.Preferences(defaults)
|
|
with self.assertRaises(RuntimeError):
|
|
prefs.get("No_such_thing")
|
|
|
|
def test_set_bad_type(self):
|
|
"""Set raises an exception when setting an unsupported type"""
|
|
defaults = self.given_defaults()
|
|
defaults["TestArray"] = ["This", "Is", "Legal", "JSON"]
|
|
prefs = self.fc.Preferences(defaults)
|
|
with self.assertRaises(RuntimeError):
|
|
prefs.get("TestArray")
|
|
|
|
@staticmethod
|
|
def given_defaults():
|
|
"""Get a dictionary of fake defaults for testing"""
|
|
defaults = {
|
|
"TestBool": True,
|
|
"TestInt": 42,
|
|
"TestFloat": 1.2,
|
|
"TestString": "Test",
|
|
}
|
|
return defaults
|