""" test_human_feel.py - Tests para HumanFeelEngine T101-T103: Unit tests """ import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import unittest from human_feel import HumanFeelEngine class TestHumanFeelEngine(unittest.TestCase): """Tests para HumanFeelEngine""" def setUp(self): self.engine = HumanFeelEngine(seed=42) def test_timing_variation_range(self): """T040: Timing variation dentro de rango ±5ms.""" notes = [{'pitch': 60, 'start': 0.0, 'velocity': 100}] result = self.engine.apply_timing_variation(notes, amount_ms=5.0) for note in result: offset_ms = (note['start'] - 0.0) * 1000 self.assertGreaterEqual(offset_ms, -5.0) self.assertLessEqual(offset_ms, 5.0) def test_velocity_humanize_variance(self): """T041: Velocity variation ±5%.""" notes = [{'pitch': 60, 'start': 0.0, 'velocity': 100}] result = self.engine.apply_velocity_humanize(notes, variance=0.05) for note in result: # Velocity debe estar en rango 95-105 self.assertGreaterEqual(note['velocity'], 95) self.assertLessEqual(note['velocity'], 105) def test_note_skip_probability(self): """T042: Probabilidad de skip ~2%.""" notes = [{'pitch': 60, 'start': float(i), 'velocity': 100} for i in range(100)] result = self.engine.apply_note_skip_probability(notes, prob=0.02) # Con seed=42, debe mantener aprox 98% de notas self.assertGreater(len(result), 90) # No muy estricto por randomness self.assertLess(len(result), 100) def test_section_dynamics_scale(self): """T047-T050: Dinámica por sección.""" notes = [{'pitch': 60, 'start': 0.0, 'velocity': 100}] # Intro = 70% intro_notes = self.engine.apply_section_dynamics(notes, 'intro') self.assertEqual(intro_notes[0]['velocity'], 70) # Drop = 100% drop_notes = self.engine.apply_section_dynamics(notes, 'drop') self.assertEqual(drop_notes[0]['velocity'], 100) # Build = 85% build_notes = self.engine.apply_section_dynamics(notes, 'build') self.assertEqual(build_notes[0]['velocity'], 85) def test_groove_applies_to_offbeat(self): """T044-T046: Groove aplica a notas off-beat.""" # Nota en off-beat (beat position 0.5) notes = [{'pitch': 60, 'start': 4.5, 'velocity': 100}] result = self.engine.apply_groove(notes, style='shuffle', amount=1.0) # Debe tener delay aplicado self.assertGreater(result[0]['start'], 4.5) if __name__ == '__main__': unittest.main()