"""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" def get_play_length(self): return 1.5 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.catalog = patch.object(self.module, "update_catalog").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"]) self.catalog.assert_called_once_with("Hero", { "great_sword_attack_2": 1.5, "great_sword_walk": 1.5}, replace=True) 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) self.catalog.assert_called_once_with("Hero", {"selected": 1.5}, replace=False) 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)