mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 11:40:13 +01:00
696 lines
22 KiB
Python
696 lines
22 KiB
Python
import unittest
|
|
from unittest.mock import patch
|
|
|
|
from libs.animation import (
|
|
Animation,
|
|
BaseAnimation,
|
|
FadeAnimation,
|
|
MoveAnimation,
|
|
TextStretchAnimation,
|
|
TextureChangeAnimation,
|
|
TextureResizeAnimation,
|
|
parse_animations,
|
|
)
|
|
|
|
|
|
class TestBaseAnimation(unittest.TestCase):
|
|
"""Test cases for the BaseAnimation class."""
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
@patch('libs.animation.global_data')
|
|
def setUp(self, mock_global_data, mock_get_ms):
|
|
"""Set up test fixtures."""
|
|
mock_get_ms.return_value = 0.0
|
|
mock_global_data.input_locked = 0
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_initialization(self, mock_get_ms):
|
|
"""Test basic initialization of BaseAnimation."""
|
|
mock_get_ms.return_value = 100.0
|
|
|
|
anim = BaseAnimation(duration=1000.0, delay=100.0, loop=True, lock_input=True)
|
|
|
|
self.assertEqual(anim.duration, 1000.0)
|
|
self.assertEqual(anim.delay, 100.0)
|
|
self.assertEqual(anim.delay_saved, 100.0)
|
|
self.assertEqual(anim.start_ms, 100.0)
|
|
self.assertFalse(anim.is_finished)
|
|
self.assertEqual(anim.attribute, 0)
|
|
self.assertFalse(anim.is_started)
|
|
self.assertTrue(anim.loop)
|
|
self.assertTrue(anim.lock_input)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
@patch('libs.animation.global_data')
|
|
def test_start(self, mock_global_data, mock_get_ms):
|
|
"""Test starting an animation."""
|
|
mock_get_ms.return_value = 200.0
|
|
mock_global_data.input_locked = 0
|
|
|
|
anim = BaseAnimation(duration=1000.0, lock_input=True)
|
|
anim.start()
|
|
|
|
self.assertTrue(anim.is_started)
|
|
self.assertFalse(anim.is_finished)
|
|
self.assertEqual(mock_global_data.input_locked, 1)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
@patch('libs.animation.global_data')
|
|
def test_restart(self, mock_global_data, mock_get_ms):
|
|
"""Test restarting an animation."""
|
|
mock_get_ms.side_effect = [0.0, 500.0, 1000.0]
|
|
mock_global_data.input_locked = 0
|
|
|
|
anim = BaseAnimation(duration=1000.0, delay=100.0, lock_input=True)
|
|
anim.is_finished = True
|
|
anim.delay = 0.0
|
|
|
|
anim.restart()
|
|
|
|
self.assertEqual(anim.start_ms, 500.0)
|
|
self.assertFalse(anim.is_finished)
|
|
self.assertEqual(anim.delay, 100.0)
|
|
self.assertEqual(mock_global_data.input_locked, 1)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
@patch('libs.animation.global_data')
|
|
def test_pause_unpause(self, mock_global_data, mock_get_ms):
|
|
"""Test pausing and unpausing."""
|
|
mock_get_ms.return_value = 0.0
|
|
mock_global_data.input_locked = 1
|
|
|
|
anim = BaseAnimation(duration=1000.0, lock_input=True)
|
|
anim.is_started = True
|
|
|
|
anim.pause()
|
|
self.assertFalse(anim.is_started)
|
|
self.assertEqual(mock_global_data.input_locked, 0)
|
|
|
|
anim.unpause()
|
|
self.assertTrue(anim.is_started)
|
|
self.assertEqual(mock_global_data.input_locked, 1)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_loop_restarts(self, mock_get_ms):
|
|
"""Test that looped animations restart when finished."""
|
|
mock_get_ms.side_effect = [0.0, 100.0]
|
|
|
|
anim = BaseAnimation(duration=1000.0, loop=True)
|
|
anim.is_finished = True
|
|
|
|
with patch.object(anim, 'restart') as mock_restart:
|
|
anim.update(100.0)
|
|
mock_restart.assert_called_once()
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
@patch('libs.animation.global_data')
|
|
def test_input_lock_unlock(self, mock_global_data, mock_get_ms):
|
|
"""Test input locking mechanism."""
|
|
mock_get_ms.return_value = 0.0
|
|
mock_global_data.input_locked = 1
|
|
|
|
anim = BaseAnimation(duration=1000.0, lock_input=True)
|
|
anim.is_finished = True
|
|
anim.unlocked = False
|
|
|
|
anim.update(100.0)
|
|
|
|
self.assertTrue(anim.unlocked)
|
|
self.assertEqual(mock_global_data.input_locked, 0)
|
|
|
|
def test_easing_functions(self):
|
|
"""Test easing functions produce expected values."""
|
|
anim = BaseAnimation(duration=1000.0)
|
|
|
|
# Test quadratic ease in
|
|
self.assertAlmostEqual(anim._ease_in(0.5, "quadratic"), 0.25)
|
|
self.assertAlmostEqual(anim._ease_in(1.0, "quadratic"), 1.0)
|
|
|
|
# Test cubic ease in
|
|
self.assertAlmostEqual(anim._ease_in(0.5, "cubic"), 0.125)
|
|
|
|
# Test exponential ease in
|
|
self.assertEqual(anim._ease_in(0.0, "exponential"), 0)
|
|
|
|
# Test quadratic ease out
|
|
self.assertAlmostEqual(anim._ease_out(0.5, "quadratic"), 0.75)
|
|
|
|
# Test cubic ease out
|
|
self.assertAlmostEqual(anim._ease_out(0.5, "cubic"), 0.875)
|
|
|
|
# Test exponential ease out
|
|
self.assertEqual(anim._ease_out(1.0, "exponential"), 1)
|
|
|
|
|
|
class TestFadeAnimation(unittest.TestCase):
|
|
"""Test cases for the FadeAnimation class."""
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_initialization(self, mock_get_ms):
|
|
"""Test fade animation initialization."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = FadeAnimation(
|
|
duration=1000.0,
|
|
initial_opacity=1.0,
|
|
final_opacity=0.0,
|
|
delay=100.0,
|
|
ease_in="quadratic"
|
|
)
|
|
|
|
self.assertEqual(anim.initial_opacity, 1.0)
|
|
self.assertEqual(anim.final_opacity, 0.0)
|
|
self.assertEqual(anim.attribute, 1.0)
|
|
self.assertEqual(anim.ease_in, "quadratic")
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_fade_during_delay(self, mock_get_ms):
|
|
"""Test that opacity stays at initial during delay."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = FadeAnimation(duration=1000.0, initial_opacity=1.0, final_opacity=0.0, delay=500.0)
|
|
anim.start()
|
|
|
|
anim.update(250.0) # Within delay period
|
|
|
|
self.assertEqual(anim.attribute, 1.0)
|
|
self.assertFalse(anim.is_finished)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_fade_progression(self, mock_get_ms):
|
|
"""Test fade progresses correctly."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = FadeAnimation(duration=1000.0, initial_opacity=1.0, final_opacity=0.0)
|
|
anim.start()
|
|
|
|
anim.update(500.0) # Halfway through
|
|
|
|
self.assertAlmostEqual(anim.attribute, 0.5, places=2)
|
|
self.assertFalse(anim.is_finished)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_fade_completion(self, mock_get_ms):
|
|
"""Test fade completes at final opacity."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = FadeAnimation(duration=1000.0, initial_opacity=1.0, final_opacity=0.0)
|
|
anim.start()
|
|
|
|
anim.update(1000.0) # End of animation
|
|
|
|
self.assertEqual(anim.attribute, 0.0)
|
|
self.assertTrue(anim.is_finished)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_fade_with_reverse_delay(self, mock_get_ms):
|
|
"""Test fade reverses after reverse_delay."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = FadeAnimation(
|
|
duration=1000.0,
|
|
initial_opacity=1.0,
|
|
final_opacity=0.0,
|
|
reverse_delay=200.0
|
|
)
|
|
anim.start()
|
|
|
|
anim.update(1000.0) # Complete first fade
|
|
|
|
self.assertEqual(anim.attribute, 0.0)
|
|
self.assertTrue(anim.is_reversing)
|
|
self.assertEqual(anim.initial_opacity, 0.0)
|
|
self.assertEqual(anim.final_opacity, 1.0)
|
|
self.assertFalse(anim.is_finished)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_fade_with_easing(self, mock_get_ms):
|
|
"""Test fade applies easing correctly."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = FadeAnimation(
|
|
duration=1000.0,
|
|
initial_opacity=0.0,
|
|
final_opacity=1.0,
|
|
ease_in="quadratic"
|
|
)
|
|
anim.start()
|
|
|
|
anim.update(500.0) # Halfway
|
|
|
|
# With quadratic ease in, at 0.5 progress we should have 0.25
|
|
self.assertAlmostEqual(anim.attribute, 0.25, places=2)
|
|
|
|
|
|
class TestMoveAnimation(unittest.TestCase):
|
|
"""Test cases for the MoveAnimation class."""
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_initialization(self, mock_get_ms):
|
|
"""Test move animation initialization."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = MoveAnimation(
|
|
duration=1000.0,
|
|
start_position=0,
|
|
total_distance=100,
|
|
delay=50.0
|
|
)
|
|
|
|
self.assertEqual(anim.start_position, 0)
|
|
self.assertEqual(anim.total_distance, 100)
|
|
self.assertEqual(anim.delay, 50.0)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_move_during_delay(self, mock_get_ms):
|
|
"""Test position stays at start during delay."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = MoveAnimation(duration=1000.0, start_position=50, total_distance=100, delay=200.0)
|
|
anim.start()
|
|
|
|
anim.update(100.0) # Within delay
|
|
|
|
self.assertEqual(anim.attribute, 50)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_move_progression(self, mock_get_ms):
|
|
"""Test move progresses correctly."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = MoveAnimation(duration=1000.0, start_position=0, total_distance=100)
|
|
anim.start()
|
|
|
|
anim.update(500.0) # Halfway
|
|
|
|
self.assertAlmostEqual(anim.attribute, 50.0, places=2)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_move_completion(self, mock_get_ms):
|
|
"""Test move completes at final position."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = MoveAnimation(duration=1000.0, start_position=0, total_distance=100)
|
|
anim.start()
|
|
|
|
anim.update(1000.0)
|
|
|
|
self.assertEqual(anim.attribute, 100)
|
|
self.assertTrue(anim.is_finished)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_move_with_reverse_delay(self, mock_get_ms):
|
|
"""Test move reverses after reverse_delay."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = MoveAnimation(
|
|
duration=1000.0,
|
|
start_position=0,
|
|
total_distance=100,
|
|
reverse_delay=100.0
|
|
)
|
|
anim.start()
|
|
|
|
anim.update(1000.0) # Complete first move
|
|
|
|
self.assertEqual(anim.start_position, 100)
|
|
self.assertEqual(anim.total_distance, -100)
|
|
self.assertIsNone(anim.reverse_delay)
|
|
self.assertFalse(anim.is_finished)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_move_with_easing(self, mock_get_ms):
|
|
"""Test move applies easing."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = MoveAnimation(
|
|
duration=1000.0,
|
|
start_position=0,
|
|
total_distance=100,
|
|
ease_out="quadratic"
|
|
)
|
|
anim.start()
|
|
|
|
anim.update(500.0)
|
|
|
|
# With quadratic ease out, at 0.5 progress we should have 0.75
|
|
self.assertAlmostEqual(anim.attribute, 75.0, places=2)
|
|
|
|
|
|
class TestTextureChangeAnimation(unittest.TestCase):
|
|
"""Test cases for the TextureChangeAnimation class."""
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_initialization(self, mock_get_ms):
|
|
"""Test texture change animation initialization."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
textures = [(0.0, 100.0, 0), (100.0, 200.0, 1), (200.0, 300.0, 2)]
|
|
anim = TextureChangeAnimation(duration=300.0, textures=textures)
|
|
|
|
self.assertEqual(anim.textures, textures)
|
|
self.assertEqual(anim.attribute, 0) # First texture index
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_texture_change_progression(self, mock_get_ms):
|
|
"""Test texture changes at correct times."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
textures = [(0.0, 100.0, 0), (100.0, 200.0, 1), (200.0, 300.0, 2)]
|
|
anim = TextureChangeAnimation(duration=300.0, textures=textures)
|
|
anim.start()
|
|
|
|
anim.update(50.0)
|
|
self.assertEqual(anim.attribute, 0)
|
|
|
|
anim.update(150.0)
|
|
self.assertEqual(anim.attribute, 1)
|
|
|
|
anim.update(250.0)
|
|
self.assertEqual(anim.attribute, 2)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_texture_change_completion(self, mock_get_ms):
|
|
"""Test texture change completes."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
textures = [(0.0, 100.0, 0), (100.0, 200.0, 1)]
|
|
anim = TextureChangeAnimation(duration=200.0, textures=textures)
|
|
anim.start()
|
|
|
|
anim.update(300.0) # Past duration
|
|
|
|
self.assertTrue(anim.is_finished)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_texture_change_with_delay(self, mock_get_ms):
|
|
"""Test texture change respects delay."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
textures = [(0.0, 100.0, 0), (100.0, 200.0, 1)]
|
|
anim = TextureChangeAnimation(duration=200.0, textures=textures, delay=100.0)
|
|
anim.start()
|
|
|
|
anim.update(50.0) # During delay
|
|
self.assertEqual(anim.attribute, 0)
|
|
|
|
anim.update(150.0) # 50ms into animation (after delay)
|
|
self.assertEqual(anim.attribute, 0)
|
|
|
|
|
|
class TestTextureResizeAnimation(unittest.TestCase):
|
|
"""Test cases for the TextureResizeAnimation class."""
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_initialization(self, mock_get_ms):
|
|
"""Test texture resize initialization."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = TextureResizeAnimation(
|
|
duration=1000.0,
|
|
initial_size=1.0,
|
|
final_size=2.0
|
|
)
|
|
|
|
self.assertEqual(anim.initial_size, 1.0)
|
|
self.assertEqual(anim.final_size, 2.0)
|
|
self.assertEqual(anim.attribute, 1.0)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_resize_progression(self, mock_get_ms):
|
|
"""Test resize progresses correctly."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = TextureResizeAnimation(duration=1000.0, initial_size=1.0, final_size=2.0)
|
|
anim.start()
|
|
|
|
anim.update(500.0) # Halfway
|
|
|
|
self.assertAlmostEqual(anim.attribute, 1.5, places=2)
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_resize_completion(self, mock_get_ms):
|
|
"""Test resize completes."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = TextureResizeAnimation(duration=1000.0, initial_size=1.0, final_size=0.5)
|
|
anim.start()
|
|
|
|
anim.update(1000.0)
|
|
|
|
self.assertEqual(anim.attribute, 0.5)
|
|
self.assertTrue(anim.is_finished)
|
|
|
|
|
|
class TestTextStretchAnimation(unittest.TestCase):
|
|
"""Test cases for the TextStretchAnimation class."""
|
|
|
|
@patch('libs.animation.get_current_ms')
|
|
def test_stretch_phases(self, mock_get_ms):
|
|
"""Test text stretch animation phases."""
|
|
mock_get_ms.return_value = 0.0
|
|
|
|
anim = TextStretchAnimation(duration=100.0)
|
|
anim.start()
|
|
|
|
# Phase 1: Growing
|
|
anim.update(50.0)
|
|
self.assertGreater(anim.attribute, 2)
|
|
|
|
# Phase 2: Shrinking back
|
|
anim.update(150.0)
|
|
self.assertGreater(anim.attribute, 0)
|
|
|
|
# Phase 3: Finished
|
|
anim.update(300.0)
|
|
self.assertEqual(anim.attribute, 0)
|
|
self.assertTrue(anim.is_finished)
|
|
|
|
|
|
class TestAnimationFactory(unittest.TestCase):
|
|
"""Test cases for the Animation factory class."""
|
|
|
|
def test_create_fade(self):
|
|
"""Test factory creates fade animation."""
|
|
anim = Animation.create_fade(1000.0, initial_opacity=1.0, final_opacity=0.0)
|
|
|
|
self.assertIsInstance(anim, FadeAnimation)
|
|
self.assertEqual(anim.duration, 1000.0)
|
|
self.assertEqual(anim.initial_opacity, 1.0)
|
|
|
|
def test_create_move(self):
|
|
"""Test factory creates move animation."""
|
|
anim = Animation.create_move(1000.0, start_position=0, total_distance=100)
|
|
|
|
self.assertIsInstance(anim, MoveAnimation)
|
|
self.assertEqual(anim.duration, 1000.0)
|
|
self.assertEqual(anim.total_distance, 100)
|
|
|
|
def test_create_texture_change(self):
|
|
"""Test factory creates texture change animation."""
|
|
textures = [(0.0, 100.0, 0)]
|
|
anim = Animation.create_texture_change(1000.0, textures=textures)
|
|
|
|
self.assertIsInstance(anim, TextureChangeAnimation)
|
|
self.assertEqual(anim.textures, textures)
|
|
|
|
def test_create_texture_resize(self):
|
|
"""Test factory creates texture resize animation."""
|
|
anim = Animation.create_texture_resize(1000.0, initial_size=1.0, final_size=2.0)
|
|
|
|
self.assertIsInstance(anim, TextureResizeAnimation)
|
|
self.assertEqual(anim.initial_size, 1.0)
|
|
|
|
|
|
class TestParseAnimations(unittest.TestCase):
|
|
"""Test cases for parse_animations function."""
|
|
|
|
def test_parse_basic_animation(self):
|
|
"""Test parsing a simple animation."""
|
|
animation_json = [
|
|
{
|
|
"id": 1,
|
|
"type": "fade",
|
|
"duration": 1000.0,
|
|
"initial_opacity": 1.0,
|
|
"final_opacity": 0.0
|
|
}
|
|
]
|
|
|
|
result = parse_animations(animation_json)
|
|
|
|
self.assertIn(1, result)
|
|
self.assertIsInstance(result[1], FadeAnimation)
|
|
self.assertEqual(result[1].duration, 1000.0)
|
|
|
|
def test_parse_multiple_animations(self):
|
|
"""Test parsing multiple animations."""
|
|
animation_json = [
|
|
{"id": 1, "type": "fade", "duration": 1000.0},
|
|
{"id": 2, "type": "move", "duration": 500.0, "total_distance": 50}
|
|
]
|
|
|
|
result = parse_animations(animation_json)
|
|
|
|
self.assertEqual(len(result), 2)
|
|
self.assertIsInstance(result[1], FadeAnimation)
|
|
self.assertIsInstance(result[2], MoveAnimation)
|
|
|
|
def test_parse_with_reference(self):
|
|
"""Test parsing animations with references."""
|
|
animation_json = [
|
|
{"id": 1, "type": "fade", "duration": 1000.0, "initial_opacity": 1.0},
|
|
{
|
|
"id": 2,
|
|
"type": "fade",
|
|
"duration": {"reference_id": 1, "property": "duration"},
|
|
"initial_opacity": 0.5
|
|
}
|
|
]
|
|
|
|
result = parse_animations(animation_json)
|
|
|
|
self.assertEqual(result[2].duration, 1000.0)
|
|
self.assertEqual(result[2].initial_opacity, 0.5)
|
|
|
|
def test_parse_with_reference_and_init_val(self):
|
|
"""Test parsing with reference and init_val modifier."""
|
|
animation_json = [
|
|
{"id": 1, "type": "fade", "duration": 1000.0},
|
|
{
|
|
"id": 2,
|
|
"type": "fade",
|
|
"duration": {
|
|
"reference_id": 1,
|
|
"property": "duration",
|
|
"init_val": 500.0
|
|
}
|
|
}
|
|
]
|
|
|
|
result = parse_animations(animation_json)
|
|
|
|
self.assertEqual(result[2].duration, 1500.0)
|
|
|
|
def test_parse_missing_id_raises_error(self):
|
|
"""Test that missing id raises exception."""
|
|
animation_json = [
|
|
{"type": "fade", "duration": 1000.0}
|
|
]
|
|
|
|
with self.assertRaises(Exception) as context:
|
|
parse_animations(animation_json)
|
|
self.assertIn("requires id", str(context.exception))
|
|
|
|
def test_parse_missing_type_raises_error(self):
|
|
"""Test that missing type raises exception."""
|
|
animation_json = [
|
|
{"id": 1, "duration": 1000.0}
|
|
]
|
|
|
|
with self.assertRaises(Exception) as context:
|
|
parse_animations(animation_json)
|
|
self.assertIn("requires type", str(context.exception))
|
|
|
|
def test_parse_circular_reference_raises_error(self):
|
|
"""Test that circular references are detected."""
|
|
animation_json = [
|
|
{
|
|
"id": 1,
|
|
"type": "fade",
|
|
"duration": {"reference_id": 2, "property": "duration"}
|
|
},
|
|
{
|
|
"id": 2,
|
|
"type": "fade",
|
|
"duration": {"reference_id": 1, "property": "duration"}
|
|
}
|
|
]
|
|
|
|
with self.assertRaises(Exception) as context:
|
|
parse_animations(animation_json)
|
|
self.assertIn("Circular reference", str(context.exception))
|
|
|
|
def test_parse_unknown_type_raises_error(self):
|
|
"""Test that unknown animation type raises exception."""
|
|
animation_json = [
|
|
{"id": 1, "type": "unknown_type", "duration": 1000.0}
|
|
]
|
|
|
|
with self.assertRaises(Exception) as context:
|
|
parse_animations(animation_json)
|
|
self.assertIn("Unknown Animation type", str(context.exception))
|
|
|
|
def test_parse_missing_reference_property_raises_error(self):
|
|
"""Test that missing reference property raises exception."""
|
|
animation_json = [
|
|
{"id": 1, "type": "fade", "duration": 1000.0},
|
|
{
|
|
"id": 2,
|
|
"type": "fade",
|
|
"duration": {"reference_id": 1}
|
|
}
|
|
]
|
|
|
|
with self.assertRaises(Exception) as context:
|
|
parse_animations(animation_json)
|
|
self.assertIn("requires 'property'", str(context.exception))
|
|
|
|
def test_parse_nonexistent_reference_raises_error(self):
|
|
"""Test that referencing nonexistent animation raises exception."""
|
|
animation_json = [
|
|
{
|
|
"id": 1,
|
|
"type": "fade",
|
|
"duration": {"reference_id": 999, "property": "duration"}
|
|
}
|
|
]
|
|
|
|
with self.assertRaises(Exception) as context:
|
|
parse_animations(animation_json)
|
|
self.assertIn("not found", str(context.exception))
|
|
|
|
def test_parse_ignores_comments(self):
|
|
"""Test that comments are ignored during parsing."""
|
|
animation_json = [
|
|
{
|
|
"id": 1,
|
|
"type": "fade",
|
|
"duration": 1000.0,
|
|
"comment": "This is a fade animation"
|
|
}
|
|
]
|
|
|
|
result = parse_animations(animation_json)
|
|
|
|
self.assertIn(1, result)
|
|
self.assertIsInstance(result[1], FadeAnimation)
|
|
|
|
def test_parse_nested_references(self):
|
|
"""Test parsing nested reference chains."""
|
|
animation_json = [
|
|
{"id": 1, "type": "fade", "duration": 1000.0},
|
|
{
|
|
"id": 2,
|
|
"type": "fade",
|
|
"duration": {"reference_id": 1, "property": "duration"}
|
|
},
|
|
{
|
|
"id": 3,
|
|
"type": "fade",
|
|
"duration": {
|
|
"reference_id": 2,
|
|
"property": "duration",
|
|
"init_val": 500.0
|
|
}
|
|
}
|
|
]
|
|
|
|
result = parse_animations(animation_json)
|
|
|
|
self.assertEqual(result[3].duration, 1500.0)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|