FreeCAD
299 строк · 12.6 Кб
1# SPDX-License-Identifier: LGPL-2.1-or-later
2# ***************************************************************************
3# * *
4# * Copyright (c) 2023 FreeCAD Project Association *
5# * *
6# * This file is part of FreeCAD. *
7# * *
8# * FreeCAD is free software: you can redistribute it and/or modify it *
9# * under the terms of the GNU Lesser General Public License as *
10# * published by the Free Software Foundation, either version 2.1 of the *
11# * License, or (at your option) any later version. *
12# * *
13# * FreeCAD is distributed in the hope that it will be useful, but *
14# * WITHOUT ANY WARRANTY; without even the implied warranty of *
15# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16# * Lesser General Public License for more details. *
17# * *
18# * You should have received a copy of the GNU Lesser General Public *
19# * License along with FreeCAD. If not, see *
20# * <https://www.gnu.org/licenses/>. *
21# * *
22# ***************************************************************************
23
24"""Tests for the Addon Manager's FreeCAD interface classes."""
25
26import json27import os28import sys29import tempfile30import unittest31from unittest.mock import patch, MagicMock32
33# pylint: disable=protected-access,import-outside-toplevel
34
35
36class TestConsole(unittest.TestCase):37"""Tests for the Console"""38
39def setUp(self) -> None:40self.saved_freecad = None41if "FreeCAD" in sys.modules:42self.saved_freecad = sys.modules["FreeCAD"]43sys.modules.pop("FreeCAD")44if "addonmanager_freecad_interface" in sys.modules:45sys.modules.pop("addonmanager_freecad_interface")46sys.path.append("../../")47
48def tearDown(self) -> None:49if "FreeCAD" in sys.modules:50sys.modules.pop("FreeCAD")51if self.saved_freecad is not None:52sys.modules["FreeCAD"] = self.saved_freecad53
54def test_log_with_freecad(self):55"""Ensure that if FreeCAD exists, the appropriate function is called"""56sys.modules["FreeCAD"] = unittest.mock.MagicMock()57import addonmanager_freecad_interface as fc58
59fc.Console.PrintLog("Test output")60self.assertTrue(isinstance(fc.Console, unittest.mock.MagicMock))61self.assertTrue(fc.Console.PrintLog.called)62
63def test_log_no_freecad(self):64"""Test that if the FreeCAD import fails, the logger is set up correctly, and65implements PrintLog"""
66sys.modules["FreeCAD"] = None67with patch("addonmanager_freecad_interface.logging", new=MagicMock()) as mock_logging:68import addonmanager_freecad_interface as fc69
70fc.Console.PrintLog("Test output")71self.assertTrue(isinstance(fc.Console, fc.ConsoleReplacement))72self.assertTrue(mock_logging.log.called)73
74def test_message_no_freecad(self):75"""Test that if the FreeCAD import fails the logger implements PrintMessage"""76sys.modules["FreeCAD"] = None77with patch("addonmanager_freecad_interface.logging", new=MagicMock()) as mock_logging:78import addonmanager_freecad_interface as fc79
80fc.Console.PrintMessage("Test output")81self.assertTrue(mock_logging.info.called)82
83def test_warning_no_freecad(self):84"""Test that if the FreeCAD import fails the logger implements PrintWarning"""85sys.modules["FreeCAD"] = None86with patch("addonmanager_freecad_interface.logging", new=MagicMock()) as mock_logging:87import addonmanager_freecad_interface as fc88
89fc.Console.PrintWarning("Test output")90self.assertTrue(mock_logging.warning.called)91
92def test_error_no_freecad(self):93"""Test that if the FreeCAD import fails the logger implements PrintError"""94sys.modules["FreeCAD"] = None95with patch("addonmanager_freecad_interface.logging", new=MagicMock()) as mock_logging:96import addonmanager_freecad_interface as fc97
98fc.Console.PrintError("Test output")99self.assertTrue(mock_logging.error.called)100
101
102class TestParameters(unittest.TestCase):103"""Tests for the Parameters"""104
105def setUp(self) -> None:106self.saved_freecad = None107if "FreeCAD" in sys.modules:108self.saved_freecad = sys.modules["FreeCAD"]109sys.modules.pop("FreeCAD")110if "addonmanager_freecad_interface" in sys.modules:111sys.modules.pop("addonmanager_freecad_interface")112sys.path.append("../../")113
114def tearDown(self) -> None:115if "FreeCAD" in sys.modules:116sys.modules.pop("FreeCAD")117if self.saved_freecad is not None:118sys.modules["FreeCAD"] = self.saved_freecad119
120def test_param_get_with_freecad(self):121"""Ensure that if FreeCAD exists, the built-in FreeCAD function is called"""122sys.modules["FreeCAD"] = unittest.mock.MagicMock()123import addonmanager_freecad_interface as fc124
125prefs = fc.ParamGet("some/fake/path")126self.assertTrue(isinstance(prefs, unittest.mock.MagicMock))127
128def test_param_get_no_freecad(self):129"""Test that if the FreeCAD import fails, param_get returns a ParametersReplacement"""130sys.modules["FreeCAD"] = None131import addonmanager_freecad_interface as fc132
133prefs = fc.ParamGet("some/fake/path")134self.assertTrue(isinstance(prefs, fc.ParametersReplacement))135
136def test_replacement_getters_and_setters(self):137"""Test that ParameterReplacement's getters, setters, and deleters work"""138sys.modules["FreeCAD"] = None139import addonmanager_freecad_interface as fc140
141prf = fc.ParamGet("some/fake/path")142gs_types = [143("Bool", prf.GetBool, prf.SetBool, prf.RemBool, True, False),144("Int", prf.GetInt, prf.SetInt, prf.RemInt, 42, 0),145("Float", prf.GetFloat, prf.SetFloat, prf.RemFloat, 1.2, 3.4),146("String", prf.GetString, prf.SetString, prf.RemString, "test", "other"),147]148for gs_type in gs_types:149with self.subTest(msg=f"Testing {gs_type[0]}", gs_type=gs_type):150getter = gs_type[1]151setter = gs_type[2]152deleter = gs_type[3]153value_1 = gs_type[4]154value_2 = gs_type[5]155self.assertEqual(getter("test", value_1), value_1)156self.assertEqual(getter("test", value_2), value_2)157self.assertNotIn("test", prf.parameters)158setter("test", value_1)159self.assertIn("test", prf.parameters)160self.assertEqual(getter("test", value_2), value_1)161deleter("test")162self.assertNotIn("test", prf.parameters)163
164
165class TestDataPaths(unittest.TestCase):166"""Tests for the data paths"""167
168def setUp(self) -> None:169self.saved_freecad = None170if "FreeCAD" in sys.modules:171self.saved_freecad = sys.modules["FreeCAD"]172sys.modules.pop("FreeCAD")173if "addonmanager_freecad_interface" in sys.modules:174sys.modules.pop("addonmanager_freecad_interface")175sys.path.append("../../")176
177def tearDown(self) -> None:178if "FreeCAD" in sys.modules:179sys.modules.pop("FreeCAD")180if self.saved_freecad is not None:181sys.modules["FreeCAD"] = self.saved_freecad182
183def test_init_with_freecad(self):184"""Ensure that if FreeCAD exists, the appropriate functions are called"""185sys.modules["FreeCAD"] = unittest.mock.MagicMock()186import addonmanager_freecad_interface as fc187
188data_paths = fc.DataPaths()189self.assertTrue(sys.modules["FreeCAD"].getUserAppDataDir.called)190self.assertTrue(sys.modules["FreeCAD"].getUserMacroDir.called)191self.assertTrue(sys.modules["FreeCAD"].getUserCachePath.called)192self.assertIsNotNone(data_paths.mod_dir)193self.assertIsNotNone(data_paths.cache_dir)194self.assertIsNotNone(data_paths.macro_dir)195
196def test_init_without_freecad(self):197"""Ensure that if FreeCAD does not exist, the appropriate functions are called"""198sys.modules["FreeCAD"] = None199import addonmanager_freecad_interface as fc200
201data_paths = fc.DataPaths()202self.assertIsNotNone(data_paths.mod_dir)203self.assertIsNotNone(data_paths.cache_dir)204self.assertIsNotNone(data_paths.macro_dir)205self.assertNotEqual(data_paths.mod_dir, data_paths.cache_dir)206self.assertNotEqual(data_paths.mod_dir, data_paths.macro_dir)207self.assertNotEqual(data_paths.cache_dir, data_paths.macro_dir)208
209
210class TestPreferences(unittest.TestCase):211"""Tests for the preferences wrapper"""212
213def setUp(self) -> None:214sys.path.append("../../")215import addonmanager_freecad_interface as fc216
217self.fc = fc218
219def tearDown(self) -> None:220pass221
222def test_load_preferences_defaults(self):223"""Preferences are loaded from a given file"""224defaults = self.given_defaults()225with tempfile.TemporaryDirectory() as temp_dir:226json_file = os.path.join(temp_dir, "defaults.json")227with open(json_file, "w", encoding="utf-8") as f:228f.write(json.dumps(defaults))229self.fc.Preferences._load_preferences_defaults(json_file)230self.assertDictEqual(defaults, self.fc.Preferences.preferences_defaults)231
232def test_in_memory_defaults(self):233"""Preferences are loaded from memory"""234defaults = self.given_defaults()235prefs = self.fc.Preferences(defaults)236self.assertDictEqual(defaults, prefs.preferences_defaults)237
238def test_get_good(self):239"""Get returns results when matching an existing preference"""240defaults = self.given_defaults()241prefs = self.fc.Preferences(defaults)242self.assertEqual(prefs.get("TestBool"), defaults["TestBool"])243self.assertEqual(prefs.get("TestInt"), defaults["TestInt"])244self.assertEqual(prefs.get("TestFloat"), defaults["TestFloat"])245self.assertEqual(prefs.get("TestString"), defaults["TestString"])246
247def test_get_nonexistent(self):248"""Get raises an exception when asked for a non-existent preference"""249defaults = self.given_defaults()250prefs = self.fc.Preferences(defaults)251with self.assertRaises(RuntimeError):252prefs.get("No_such_thing")253
254def test_get_bad_type(self):255"""Get raises an exception when getting an unsupported type"""256defaults = self.given_defaults()257defaults["TestArray"] = ["This", "Is", "Legal", "JSON"]258prefs = self.fc.Preferences(defaults)259with self.assertRaises(RuntimeError):260prefs.get("TestArray")261
262def test_set_good(self):263"""Set works when matching an existing preference"""264defaults = self.given_defaults()265prefs = self.fc.Preferences(defaults)266prefs.set("TestBool", False)267self.assertEqual(prefs.get("TestBool"), False)268prefs.set("TestInt", 4321)269self.assertEqual(prefs.get("TestInt"), 4321)270prefs.set("TestFloat", 3.14159)271self.assertEqual(prefs.get("TestFloat"), 3.14159)272prefs.set("TestString", "Forty two")273self.assertEqual(prefs.get("TestString"), "Forty two")274
275def test_set_nonexistent(self):276"""Set raises an exception when asked for a non-existent preference"""277defaults = self.given_defaults()278prefs = self.fc.Preferences(defaults)279with self.assertRaises(RuntimeError):280prefs.get("No_such_thing")281
282def test_set_bad_type(self):283"""Set raises an exception when setting an unsupported type"""284defaults = self.given_defaults()285defaults["TestArray"] = ["This", "Is", "Legal", "JSON"]286prefs = self.fc.Preferences(defaults)287with self.assertRaises(RuntimeError):288prefs.get("TestArray")289
290@staticmethod291def given_defaults():292"""Get a dictionary of fake defaults for testing"""293defaults = {294"TestBool": True,295"TestInt": 42,296"TestFloat": 1.2,297"TestString": "Test",298}299return defaults300