70 lines
4.4 KiB
Python
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)
|