"""Automatic catalogs preserve other models, partial selections and offline imports.""" from pathlib import Path import sys import tempfile from types import ModuleType import unittest from unittest.mock import patch sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "Content/Python")) from ue_workshop import animation_catalog from ue_workshop.animation_clip import AnimationClip class AutomaticCatalogTests(unittest.TestCase): def setUp(self): self.folder = tempfile.TemporaryDirectory() self.addCleanup(self.folder.cleanup) self.output = Path(self.folder.name) / "animations.py" def load(self): namespace = {"__name__": "ue_workshop._catalog_test", "__package__": "ue_workshop"} exec(compile(self.output.read_text(encoding="utf-8"), str(self.output), "exec"), namespace) return namespace def update(self, name, clips, **options): return animation_catalog.update_catalog(name, clips, output=self.output, **options) def test_new_model_produces_enum_with_durations_without_unreal(self): self.assertEqual(self.update("StudentHero", {"walking": 2.0}), "StudentHeroAnimations") clip = self.load()["StudentHeroAnimations"].WALKING self.assertEqual(clip.key, "walking") self.assertEqual(clip.seconds, 2) self.assertEqual(clip.frames(24), 48) self.assertNotIn("unreal", sys.modules) def test_full_update_preserves_other_models_and_removes_deleted_clips(self): self.update("Fighter", {"walk": 1.0, "attack": 2.0}) self.update("StudentHero", {"jump": 3.0}) self.update("Fighter", {"walk": 1.5}) namespace = self.load() self.assertEqual([clip.key for clip in namespace["FighterAnimations"]], ["walk"]) self.assertEqual(namespace["StudentHeroAnimations"].JUMP.seconds, 3.0) def test_partial_selection_updates_duration_without_erasing_other_clips(self): self.update("Hero", {"walk": 1.0, "attack": 2.0}) self.update("Hero", {"walk": 1.5}, replace=False) catalog = self.load()["HeroAnimations"] self.assertEqual(catalog.WALK.seconds, 1.5) self.assertEqual(catalog.ATTACK.seconds, 2.0) def test_unchanged_metadata_does_not_rewrite_file(self): self.update("Hero", {"walk": 1.0}) with patch.object(animation_catalog.tempfile, "NamedTemporaryFile", side_effect=AssertionError("rewritten")): self.update("Hero", {"walk": 1.0}) def test_empty_model_is_valid(self): self.update("Hero", {}) self.assertEqual(list(self.load()["HeroAnimations"]), []) def test_invalid_metadata_does_not_damage_existing_catalog(self): self.update("Hero", {"walk": 1.0}) before = self.output.read_bytes() for data in ({"walk": 0}, {"walk": float("nan")}, {"walk": 1.0, "WALK": 2.0}): with self.subTest(data=data), self.assertRaises(ValueError): self.update("Hero", data) self.assertEqual(self.output.read_bytes(), before) def test_publish_adds_class_to_loaded_module_without_replacing_other_enums(self): module = ModuleType("ue_workshop.animations") module.AnimationClip = AnimationClip module.ExistingAnimations = object() original = module.ExistingAnimations source = animation_catalog._class_source("StudentHeroAnimations", {"walk": 2.0}) with patch.dict(sys.modules, {"ue_workshop.animations": module}): animation_catalog._publish_class(source, self.output) self.assertEqual(module.StudentHeroAnimations.WALK.frames(24), 48) self.assertIs(module.ExistingAnimations, original)