Files
product-vamp/tests/test_rhythm_contracts.py
T
khodowany 82349cf897
ci / checks (push) Failing after 23m37s
Playback
2026-10-05 23:41:11 -05:00

70 lines
4.4 KiB
Python

import dataclasses, unittest
import fractions
import generator.solo.types
import generator.solo.harmonic
import generator.solo.groups
import tests.solo_fixtures
import tests.test_independent_generation
import tempfile
import pathlib
import generator.input_audit
def melody(rows): return tuple(tests.solo_fixtures.note(a, b, p, event_id=str(i)) for i, (a, b, p) in enumerate(rows))
class RhythmGroups(unittest.TestCase):
def test_complete_triplet_can_be_displaced_without_offsetting_binary_tail(self):
pattern = ((fractions.Fraction(1, 2), fractions.Fraction(5, 6)), (fractions.Fraction(5, 6), fractions.Fraction(7, 6)), (fractions.Fraction(7, 6), fractions.Fraction(3, 2)), (fractions.Fraction(3, 2), fractions.Fraction(5, 2)))
result = generator.solo.groups.describe(pattern)
self.assertEqual(result[0].family, 'triplet')
self.assertEqual(result[-1].family, 'binary')
self.assertEqual(result[0].displacement, fractions.Fraction(1, 2))
def test_partial_triplet_does_not_drift_into_binary(self):
self.assertFalse(generator.solo.groups.valid(((fractions.Fraction(), fractions.Fraction(1, 3)), (fractions.Fraction(1, 3), fractions.Fraction(5, 6)))))
self.assertFalse(generator.solo.groups.valid(((fractions.Fraction(1, 3), fractions.Fraction(4, 3)),)))
self.assertEqual(generator.solo.groups.describe(((fractions.Fraction(1, 3), fractions.Fraction(4, 3)),), imported=True)[0].family, 'source')
def test_relative_rhythm_identity_does_not_count_absolute_onsets(self):
original = melody(((0, 1, 'c'), (1, 2, 'e'), (2, 3, 'd')))
self.assertEqual(len(set(generator.solo.groups.signature(original))), 1)
moved = tuple((dataclasses.replace(n, start=n.start + 13, end=n.end + 13) for n in original))
self.assertEqual(generator.solo.groups.signature(original), generator.solo.groups.signature(moved))
def test_broadening_never_cuts_inside_tuplet(self):
pattern = ((fractions.Fraction(), fractions.Fraction(1, 3)), (fractions.Fraction(1, 3), fractions.Fraction(2, 3)), (fractions.Fraction(2, 3), fractions.Fraction(1)))
self.assertEqual(generator.solo.groups.broaden(pattern, fractions.Fraction(1, 2), fractions.Fraction(4)), ())
class PendingTreatments(unittest.TestCase):
def test_complete_route_can_cross_a_search_frontier(self):
fixture = tests.test_independent_generation.TypedExpressionContracts()
fixture.setUp()
notes, contract = fixture.make()
partial = fixture.h.evaluate(notes[:2], until=fractions.Fraction(2), complete=False, expressions=(contract,))
self.assertTrue(partial.valid, partial.error)
self.assertEqual(partial.pending, (contract,))
self.assertFalse(fixture.h.evaluate(notes[:2], until=fractions.Fraction(3), expressions=(contract,)).valid)
complete = fixture.h.evaluate(notes, until=fractions.Fraction(4), complete=True, expressions=(contract,))
self.assertTrue(complete.valid)
self.assertFalse(complete.pending)
class InputFirewall(unittest.TestCase):
def test_read_write_handles_cannot_bypass_boundary(self):
with tempfile.TemporaryDirectory() as folder:
forbidden = pathlib.Path(folder) / 'target.xml'
forbidden.write_text('not musical input')
for mode in ('r', 'r+'):
with self.assertRaisesRegex(ValueError, 'Forbidden composition input'):
with generator.input_audit.composition(pathlib.Path('data'), 'electric_solo'):
with forbidden.open(mode) as stream:
stream.read()
class PreparedSuspensions(unittest.TestCase):
def test_preparation_and_delayed_resolution_are_required(self):
h = generator.solo.harmonic.Harmony(tests.solo_fixtures.choices(('Gm7', 'C7')))
held = tests.solo_fixtures.note(fractions.Fraction(5, 2), fractions.Fraction(9, 2), 'f', event_id='held')
target = tests.solo_fixtures.note(fractions.Fraction(9, 2), 6, 'e', event_id='resolution')
contract = generator.solo.types.ExpressionContract('prepared', 'suspension', 'prepared_step_down', (held,), target, 'C7', boundary=fractions.Fraction(4))
self.assertTrue(h.evaluate((held, target), expressions=(contract,)).valid)
self.assertTrue(h.evaluate((held, target)).valid)
self.assertFalse(h.evaluate((held, target), infer=False).valid)
changed = dataclasses.replace(held, start=fractions.Fraction(15, 4))
self.assertFalse(h.evaluate((changed, target), expressions=(dataclasses.replace(contract, events=(changed,)),)).valid)
self.assertFalse(h.evaluate((held,), expressions=(contract,)).valid)