Addon Manager: Create new uninstaller
This commit is contained in:
74
src/Mod/AddonManager/AddonManagerTest/app/mocks.py
Normal file
74
src/Mod/AddonManager/AddonManagerTest/app/mocks.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2022 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/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
"""Mock objects for use when testing the addon manager non-GUI code."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
|
||||
class MockAddon:
|
||||
"""Minimal Addon class"""
|
||||
|
||||
def __init__(self):
|
||||
self.name = "TestAddon"
|
||||
self.url = "https://github.com/FreeCAD/FreeCAD-addons"
|
||||
self.branch = "master"
|
||||
self.macro = None
|
||||
|
||||
|
||||
class MockMacro:
|
||||
"""Minimal Macro class"""
|
||||
|
||||
def __init__(self):
|
||||
self.name = "MockMacro"
|
||||
self.filename = self.name + ".FCMacro"
|
||||
self.icon = "" # If set, should just be fake filename, doesn't have to exist
|
||||
self.xpm = ""
|
||||
self.other_files = [] # If set, should be fake names, don't have to exist
|
||||
|
||||
def install(self, location: os.PathLike):
|
||||
"""Installer function for the mock macro object: creates a file with the src_filename
|
||||
attribute, and optionally an icon, xpm, and other_files. The data contained in these files
|
||||
is not usable and serves only as a placeholder for the existence of the files."""
|
||||
|
||||
with open(
|
||||
os.path.join(location, self.filename),
|
||||
"w",
|
||||
encoding="utf-8",
|
||||
) as f:
|
||||
f.write("Test file for macro installation unit tests")
|
||||
if self.icon:
|
||||
with open(os.path.join(location, self.icon), "wb") as f:
|
||||
f.write(b"Fake icon data - nothing to see here\n")
|
||||
if self.xpm:
|
||||
with open(
|
||||
os.path.join(location, "MockMacro_icon.xpm"), "w", encoding="utf-8"
|
||||
) as f:
|
||||
f.write(self.xpm)
|
||||
for name in self.other_files:
|
||||
if "/" in name:
|
||||
new_location = os.path.dirname(os.path.join(location, name))
|
||||
os.makedirs(new_location, exist_ok=True)
|
||||
with open(os.path.join(location, name), "w", encoding="utf-8") as f:
|
||||
f.write("# Fake macro data for unit testing\n")
|
||||
return True, []
|
||||
@@ -38,12 +38,7 @@ from addonmanager_git import GitManager, initialize_git
|
||||
|
||||
from Addon import Addon
|
||||
|
||||
|
||||
class MockAddon:
|
||||
def __init__(self):
|
||||
self.name = "TestAddon"
|
||||
self.url = "https://github.com/FreeCAD/FreeCAD-addons"
|
||||
self.branch = "master"
|
||||
from AddonManagerTest.app.mocks import MockAddon, MockMacro
|
||||
|
||||
|
||||
class TestAddonInstaller(unittest.TestCase):
|
||||
@@ -359,28 +354,21 @@ class TestMacroInstaller(unittest.TestCase):
|
||||
MODULE = "test_installer" # file name without extension
|
||||
|
||||
def setUp(self):
|
||||
class MacroMock:
|
||||
def install(self, location: os.PathLike):
|
||||
with open(
|
||||
os.path.join(location, "MACRO_INSTALLATION_TEST"),
|
||||
"w",
|
||||
encoding="utf-8",
|
||||
) as f:
|
||||
f.write("Test file for macro installation unit tests")
|
||||
return True, []
|
||||
"""Set up the mock objects"""
|
||||
|
||||
class AddonMock:
|
||||
def __init__(self):
|
||||
self.macro = MacroMock()
|
||||
|
||||
self.mock = AddonMock()
|
||||
self.mock = MockAddon()
|
||||
self.mock.macro = MockMacro()
|
||||
|
||||
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.
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
installer = MacroInstaller(self.mock)
|
||||
installer.installation_path = temp_dir
|
||||
installation_succeeded = installer.run()
|
||||
self.assertTrue(installation_succeeded)
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(temp_dir, "MACRO_INSTALLATION_TEST"))
|
||||
os.path.exists(os.path.join(temp_dir, self.mock.macro.filename))
|
||||
)
|
||||
|
||||
495
src/Mod/AddonManager/AddonManagerTest/app/test_uninstaller.py
Normal file
495
src/Mod/AddonManager/AddonManagerTest/app/test_uninstaller.py
Normal file
@@ -0,0 +1,495 @@
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2022 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/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
"""Contains the unit test class for addonmanager_uninstaller.py non-GUI functionality."""
|
||||
|
||||
import functools
|
||||
import os
|
||||
from stat import S_IREAD, S_IRGRP, S_IROTH, S_IWUSR
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import FreeCAD
|
||||
|
||||
from addonmanager_uninstaller import AddonUninstaller, MacroUninstaller
|
||||
|
||||
from Addon import Addon
|
||||
from AddonManagerTest.app.mocks import MockAddon, MockMacro
|
||||
|
||||
|
||||
class TestAddonUninstaller(unittest.TestCase):
|
||||
"""Test class for addonmanager_uninstaller.py non-GUI functionality"""
|
||||
|
||||
MODULE = "test_uninstaller" # file name without extension
|
||||
|
||||
def setUp(self):
|
||||
"""Initialize data needed for all tests"""
|
||||
self.test_data_dir = os.path.join(
|
||||
FreeCAD.getHomePath(), "Mod", "AddonManager", "AddonManagerTest", "data"
|
||||
)
|
||||
self.mock_addon = MockAddon()
|
||||
self.signals_caught = []
|
||||
self.test_object = AddonUninstaller(self.mock_addon)
|
||||
|
||||
self.test_object.finished.connect(
|
||||
functools.partial(self.catch_signal, "finished")
|
||||
)
|
||||
self.test_object.success.connect(
|
||||
functools.partial(self.catch_signal, "success")
|
||||
)
|
||||
self.test_object.failure.connect(
|
||||
functools.partial(self.catch_signal, "failure")
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
"""Finalize the test."""
|
||||
|
||||
def catch_signal(self, signal_name, *_):
|
||||
"""Internal use: used to catch and log any emitted signals. Not called directly."""
|
||||
self.signals_caught.append(signal_name)
|
||||
|
||||
def setup_dummy_installation(self, temp_dir) -> str:
|
||||
"""Set up a dummy Addon in temp_dir"""
|
||||
toplevel_path = os.path.join(temp_dir, self.mock_addon.name)
|
||||
os.makedirs(toplevel_path)
|
||||
with open(os.path.join(toplevel_path, "README.md"), "w") as f:
|
||||
f.write("## Mock Addon ##\n\nFile created by the unit test code.")
|
||||
self.test_object.installation_path = temp_dir
|
||||
return toplevel_path
|
||||
|
||||
def create_fake_macro(self, macro_directory, fake_macro_name, digest):
|
||||
"""Create an FCMacro file and matching digest entry for later removal"""
|
||||
os.makedirs(macro_directory, exist_ok=True)
|
||||
fake_file_installed = os.path.join(macro_directory, fake_macro_name)
|
||||
with open(digest, "a", encoding="utf-8") as f:
|
||||
f.write("# The following files were created outside this installation:\n")
|
||||
f.write(fake_file_installed + "\n")
|
||||
with open(fake_file_installed, "w", encoding="utf-8") as f:
|
||||
f.write("# Fake macro data for unit testing")
|
||||
|
||||
def test_uninstall_normal(self):
|
||||
"""Test the integrated uninstall function under normal circumstances"""
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
toplevel_path = self.setup_dummy_installation(temp_dir)
|
||||
self.test_object.run()
|
||||
self.assertTrue(os.path.exists(temp_dir))
|
||||
self.assertFalse(os.path.exists(toplevel_path))
|
||||
self.assertNotIn("failure", self.signals_caught)
|
||||
self.assertIn("success", self.signals_caught)
|
||||
self.assertIn("finished", self.signals_caught)
|
||||
|
||||
def test_uninstall_no_name(self):
|
||||
"""Test the integrated uninstall function for an addon without a name"""
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
toplevel_path = self.setup_dummy_installation(temp_dir)
|
||||
self.mock_addon.name = None
|
||||
result = self.test_object.run()
|
||||
self.assertTrue(os.path.exists(temp_dir))
|
||||
self.assertIn("failure", self.signals_caught)
|
||||
self.assertNotIn("success", self.signals_caught)
|
||||
self.assertIn("finished", self.signals_caught)
|
||||
|
||||
def test_uninstall_dangerous_name(self):
|
||||
"""Test the integrated uninstall function for an addon with a dangerous name"""
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
toplevel_path = self.setup_dummy_installation(temp_dir)
|
||||
self.mock_addon.name = "./"
|
||||
result = self.test_object.run()
|
||||
self.assertTrue(os.path.exists(temp_dir))
|
||||
self.assertIn("failure", self.signals_caught)
|
||||
self.assertNotIn("success", self.signals_caught)
|
||||
self.assertIn("finished", self.signals_caught)
|
||||
|
||||
def test_uninstall_unmatching_name(self):
|
||||
"""Test the integrated uninstall function for an addon with a name that isn't installed"""
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
toplevel_path = self.setup_dummy_installation(temp_dir)
|
||||
self.mock_addon.name += "Nonexistent"
|
||||
result = self.test_object.run()
|
||||
self.assertTrue(os.path.exists(temp_dir))
|
||||
self.assertIn("failure", self.signals_caught)
|
||||
self.assertNotIn("success", self.signals_caught)
|
||||
self.assertIn("finished", self.signals_caught)
|
||||
|
||||
def test_uninstall_addon_with_macros(self):
|
||||
"""Tests that the uninstaller removes the macro files"""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
toplevel_path = self.setup_dummy_installation(temp_dir)
|
||||
macro_directory = os.path.join(temp_dir, "Macros")
|
||||
self.create_fake_macro(
|
||||
macro_directory,
|
||||
"FakeMacro.FCMacro",
|
||||
os.path.join(toplevel_path, "AM_INSTALLATION_DIGEST.txt"),
|
||||
)
|
||||
result = self.test_object.run()
|
||||
self.assertNotIn("failure", self.signals_caught)
|
||||
self.assertIn("success", self.signals_caught)
|
||||
self.assertIn("finished", self.signals_caught)
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(macro_directory, "FakeMacro.FCMacro"))
|
||||
)
|
||||
self.assertTrue(os.path.exists(macro_directory))
|
||||
|
||||
def test_uninstall_calls_script(self):
|
||||
"""Tests that the main uninstaller run function calls the uninstall.py script"""
|
||||
|
||||
class Interceptor:
|
||||
def __init__(self):
|
||||
self.called = False
|
||||
self.args = []
|
||||
|
||||
def func(self, *args):
|
||||
self.called = True
|
||||
self.args = args
|
||||
|
||||
interceptor = Interceptor()
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
toplevel_path = self.setup_dummy_installation(temp_dir)
|
||||
self.test_object.run_uninstall_script = interceptor.func
|
||||
result = self.test_object.run()
|
||||
self.assertTrue(interceptor.called, "Failed to call uninstall script")
|
||||
|
||||
def test_remove_extra_files_no_digest(self):
|
||||
"""Tests that a lack of digest file is not an error, and nothing gets removed"""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
self.test_object.remove_extra_files(temp_dir) # Shouldn't throw
|
||||
self.assertTrue(os.path.exists(temp_dir))
|
||||
|
||||
def test_remove_extra_files_empty_digest(self):
|
||||
"""Test that an empty digest file is not an error, and nothing gets removed"""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
with open("AM_INSTALLATION_DIGEST.txt", "w", encoding="utf-8") as f:
|
||||
f.write("")
|
||||
self.test_object.remove_extra_files(temp_dir) # Shouldn't throw
|
||||
self.assertTrue(os.path.exists(temp_dir))
|
||||
|
||||
def test_remove_extra_files_comment_only_digest(self):
|
||||
"""Test that a digest file that contains only comment lines is not an error, and nothing
|
||||
gets removed"""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
with open("AM_INSTALLATION_DIGEST.txt", "w", encoding="utf-8") as f:
|
||||
f.write("# Fake digest file for unit testing")
|
||||
self.test_object.remove_extra_files(temp_dir) # Shouldn't throw
|
||||
self.assertTrue(os.path.exists(temp_dir))
|
||||
|
||||
def test_remove_extra_files_repeated_files(self):
|
||||
"""Test that a digest with the same file repeated removes it once, but doesn't error on
|
||||
later requests to remove it."""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
toplevel_path = self.setup_dummy_installation(temp_dir)
|
||||
macro_directory = os.path.join(temp_dir, "Macros")
|
||||
self.create_fake_macro(
|
||||
macro_directory,
|
||||
"FakeMacro.FCMacro",
|
||||
os.path.join(toplevel_path, "AM_INSTALLATION_DIGEST.txt"),
|
||||
)
|
||||
self.create_fake_macro(
|
||||
macro_directory,
|
||||
"FakeMacro.FCMacro",
|
||||
os.path.join(toplevel_path, "AM_INSTALLATION_DIGEST.txt"),
|
||||
)
|
||||
self.create_fake_macro(
|
||||
macro_directory,
|
||||
"FakeMacro.FCMacro",
|
||||
os.path.join(toplevel_path, "AM_INSTALLATION_DIGEST.txt"),
|
||||
)
|
||||
self.test_object.remove_extra_files(toplevel_path) # Shouldn't throw
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(macro_directory, "FakeMacro.FCMacro"))
|
||||
)
|
||||
|
||||
def test_remove_extra_files_normal_case(self):
|
||||
"""Test that a digest that is a "normal" case removes the requested files"""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
toplevel_path = self.setup_dummy_installation(temp_dir)
|
||||
macro_directory = os.path.join(temp_dir, "Macros")
|
||||
self.create_fake_macro(
|
||||
macro_directory,
|
||||
"FakeMacro1.FCMacro",
|
||||
os.path.join(toplevel_path, "AM_INSTALLATION_DIGEST.txt"),
|
||||
)
|
||||
self.create_fake_macro(
|
||||
macro_directory,
|
||||
"FakeMacro2.FCMacro",
|
||||
os.path.join(toplevel_path, "AM_INSTALLATION_DIGEST.txt"),
|
||||
)
|
||||
self.create_fake_macro(
|
||||
macro_directory,
|
||||
"FakeMacro3.FCMacro",
|
||||
os.path.join(toplevel_path, "AM_INSTALLATION_DIGEST.txt"),
|
||||
)
|
||||
|
||||
# Make sure the setup worked as expected, otherwise the test is meaningless
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(macro_directory, "FakeMacro1.FCMacro"))
|
||||
)
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(macro_directory, "FakeMacro2.FCMacro"))
|
||||
)
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(macro_directory, "FakeMacro3.FCMacro"))
|
||||
)
|
||||
|
||||
self.test_object.remove_extra_files(toplevel_path) # Shouldn't throw
|
||||
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(macro_directory, "FakeMacro1.FCMacro"))
|
||||
)
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(macro_directory, "FakeMacro2.FCMacro"))
|
||||
)
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(macro_directory, "FakeMacro3.FCMacro"))
|
||||
)
|
||||
|
||||
def test_runs_uninstaller_script_successful(self):
|
||||
"""Tests that the uninstall.py script is called"""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
toplevel_path = self.setup_dummy_installation(temp_dir)
|
||||
with open(
|
||||
os.path.join(toplevel_path, "uninstall.py"), "w", encoding="utf-8"
|
||||
) as f:
|
||||
double_escaped = temp_dir.replace("\\", "\\\\")
|
||||
f.write(
|
||||
f"""# Mock uninstaller script
|
||||
import os
|
||||
path = '{double_escaped}'
|
||||
with open(os.path.join(path,"RAN_UNINSTALLER.txt"),"w",encoding="utf-8") as f:
|
||||
f.write("File created by uninstall.py from unit tests")
|
||||
"""
|
||||
)
|
||||
self.test_object.run_uninstall_script(
|
||||
toplevel_path
|
||||
) # The exception does not leak out
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(temp_dir, "RAN_UNINSTALLER.txt"))
|
||||
)
|
||||
|
||||
def test_runs_uninstaller_script_failure(self):
|
||||
"""Tests that exceptions in the uninstall.py script do not leak out"""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
toplevel_path = self.setup_dummy_installation(temp_dir)
|
||||
with open(
|
||||
os.path.join(toplevel_path, "uninstall.py"), "w", encoding="utf-8"
|
||||
) as f:
|
||||
f.write(
|
||||
f"""# Mock uninstaller script
|
||||
raise RuntimeError("Fake exception for unit testing")
|
||||
"""
|
||||
)
|
||||
self.test_object.run_uninstall_script(
|
||||
toplevel_path
|
||||
) # The exception does not leak out
|
||||
|
||||
|
||||
class TestMacroUninstaller(unittest.TestCase):
|
||||
"""Test class for addonmanager_uninstaller.py non-GUI functionality"""
|
||||
|
||||
MODULE = "test_uninstaller" # file name without extension
|
||||
|
||||
def setUp(self):
|
||||
self.mock_addon = MockAddon()
|
||||
self.mock_addon.macro = MockMacro()
|
||||
self.test_object = MacroUninstaller(self.mock_addon)
|
||||
self.signals_caught = []
|
||||
|
||||
self.test_object.finished.connect(
|
||||
functools.partial(self.catch_signal, "finished")
|
||||
)
|
||||
self.test_object.success.connect(
|
||||
functools.partial(self.catch_signal, "success")
|
||||
)
|
||||
self.test_object.failure.connect(
|
||||
functools.partial(self.catch_signal, "failure")
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def catch_signal(self, signal_name, *_):
|
||||
"""Internal use: used to catch and log any emitted signals. Not called directly."""
|
||||
self.signals_caught.append(signal_name)
|
||||
|
||||
def test_remove_simple_macro(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
self.test_object.installation_location = temp_dir
|
||||
self.mock_addon.macro.install(temp_dir)
|
||||
# Make sure the setup worked, otherwise the test is meaningless
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(temp_dir, self.mock_addon.macro.filename))
|
||||
)
|
||||
self.test_object.run()
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(temp_dir, self.mock_addon.macro.filename))
|
||||
)
|
||||
self.assertNotIn("failure", self.signals_caught)
|
||||
self.assertIn("success", self.signals_caught)
|
||||
self.assertIn("finished", self.signals_caught)
|
||||
|
||||
def test_remove_macro_with_icon(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
self.test_object.installation_location = temp_dir
|
||||
self.mock_addon.macro.icon = "mock_icon_test.svg"
|
||||
self.mock_addon.macro.install(temp_dir)
|
||||
# Make sure the setup worked, otherwise the test is meaningless
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(temp_dir, self.mock_addon.macro.filename))
|
||||
)
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(temp_dir, self.mock_addon.macro.icon))
|
||||
)
|
||||
self.test_object.run()
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(temp_dir, self.mock_addon.macro.filename))
|
||||
)
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(temp_dir, self.mock_addon.macro.icon))
|
||||
)
|
||||
self.assertNotIn("failure", self.signals_caught)
|
||||
self.assertIn("success", self.signals_caught)
|
||||
self.assertIn("finished", self.signals_caught)
|
||||
|
||||
def test_remove_macro_with_xpm_data(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
self.test_object.installation_location = temp_dir
|
||||
self.mock_addon.macro.xpm = "/*Fake XPM data*/"
|
||||
self.mock_addon.macro.install(temp_dir)
|
||||
# Make sure the setup worked, otherwise the test is meaningless
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(temp_dir, self.mock_addon.macro.filename))
|
||||
)
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(temp_dir, "MockMacro_icon.xpm"))
|
||||
)
|
||||
self.test_object.run()
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(temp_dir, self.mock_addon.macro.filename))
|
||||
)
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(temp_dir, "MockMacro_icon.xpm"))
|
||||
)
|
||||
self.assertNotIn("failure", self.signals_caught)
|
||||
self.assertIn("success", self.signals_caught)
|
||||
self.assertIn("finished", self.signals_caught)
|
||||
|
||||
def test_remove_macro_with_files(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
self.test_object.installation_location = temp_dir
|
||||
self.mock_addon.macro.other_files = [
|
||||
"test_file_1.txt",
|
||||
"test_file_2.FCMacro",
|
||||
"subdir/test_file_3.txt",
|
||||
]
|
||||
self.mock_addon.macro.install(temp_dir)
|
||||
# Make sure the setup worked, otherwise the test is meaningless
|
||||
for f in self.mock_addon.macro.other_files:
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(temp_dir, f)),
|
||||
f"Expected {f} to exist, and it does not",
|
||||
)
|
||||
self.test_object.run()
|
||||
for f in self.mock_addon.macro.other_files:
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(temp_dir, f)),
|
||||
f"Expected {f} to be removed, and it was not",
|
||||
)
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(temp_dir, "subdir")),
|
||||
"Failed to remove empty subdirectory",
|
||||
)
|
||||
self.assertNotIn("failure", self.signals_caught)
|
||||
self.assertIn("success", self.signals_caught)
|
||||
self.assertIn("finished", self.signals_caught)
|
||||
|
||||
def test_remove_nonexistent_macro(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
self.test_object.installation_location = temp_dir
|
||||
# Don't run the installer:
|
||||
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(temp_dir, self.mock_addon.macro.filename))
|
||||
)
|
||||
self.test_object.run() # Should not raise an exception
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(temp_dir, self.mock_addon.macro.filename))
|
||||
)
|
||||
self.assertNotIn("failure", self.signals_caught)
|
||||
self.assertIn("success", self.signals_caught)
|
||||
self.assertIn("finished", self.signals_caught)
|
||||
|
||||
def test_remove_write_protected_macro(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
self.test_object.installation_location = temp_dir
|
||||
self.mock_addon.macro.install(temp_dir)
|
||||
# Make sure the setup worked, otherwise the test is meaningless
|
||||
f = os.path.join(temp_dir, self.mock_addon.macro.filename)
|
||||
self.assertTrue(os.path.exists(f))
|
||||
os.chmod(f, S_IREAD | S_IRGRP | S_IROTH)
|
||||
self.test_object.run()
|
||||
os.chmod(f, S_IWUSR | S_IREAD)
|
||||
|
||||
self.assertIn("failure", self.signals_caught)
|
||||
self.assertNotIn("success", self.signals_caught)
|
||||
self.assertIn("finished", self.signals_caught)
|
||||
|
||||
def test_cleanup_directories_multiple_empty(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
empty_directories = set(["empty1", "empty2", "empty3"])
|
||||
full_paths = set()
|
||||
for directory in empty_directories:
|
||||
full_path = os.path.join(temp_dir, directory)
|
||||
os.mkdir(full_path)
|
||||
full_paths.add(full_path)
|
||||
|
||||
for directory in full_paths:
|
||||
self.assertTrue(directory, "Test code failed to create {directory}")
|
||||
self.test_object._cleanup_directories(full_paths)
|
||||
for directory in full_paths:
|
||||
self.assertFalse(os.path.exists(directory))
|
||||
|
||||
def test_cleanup_directories_none(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
full_paths = set()
|
||||
self.test_object._cleanup_directories(full_paths) # Shouldn't throw
|
||||
|
||||
def test_cleanup_directories_not_empty(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
empty_directories = set(["empty1", "empty2", "empty3"])
|
||||
full_paths = set()
|
||||
for directory in empty_directories:
|
||||
full_path = os.path.join(temp_dir, directory)
|
||||
os.mkdir(full_path)
|
||||
full_paths.add(full_path)
|
||||
with open(
|
||||
os.path.join(full_path, "test.txt"), "w", encoding="utf-8"
|
||||
) as f:
|
||||
f.write("Unit test dummy data\n")
|
||||
|
||||
for directory in full_paths:
|
||||
self.assertTrue(directory, "Test code failed to create {directory}")
|
||||
self.test_object._cleanup_directories(full_paths)
|
||||
for directory in full_paths:
|
||||
self.assertTrue(os.path.exists(directory))
|
||||
Reference in New Issue
Block a user