86 lines
4.5 KiB
Python
86 lines
4.5 KiB
Python
"""Focused guards: anatomy/phase validation and atomic generation promotion."""
|
|
import copy
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
import tempfile
|
|
import types
|
|
import unittest
|
|
from unittest import mock
|
|
|
|
REPO=Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0,str(REPO/'Tools'))
|
|
import enemy_character_pipeline as pipeline
|
|
import generate_animal_walk as generator
|
|
|
|
class EnemyProductionTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.data=json.loads((REPO/'Config/Art/EnemyCharacters/RockBall.json').read_text(encoding='utf-8'))
|
|
|
|
def test_recipe_rejects_wrong_anatomy_and_phase_order(self):
|
|
with mock.patch.object(Path,'read_text',return_value=json.dumps(self.data)):
|
|
self.assertEqual(pipeline.recipe(Path('fixture'))['id'],'RockBall')
|
|
for change in ({'profile':'quadruped_unvalidated'},{'attackImpact':.1},{'walkStride':10}):
|
|
data={**self.data,**change}
|
|
with mock.patch.object(Path,'read_text',return_value=json.dumps(data)),self.assertRaises(ValueError):
|
|
pipeline.recipe(Path('fixture'))
|
|
|
|
def test_generation_failure_preserves_previous_artifacts_and_receipt(self):
|
|
self.run_generation(False)
|
|
|
|
def test_companion_role_and_legacy_enemy_recipe(self):
|
|
with mock.patch.object(Path,'read_text',return_value=json.dumps(self.data)):
|
|
legacy=pipeline.recipe(Path('fixture'))
|
|
self.assertEqual(legacy['visualPrefab'],self.data['enemyPrefab'])
|
|
self.assertEqual(legacy['attackEvent'],'OnEnemyAttackImpact')
|
|
companion={**self.data,'usage':'companion_visual','attackStyle':'ranged_cast',
|
|
'visualPrefab':'Assets/Bundles/Characters/Animal/FireCat/FireCat_Companion.prefab'}
|
|
companion.pop('enemyPrefab')
|
|
with mock.patch.object(Path,'read_text',return_value=json.dumps(companion)):
|
|
result=pipeline.recipe(Path('fixture'))
|
|
self.assertEqual(result['attackEvent'],'OnCompanionAttackImpact')
|
|
for change in ({'usage':'unknown'},{'boneMappingReady':False},{'attackStyle':'unknown'},
|
|
{'visualPrefab':'../outside.prefab'},{'attackReach':20}):
|
|
with mock.patch.object(Path,'read_text',return_value=json.dumps({**companion,**change})),self.assertRaises(ValueError):
|
|
pipeline.recipe(Path('fixture'))
|
|
|
|
def test_generation_success_commits_matching_receipt_last(self):
|
|
self.run_generation(True)
|
|
|
|
def run_generation(self,success):
|
|
root=REPO/'Tools/output/enemy_pipeline_tests'
|
|
root.mkdir(exist_ok=True)
|
|
with tempfile.TemporaryDirectory(dir=root) as temporary:
|
|
base=Path(temporary).resolve()
|
|
self.assertTrue(base.is_relative_to(root.resolve()))
|
|
out=base/'motion';out.mkdir()
|
|
rig=base/'rig_fixture.dat';rig.write_text('same-rig-fixture')
|
|
(out/'animated.fbx').write_text('previous-fixture')
|
|
(out/'motion_request.json').write_text('{"status":"previous"}')
|
|
runner=types.ModuleType('backend.unimate_runner')
|
|
runner._cfg=lambda: {'unimate_jobs_dir':'/fixture'}
|
|
runner._scp_to=lambda *a,**k: None
|
|
runner._ssh=lambda *a,**k: None
|
|
def submit(**kwargs):
|
|
if not success:raise RuntimeError('fixture inference failure')
|
|
(kwargs['directory']/'animated.fbx').write_text('new-fixture')
|
|
return {'remote_job':'fixture-only-no-network'}
|
|
runner.submit_unimate_job=submit
|
|
backend=types.ModuleType('backend');backend.__path__=[];backend.unimate_runner=runner
|
|
argv=['generate_animal_walk','--rig',str(rig),'--out',str(out),'--motion','Attack','--face-right','right','--face-left','left']
|
|
with mock.patch.dict(sys.modules,{'backend':backend,'backend.unimate_runner':runner}),mock.patch.object(sys,'argv',argv):
|
|
if success:generator.main()
|
|
else:
|
|
with self.assertRaises(RuntimeError):generator.main()
|
|
record=json.loads((out/'motion_request.json').read_text())
|
|
if success:
|
|
self.assertEqual((out/'animated.fbx').read_text(),'new-fixture')
|
|
self.assertEqual(record['status'],'generated')
|
|
self.assertEqual(record['source_fbx_sha256'],hashlib.sha256((out/'animated.fbx').read_bytes()).hexdigest())
|
|
else:
|
|
self.assertEqual((out/'animated.fbx').read_text(),'previous-fixture')
|
|
self.assertEqual(record,{'status':'previous'})
|
|
|
|
if __name__=='__main__':unittest.main()
|