Files
PythonInUnreal/tests/test_clip_discovery.py
T
2026-10-31 20:16:32 +01:00

82 lines
3.6 KiB
Python

"""Automatic folder discovery, explicit selection and the manual reimport switch."""
import importlib.util
from pathlib import Path
import sys
import tempfile
import unittest
from unittest.mock import MagicMock, patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "Content/Python"))
from ue_workshop.characters import _clips
class Mesh:
def get_editor_property(self, name):
return "Skeleton"
class Clip:
def get_name(self):
return "EmbeddedTake"
class DiscoveryTests(unittest.TestCase):
def setUp(self):
self.folder = tempfile.TemporaryDirectory()
self.addCleanup(self.folder.cleanup)
self.model = Path(self.folder.name) / "Hero.fbx"
self.walk = self.model.with_name("great sword walk.fbx")
self.attack = self.model.with_name("great sword attack (2).fbx")
for path in (self.model, self.walk, self.attack):
path.touch()
unreal = MagicMock()
unreal.SkeletalMesh, unreal.AnimSequence = Mesh, Clip
name = "ue_workshop._skeletal_discovery_test"
path = Path(__file__).resolve().parents[1] / "Content/Python/ue_workshop/skeletal.py"
spec = importlib.util.spec_from_file_location(name, path)
self.module = importlib.util.module_from_spec(spec)
with patch.dict(sys.modules, {"unreal": unreal, name: self.module}):
spec.loader.exec_module(self.module)
self.cache = patch.object(self.module, "_cached", return_value=[Mesh()]).start()
self.importer = patch.object(self.module, "_import", return_value=[Mesh()]).start()
self.animation = patch.object(self.module, "import_animation", side_effect=lambda *a, **kw: Clip()).start()
self.addCleanup(patch.stopall)
def test_discovery_excludes_model_and_derives_names_without_aliases(self):
self.assertEqual(_clips(self.model), {
"great_sword_attack_2": self.attack,
"great_sword_walk": self.walk,
})
def test_omitted_animations_loads_every_neighbor_and_reuses_model(self):
result = self.module.import_model(self.model, name="Hero")
self.assertEqual(set(result.animations), {"great_sword_attack_2", "great_sword_walk"})
self.importer.assert_not_called()
self.assertEqual(self.animation.call_count, 2)
for call in self.animation.call_args_list:
self.assertFalse(call.kwargs["reimport"])
def test_empty_dictionary_disables_discovery_and_keeps_embedded_name(self):
self.cache.return_value = [Mesh(), Clip()]
result = self.module.import_model(self.model, name="Hero", animations={})
self.assertEqual(set(result.animations), {"EmbeddedTake"})
self.animation.assert_not_called()
def test_explicit_selection_ignores_other_files(self):
result = self.module.import_model(self.model, name="Hero", animations={"selected": self.walk})
self.assertEqual(set(result.animations), {"selected"})
self.animation.assert_called_once()
self.assertEqual(self.animation.call_args.args[0], self.walk)
def test_reimport_true_reimports_model_and_discovered_clips(self):
self.module.import_model(self.model, name="Hero", reimport=True)
self.cache.assert_not_called()
self.importer.assert_called_once()
self.assertTrue(self.importer.call_args.kwargs["reimport"])
self.assertTrue(all(call.kwargs["reimport"] for call in self.animation.call_args_list))
def test_colliding_normalized_names_are_rejected(self):
self.model.with_name("great-sword-walk.fbx").touch()
with self.assertRaises(ValueError):
_clips(self.model)