FreeCAD
201 строка · 8.3 Кб
1# SPDX-License-Identifier: LGPL-2.1-or-later
2# ***************************************************************************
3# * *
4# * Copyright (c) 2022 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
24import functools25import os26import subprocess27import tempfile28from time import sleep29import unittest30
31from addonmanager_dependency_installer import DependencyInstaller32
33
34class CompleteProcessMock(subprocess.CompletedProcess):35def __init__(self):36super().__init__(["fake_arg"], 0)37self.stdout = "Mock subprocess call stdout result"38
39
40class SubprocessMock:41def __init__(self):42self.arg_log = []43self.called = False44self.call_count = 045self.delay = 046self.succeed = True47
48def subprocess_interceptor(self, args):49self.arg_log.append(args)50self.called = True51self.call_count += 152sleep(self.delay)53if self.succeed:54return CompleteProcessMock()55raise subprocess.CalledProcessError(1, " ".join(args), "Unit test mock output")56
57
58class FakeFunction:59def __init__(self):60self.called = False61self.call_count = 062self.return_value = None63self.arg_log = []64
65def func_call(self, *args):66self.arg_log.append(args)67self.called = True68self.call_count += 169return self.return_value70
71
72class TestDependencyInstaller(unittest.TestCase):73"""Test the dependency installation class"""74
75def setUp(self):76self.subprocess_mock = SubprocessMock()77self.test_object = DependencyInstaller([], ["required_py_package"], ["optional_py_package"])78self.test_object._subprocess_wrapper = self.subprocess_mock.subprocess_interceptor79self.signals_caught = []80self.test_object.failure.connect(functools.partial(self.catch_signal, "failure"))81self.test_object.finished.connect(functools.partial(self.catch_signal, "finished"))82self.test_object.no_pip.connect(functools.partial(self.catch_signal, "no_pip"))83self.test_object.no_python_exe.connect(84functools.partial(self.catch_signal, "no_python_exe")85)86
87def tearDown(self):88pass89
90def catch_signal(self, signal_name, *_):91self.signals_caught.append(signal_name)92
93def test_run_no_pip(self):94self.test_object._verify_pip = lambda: False95self.test_object.run()96self.assertIn("finished", self.signals_caught)97
98def test_run_with_pip(self):99ff = FakeFunction()100self.test_object._verify_pip = lambda: True101self.test_object._install_python_packages = ff.func_call102self.test_object.run()103self.assertIn("finished", self.signals_caught)104self.assertTrue(ff.called)105
106def test_run_with_no_packages(self):107ff = FakeFunction()108self.test_object._verify_pip = lambda: True109self.test_object._install_python_packages = ff.func_call110self.test_object.python_requires = []111self.test_object.python_optional = []112self.test_object.run()113self.assertIn("finished", self.signals_caught)114self.assertFalse(ff.called)115
116def test_install_python_packages_new_location(self):117ff_required = FakeFunction()118ff_optional = FakeFunction()119self.test_object._install_required = ff_required.func_call120self.test_object._install_optional = ff_optional.func_call121with tempfile.TemporaryDirectory() as td:122self.test_object.location = os.path.join(td, "UnitTestLocation")123self.test_object._install_python_packages()124self.assertTrue(ff_required.called)125self.assertTrue(ff_optional.called)126self.assertTrue(os.path.exists(self.test_object.location))127
128def test_install_python_packages_existing_location(self):129ff_required = FakeFunction()130ff_optional = FakeFunction()131self.test_object._install_required = ff_required.func_call132self.test_object._install_optional = ff_optional.func_call133with tempfile.TemporaryDirectory() as td:134self.test_object.location = td135self.test_object._install_python_packages()136self.assertTrue(ff_required.called)137self.assertTrue(ff_optional.called)138
139def test_verify_pip_no_python(self):140self.test_object._get_python = lambda: None141should_continue = self.test_object._verify_pip()142self.assertFalse(should_continue)143self.assertEqual(len(self.signals_caught), 0)144
145def test_verify_pip_no_pip(self):146sm = SubprocessMock()147sm.succeed = False148self.test_object._subprocess_wrapper = sm.subprocess_interceptor149self.test_object._get_python = lambda: "fake_python"150result = self.test_object._verify_pip()151self.assertFalse(result)152self.assertIn("no_pip", self.signals_caught)153
154def test_verify_pip_with_pip(self):155sm = SubprocessMock()156sm.succeed = True157self.test_object._subprocess_wrapper = sm.subprocess_interceptor158self.test_object._get_python = lambda: "fake_python"159result = self.test_object._verify_pip()160self.assertTrue(result)161self.assertNotIn("no_pip", self.signals_caught)162
163def test_install_required_loops(self):164sm = SubprocessMock()165sm.succeed = True166self.test_object._subprocess_wrapper = sm.subprocess_interceptor167self.test_object._get_python = lambda: "fake_python"168self.test_object.python_requires = ["test1", "test2", "test3"]169self.test_object._install_required("vendor_path")170self.assertEqual(sm.call_count, 3)171
172def test_install_required_failure(self):173sm = SubprocessMock()174sm.succeed = False175self.test_object._subprocess_wrapper = sm.subprocess_interceptor176self.test_object._get_python = lambda: "fake_python"177self.test_object.python_requires = ["test1", "test2", "test3"]178self.test_object._install_required("vendor_path")179self.assertEqual(sm.call_count, 1)180self.assertIn("failure", self.signals_caught)181
182def test_install_optional_loops(self):183sm = SubprocessMock()184sm.succeed = True185self.test_object._subprocess_wrapper = sm.subprocess_interceptor186self.test_object._get_python = lambda: "fake_python"187self.test_object.python_optional = ["test1", "test2", "test3"]188self.test_object._install_optional("vendor_path")189self.assertEqual(sm.call_count, 3)190
191def test_install_optional_failure(self):192sm = SubprocessMock()193sm.succeed = False194self.test_object._subprocess_wrapper = sm.subprocess_interceptor195self.test_object._get_python = lambda: "fake_python"196self.test_object.python_optional = ["test1", "test2", "test3"]197self.test_object._install_optional("vendor_path")198self.assertEqual(sm.call_count, 3)199
200def test_run_pip(self):201pass202