Files
Zordon/tests/test_engine_manager.py
T
brett 24eb7b5616 Add Unit Tests (#132)
* Add tests and new github workflow

* Add new unit tests

* Add Github CI workflow

* Workflow fix

* Add pytest install to workflow file

* More CI / test updates

* More test cleanup

* Whitespace cleanup and link complexity override

* More whitespace cleanup

* Make lint less strict

* More lint tweaks
2026-06-06 00:02:01 -05:00

240 lines
9.4 KiB
Python

from unittest.mock import MagicMock, patch
import pytest
from src.engines.engine_manager import EngineManager, EngineDownloadWorker
class TestEngineManagerSyncClass:
"""_sync_class propagates instance attrs to class level."""
def test_sync_class_propagates_engines_path(self, engine_manager_instance):
assert EngineManager.engines_path == engine_manager_instance.engines_path
def test_sync_class_noop_when_no_instance(self):
orig = EngineManager._default_instance
try:
EngineManager._default_instance = None
EngineManager.engines_path = 'original'
EngineManager._sync_class()
assert EngineManager.engines_path == 'original'
finally:
EngineManager._default_instance = orig
EngineManager._sync_class()
def test_supported_engines_returns_list(self):
engines = EngineManager.supported_engines()
assert len(engines) >= 2
class TestEngineClassMapping:
"""Mapping file extensions and names to engine classes."""
def test_engine_class_with_name_finds_blender(self, engine_manager_instance):
cls = EngineManager.engine_class_with_name('blender')
assert cls is not None
assert cls.__name__ == 'Blender'
def test_engine_class_with_name_finds_ffmpeg(self, engine_manager_instance):
cls = EngineManager.engine_class_with_name('ffmpeg')
assert cls is not None
assert cls.__name__ == 'FFMPEG'
def test_engine_class_with_name_returns_none_for_unknown(self, engine_manager_instance):
cls = EngineManager.engine_class_with_name('nonexistent')
assert cls is None
def test_engine_class_with_name_case_insensitive(self, engine_manager_instance):
cls = EngineManager.engine_class_with_name('BLENDER')
assert cls is not None
assert cls.__name__ == 'Blender'
def test_engine_class_for_project_path_no_engines_path(self, engine_manager_instance):
engine_manager_instance.engines_path = None
with pytest.raises(FileNotFoundError):
EngineManager.engine_class_for_project_path('test.blend')
class TestGetInstalledEngineData:
"""Parsing directory listings for managed engines."""
def test_get_installed_engine_data_no_path(self, engine_manager_instance):
engine_manager_instance.engines_path = None
with pytest.raises(FileNotFoundError):
EngineManager.get_installed_engine_data()
def test_get_installed_engine_data_empty_dir(self, engine_manager_instance, tmp_path):
engine_manager_instance.engines_path = str(tmp_path / 'engines')
result = EngineManager.get_installed_engine_data(ignore_system=True)
assert result == []
@patch('src.engines.engine_manager.os.listdir')
@patch('src.engines.engine_manager.os.path.isdir')
def test_parse_managed_engine_directory(
self, mock_isdir, mock_listdir, engine_manager_instance, tmp_path,
):
engine_manager_instance.engines_path = str(tmp_path / 'engines')
mock_listdir.return_value = ['blender-3.6.0-macos-arm64']
mock_isdir.return_value = True
result = EngineManager.get_installed_engine_data(ignore_system=True)
assert len(result) == 1
assert result[0]['engine'] == 'blender'
assert result[0]['version'] == '3.6.0'
assert result[0]['system_os'] == 'macos'
assert result[0]['cpu'] == 'arm64'
assert result[0]['type'] == 'managed'
@patch('src.engines.engine_manager.os.listdir')
@patch('src.engines.engine_manager.os.path.isdir')
def test_filter_by_engine_name(
self, mock_isdir, mock_listdir, engine_manager_instance, tmp_path,
):
engine_manager_instance.engines_path = str(tmp_path / 'engines')
mock_listdir.return_value = ['blender-3.6.0-macos-arm64', 'ffmpeg-6.0-macos-arm64']
mock_isdir.return_value = True
blender_only = EngineManager.get_installed_engine_data(
filter_name='blender', ignore_system=True)
assert len(blender_only) == 1
assert blender_only[0]['engine'] == 'blender'
class TestNewestInstalledEngineData:
"""Filtering by system and CPU."""
@patch('src.engines.engine_manager.current_system_os')
@patch('src.engines.engine_manager.current_system_cpu')
def test_newest_filters_by_system_and_cpu(
self, mock_cpu, mock_os, engine_manager_instance, tmp_path,
):
mock_os.return_value = 'macos'
mock_cpu.return_value = 'arm64'
engine_manager_instance.engines_path = str(tmp_path / 'engines')
dirs = [
'blender-3.6.0-macos-arm64',
'blender-4.0.0-linux-x86_64',
'blender-4.1.0-macos-arm64',
]
with (patch('src.engines.engine_manager.os.listdir', return_value=dirs),
patch('src.engines.engine_manager.os.path.isdir', return_value=True)):
result = EngineManager.newest_installed_engine_data(
'blender', ignore_system=True)
assert result['version'] == '4.1.0'
assert result['system_os'] == 'macos'
assert result['cpu'] == 'arm64'
def test_newest_returns_empty_on_no_match(self, engine_manager_instance, tmp_path):
engine_manager_instance.engines_path = str(tmp_path / 'engines')
result = EngineManager.newest_installed_engine_data('nonexistent')
assert result == []
class TestIsVersionInstalled:
"""Checking whether a specific version is installed."""
@patch('src.engines.engine_manager.current_system_os')
@patch('src.engines.engine_manager.current_system_cpu')
def test_finds_matching_version(
self, mock_cpu, mock_os, engine_manager_instance, tmp_path,
):
mock_os.return_value = 'macos'
mock_cpu.return_value = 'arm64'
engine_manager_instance.engines_path = str(tmp_path / 'engines')
dirs = ['blender-3.6.0-macos-arm64', 'blender-4.1.0-macos-arm64']
with (patch('src.engines.engine_manager.os.listdir', return_value=dirs),
patch('src.engines.engine_manager.os.path.isdir', return_value=True)):
result = EngineManager.is_version_installed('blender', '3.6.0')
assert result is not False
@patch('src.engines.engine_manager.current_system_os')
@patch('src.engines.engine_manager.current_system_cpu')
def test_returns_false_on_no_match(
self, mock_cpu, mock_os, engine_manager_instance, tmp_path,
):
mock_os.return_value = 'macos'
mock_cpu.return_value = 'arm64'
engine_manager_instance.engines_path = str(tmp_path / 'engines')
result = EngineManager.is_version_installed('blender', '99.0.0')
assert result is False
class TestDeleteEngineDownload:
"""Deleting a managed engine directory."""
@patch('src.engines.engine_manager.shutil.rmtree')
@patch('src.engines.engine_manager.current_system_os', return_value='macos')
@patch('src.engines.engine_manager.current_system_cpu', return_value='arm64')
def test_delete_managed_engine(
self, mock_cpu, mock_os, mock_rmtree, engine_manager_instance, tmp_path,
):
engine_manager_instance.engines_path = str(tmp_path / 'engines')
engines_dir = tmp_path / 'engines' / 'blender-3.6.0-macos-arm64'
engines_dir.mkdir(parents=True)
(engines_dir / 'Blender').write_text('fake binary')
result = EngineManager.delete_engine_download('blender', '3.6.0')
assert result is True
mock_rmtree.assert_called_once_with(str(engines_dir), ignore_errors=False)
def test_delete_nonexistent_engine(self, engine_manager_instance, tmp_path):
engine_manager_instance.engines_path = str(tmp_path / 'engines')
result = EngineManager.delete_engine_download('nonexistent', '1.0.0')
assert result is False
class TestActiveDownloads:
"""Background download tracking."""
def test_active_downloads_empty_initially(self, engine_manager_instance):
assert EngineManager.active_downloads() == []
def test_download_tasks_tracked(self, engine_manager_instance):
task = MagicMock(spec=EngineDownloadWorker)
task.is_alive.return_value = True
task.name = 'blender-4.0.0-macos-arm64'
engine_manager_instance.download_tasks.append(task)
active = EngineManager.active_downloads()
assert len(active) == 1
class TestCreateWorker:
"""Creating worker instances for render jobs."""
def test_create_worker_raises_when_no_engines_installed(self, engine_manager_instance, tmp_path):
engine_manager_instance.engines_path = str(tmp_path / 'engines')
with patch.object(engine_manager_instance, '_get_installed_engine_data', return_value=[]):
with pytest.raises(FileNotFoundError, match='Cannot find any installed'):
EngineManager.create_worker(
'blender',
input_path=tmp_path / 'test.blend',
output_path=tmp_path / 'output',
)
def test_create_worker_raises_for_unknown_engine(self, engine_manager_instance):
with pytest.raises(AttributeError):
EngineManager.create_worker(
'nonexistent',
input_path='/tmp/test.blend',
output_path='/tmp/output',
)
class TestDownloadableEngines:
"""Engines with a downloader."""
def test_downloadable_engines_returns_list(self, engine_manager_instance):
engines = EngineManager.downloadable_engines()
assert isinstance(engines, list)