Files
Azornes-Comfyui-Model-Resolver/tests/test_file_manager.py
T

214 lines
8.8 KiB
Python

import os
import subprocess
import tempfile
import unittest
from unittest.mock import MagicMock, patch
from core.file_manager import (
FileManagerError,
FileManagerUnavailableError,
UnsupportedFileManagerPlatformError,
normalize_file_manager_path,
open_in_file_manager,
)
class FileManagerTests(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.TemporaryDirectory()
self.directory = os.path.join(self.temp_dir.name, "models with spaces")
os.makedirs(self.directory)
self.file_path = os.path.join(self.directory, "módel.safetensors")
with open(self.file_path, "wb") as handle:
handle.write(b"test")
def tearDown(self):
self.temp_dir.cleanup()
@staticmethod
def successful_process():
process = MagicMock()
process.wait.return_value = 0
return process
def test_rejects_empty_non_string_and_null_byte_paths(self):
for value in ("", " ", None, {"path": "model"}):
with self.subTest(value=value), self.assertRaises(ValueError):
normalize_file_manager_path(value)
with self.assertRaises(ValueError):
normalize_file_manager_path("bad\x00path")
def test_preserves_an_existing_posix_path_with_literal_backslash(self):
with patch("core.file_manager.os.name", "posix"), patch(
"core.file_manager._normalized_absolute_path",
side_effect=["/models/literal\\name", "/models/literal/name"],
), patch(
"core.file_manager.os.path.exists",
side_effect=[True],
):
result = normalize_file_manager_path("/models/literal\\name")
self.assertEqual(result, "/models/literal\\name")
def test_recovers_frontend_backslash_separators_on_posix(self):
with patch("core.file_manager.os.name", "posix"), patch(
"core.file_manager._normalized_absolute_path",
side_effect=["/models/pony\\styles", "/models/pony/styles"],
), patch(
"core.file_manager.os.path.exists",
side_effect=[False, True],
):
result = normalize_file_manager_path("/models/pony\\styles")
self.assertEqual(result, "/models/pony/styles")
@patch("core.file_manager.subprocess.Popen")
@patch("core.file_manager.shutil.which")
def test_windows_reveals_a_file_and_handles_unicode(self, mock_which, mock_popen):
mock_which.return_value = r"C:\Windows\explorer.exe"
mock_popen.return_value = self.successful_process()
result = open_in_file_manager(self.file_path, system="Windows")
command = mock_popen.call_args.args[0]
self.assertEqual(command, [r"C:\Windows\explorer.exe", "/select,", os.path.realpath(self.file_path)])
self.assertFalse(mock_popen.call_args.kwargs["shell"])
self.assertTrue(result["selected"])
@patch("core.file_manager.subprocess.Popen")
@patch("core.file_manager.shutil.which")
def test_windows_opens_a_directory_without_select(self, mock_which, mock_popen):
mock_which.return_value = r"C:\Windows\explorer.exe"
mock_popen.return_value = self.successful_process()
result = open_in_file_manager(self.directory, system="Windows")
command = mock_popen.call_args.args[0]
self.assertEqual(command, [r"C:\Windows\explorer.exe", os.path.realpath(self.directory)])
self.assertFalse(result["selected"])
@patch("core.file_manager.subprocess.Popen")
@patch("core.file_manager.shutil.which")
def test_windows_does_not_treat_explorer_exit_code_one_as_failure(
self, mock_which, mock_popen
):
mock_which.return_value = r"C:\Windows\explorer.exe"
process = MagicMock()
process.wait.return_value = 1
mock_popen.return_value = process
result = open_in_file_manager(self.file_path, system="Windows")
process.wait.assert_not_called()
self.assertEqual(result["launcher"], "explorer")
@patch("core.file_manager.subprocess.Popen")
@patch("core.file_manager.shutil.which", return_value="/usr/bin/open")
def test_macos_reveals_a_file(self, _mock_which, mock_popen):
mock_popen.return_value = self.successful_process()
result = open_in_file_manager(self.file_path, system="Darwin")
self.assertEqual(
mock_popen.call_args.args[0],
["/usr/bin/open", "-R", os.path.realpath(self.file_path)],
)
self.assertTrue(result["selected"])
@patch("core.file_manager.subprocess.Popen")
@patch("core.file_manager.shutil.which", return_value="/usr/bin/open")
def test_macos_opens_a_directory(self, _mock_which, mock_popen):
mock_popen.return_value = self.successful_process()
open_in_file_manager(self.directory, system="Darwin")
self.assertEqual(
mock_popen.call_args.args[0],
["/usr/bin/open", os.path.realpath(self.directory)],
)
@patch("core.file_manager.subprocess.Popen")
@patch("core.file_manager.shutil.which")
def test_linux_opens_the_parent_directory_with_xdg_open(self, mock_which, mock_popen):
mock_which.side_effect = lambda name: "/usr/bin/xdg-open" if name == "xdg-open" else None
mock_popen.return_value = self.successful_process()
result = open_in_file_manager(self.file_path, system="Linux")
self.assertEqual(
mock_popen.call_args.args[0],
["/usr/bin/xdg-open", os.path.realpath(self.directory)],
)
self.assertFalse(result["selected"])
@patch("core.file_manager.subprocess.Popen")
@patch("core.file_manager.shutil.which")
def test_linux_falls_back_to_gio_for_a_directory(self, mock_which, mock_popen):
mock_which.side_effect = lambda name: "/usr/bin/gio" if name == "gio" else None
mock_popen.return_value = self.successful_process()
result = open_in_file_manager(self.directory, system="Linux")
self.assertEqual(
mock_popen.call_args.args[0],
["/usr/bin/gio", "open", os.path.realpath(self.directory)],
)
self.assertEqual(result["launcher"], "gio")
@patch("core.file_manager.shutil.which", return_value=None)
def test_missing_launcher_has_a_specific_error(self, _mock_which):
with self.assertRaises(FileManagerUnavailableError):
open_in_file_manager(self.directory, system="Linux")
def test_missing_and_special_paths_are_rejected(self):
with self.assertRaises(FileNotFoundError):
open_in_file_manager(os.path.join(self.temp_dir.name, "missing"), system="Linux")
with patch("core.file_manager.os.path.exists", return_value=True), patch(
"core.file_manager.os.path.isfile", return_value=False
), patch(
"core.file_manager.os.path.isdir", return_value=False
), self.assertRaises(FileManagerError):
open_in_file_manager(self.directory, system="Linux")
def test_unsupported_platform_has_a_specific_error(self):
with self.assertRaises(UnsupportedFileManagerPlatformError):
open_in_file_manager(self.directory, system="FreeBSD")
@patch("core.file_manager.subprocess.Popen", side_effect=OSError("launch failed"))
@patch("core.file_manager.shutil.which", return_value="/usr/bin/xdg-open")
def test_spawn_failure_is_wrapped(self, _mock_which, _mock_popen):
with self.assertRaisesRegex(FileManagerError, "launch failed"):
open_in_file_manager(self.directory, system="Linux")
@patch("core.file_manager.subprocess.Popen")
@patch("core.file_manager.shutil.which", return_value="/usr/bin/xdg-open")
def test_fast_nonzero_launcher_exit_is_reported(self, _mock_which, mock_popen):
process = MagicMock()
process.wait.return_value = 3
mock_popen.return_value = process
with self.assertRaisesRegex(FileManagerError, "code 3"):
open_in_file_manager(self.directory, system="Linux")
@patch("core.file_manager.subprocess.Popen")
@patch("core.file_manager.shutil.which", return_value="/usr/bin/xdg-open")
def test_launcher_wait_failure_is_wrapped(self, _mock_which, mock_popen):
process = MagicMock()
process.wait.side_effect = OSError("wait failed")
mock_popen.return_value = process
with self.assertRaisesRegex(FileManagerError, "wait failed"):
open_in_file_manager(self.directory, system="Linux")
@patch("core.file_manager.subprocess.Popen")
@patch("core.file_manager.shutil.which", return_value="/usr/bin/xdg-open")
def test_long_running_launcher_is_treated_as_started(self, _mock_which, mock_popen):
process = MagicMock()
process.wait.side_effect = subprocess.TimeoutExpired("xdg-open", 2.0)
mock_popen.return_value = process
result = open_in_file_manager(self.directory, system="Linux")
self.assertEqual(result["launcher"], "xdg-open")