Cleanup
ci / checks (push) Failing after 23m22s

This commit is contained in:
khodowany
2026-10-08 02:13:45 -05:00
parent 300097a37e
commit bc7baea7c4
88 changed files with 18844 additions and 1335 deletions
+27 -27
View File
@@ -1,4 +1,9 @@
"""Dependency and music contracts use synthetic sources, never audition scores."""
import generator.composition_types
import generator.arrange.assembly
import generator.composition
import generator.source
import generator.validation
import concurrent.futures
import copy
import contextlib
@@ -13,15 +18,10 @@ import unittest.mock
import generator.production
import generator.model
import generator.vocabulary
import generator.solo.expressions
import generator.solo.types
import generator.solo.harmonic
import generator.solo.placement
import generator.solo.notation
import generator.solo.composer
import tests.solo_fixtures
import tests.solo_fixtures
import tests.solo_fixtures
def music(result):
@@ -30,23 +30,23 @@ def music(result):
class IndependentRoles(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.source = generator.production.SourceAnalysis.read(tests.solo_fixtures.song(count=1, chords=('CM7',)))
cls.source = generator.source.SourceAnalysis.read(tests.solo_fixtures.song(count=1, chords=('CM7',)))
cls.root = pathlib.Path('data').resolve()
def test_alone_order_parallel_and_assembly(self):
serial = {role: generator.production.generate_role(self.source, role, self.root) for role in generator.production.ROLES}
serial = {role: generator.composition.generate_role(self.source, role, self.root) for role in generator.composition_types.ROLES}
with concurrent.futures.ProcessPoolExecutor(max_workers=2, mp_context=multiprocessing.get_context('spawn')) as pool:
jobs = {role: pool.submit(generator.production._job, (self.source, role, self.root, {}, None)) for role in reversed(generator.production.ROLES)}
jobs = {role: pool.submit(generator.production._job, generator.composition_types.RoleRequest(self.source, role, self.root)) for role in reversed(generator.composition_types.ROLES)}
parallel = {role: future.result() for role, future in jobs.items()}
for role in generator.production.ROLES:
for role in generator.composition_types.ROLES:
self.assertEqual(music(serial[role]), music(parallel[role]), role)
for doc in generator.production.assemble(self.source, serial).values():
generator.model.validate_document(doc)
for doc in generator.arrange.assembly.assemble(self.source, serial).values():
generator.validation.validate_document(doc)
def test_role_setting_and_cache_are_independent(self):
with tempfile.TemporaryDirectory() as directory:
a = generator.production.generate_role(self.source, 'acoustic_solo', self.root, cache_dir=directory)
b = generator.production.generate_role(self.source, 'electric_solo', self.root, {'seed': 'changed'}, directory)
again = generator.production.generate_role(self.source, 'acoustic_solo', self.root, cache_dir=directory)
clean = generator.production.generate_role(self.source, 'acoustic_solo', self.root)
a = generator.composition.generate_role(self.source, 'acoustic_solo', self.root, cache_dir=directory)
b = generator.composition.generate_role(self.source, 'electric_solo', self.root, {'seed': 'changed'}, directory)
again = generator.composition.generate_role(self.source, 'acoustic_solo', self.root, cache_dir=directory)
clean = generator.composition.generate_role(self.source, 'acoustic_solo', self.root)
self.assertTrue(again['cache_hit'])
self.assertEqual(music(a), music(again))
self.assertEqual(music(a), music(clean))
@@ -60,7 +60,7 @@ class IndependentRoles(unittest.TestCase):
self.assertNotIn('generated_assets', str(path))
return original(path, *args, **kwargs)
with unittest.mock.patch.object(pathlib.Path, 'open', guarded):
result = generator.production.generate_role(self.source, 'acoustic_solo', self.root)
result = generator.composition.generate_role(self.source, 'acoustic_solo', self.root)
self.assertNotIn('reference', json.dumps(result['trace']))
def test_titles_and_export_ids_do_not_supply_musical_identity(self):
document = tests.solo_fixtures.song(count=1, chords=('CM7',))
@@ -69,7 +69,7 @@ class IndependentRoles(unittest.TestCase):
for m in other['staves'][0]['measures']:
m['id'] = 'exporter_123'
m['number'] = '99'
self.assertEqual(generator.production.SourceAnalysis.read(document), generator.production.SourceAnalysis.read(other))
self.assertEqual(generator.source.SourceAnalysis.read(document), generator.source.SourceAnalysis.read(other))
def test_reference_presence_and_changes_do_not_change_clean_music(self):
# No repository reference is a fixture or prerequisite. These deliberately
# invalid optional files test the absence of a discovery/fallback branch.
@@ -78,16 +78,16 @@ class IndependentRoles(unittest.TestCase):
reference_dir.mkdir()
references = [reference_dir / (source_name + '-solo-' + str(index) + suffix + '.xml')
for source_name in ('all-of-me', 'autumn-leaves') for index in (1, 2) for suffix in ('', '(4)')]
before = generator.production.generate_role(self.source, 'electric_solo', self.root)
before = generator.composition.generate_role(self.source, 'electric_solo', self.root)
for reference in references:
reference.write_text('<not-a-production-source/>')
present = generator.production.generate_role(self.source, 'electric_solo', self.root)
present = generator.composition.generate_role(self.source, 'electric_solo', self.root)
for reference in references:
reference.write_text('changed optional target, still not MusicXML')
changed = generator.production.generate_role(self.source, 'electric_solo', self.root)
changed = generator.composition.generate_role(self.source, 'electric_solo', self.root)
for reference in references:
reference.unlink()
absent = generator.production.generate_role(self.source, 'electric_solo', self.root)
absent = generator.composition.generate_role(self.source, 'electric_solo', self.root)
self.assertTrue(all(music(before) == music(v) for v in (present, changed, absent)))
def test_original_melody_changes_the_theme_with_unchanged_harmony(self):
source = tests.solo_fixtures.song(count=4, chords=('CM7',))
@@ -97,11 +97,11 @@ class IndependentRoles(unittest.TestCase):
for n in v['notes']:
for p in n['pitches']:
p['letter'] = {'c': 'e', 'd': 'f', 'e': 'g', 'f': 'a'}[p['letter']]
a = generator.production.generate_role(generator.production.SourceAnalysis.read(source), 'acoustic_solo', self.root)
b = generator.production.generate_role(generator.production.SourceAnalysis.read(changed), 'acoustic_solo', self.root)
a = generator.composition.generate_role(generator.source.SourceAnalysis.read(source), 'acoustic_solo', self.root)
b = generator.composition.generate_role(generator.source.SourceAnalysis.read(changed), 'acoustic_solo', self.root)
self.assertNotEqual(a['details']['phrases'][0]['notes'], b['details']['phrases'][0]['notes'])
def test_source_tuplet_lanes_survive_assembly(self):
serial = {r: generator.production.generate_role(self.source, r, self.root) for r in generator.production.ROLES}
serial = {r: generator.composition.generate_role(self.source, r, self.root) for r in generator.composition_types.ROLES}
# Exercise a different original lane name, as in the imported Autumn head.
doc = serial['electric_melody']['document']
staff = doc['staves'][0]
@@ -117,10 +117,10 @@ class IndependentRoles(unittest.TestCase):
n['voice'] = 'voice_source'
for t in staff['tuplets']:
t['voice'] = 'voice_source'
generator.model.validate_document(doc)
generator.model.validate_document(generator.production.assemble(self.source, serial)['electric'])
generator.validation.validate_document(doc)
generator.validation.validate_document(generator.arrange.assembly.assemble(self.source, serial)['electric'])
def test_sparse_parts_reuse_the_canonical_form(self):
result = generator.production.generate_role(generator.production.SourceAnalysis.read(tests.solo_fixtures.song(count=4, chords=('Dm7', 'G7', 'CM7'))), 'acoustic_accompaniment', self.root)
result = generator.composition.generate_role(generator.source.SourceAnalysis.read(tests.solo_fixtures.song(count=4, chords=('Dm7', 'G7', 'CM7'))), 'acoustic_accompaniment', self.root)
measures = result['document']['staves'][0]['measures']
def signature(m):
return [(n['onset'], n['duration'], [(generator.model.pitch_number(p), p['tablature']) for p in n['pitches']]) for v in m['voices'] for n in v['notes']]