Files
product-vamp/tests/test_generator_boundaries.py
khodowany bc7baea7c4
ci / checks (push) Failing after 23m22s
Cleanup
2026-10-08 02:13:45 -05:00

65 lines
3.2 KiB
Python

"""Dependency direction, cache coverage and explicit composition inputs."""
import ast
import json
import pathlib
import subprocess
import sys
import unittest
import unittest.mock
import generator.cache
import generator.composition
import generator.role_cache
import generator.source
import generator.vocabulary
import tests.solo_fixtures
ROOT = pathlib.Path(__file__).resolve().parents[1]
class GeneratorBoundaries(unittest.TestCase):
def test_generator_imports_are_acyclic(self):
files = {'.'.join(path.relative_to(ROOT).with_suffix('').parts): path for path in (ROOT / 'generator').rglob('*.py')}
graph = {name: set(generator.cache.imports(path.read_bytes())) & files.keys() for name, path in files.items()}
visited, active = set(), []
def visit(name):
self.assertNotIn(name, active, ' -> '.join([*active, name]))
if name in visited:
return
active.append(name)
for dependency in graph[name]:
visit(dependency)
active.pop()
visited.add(name)
for name in graph:
visit(name)
def test_composition_import_does_not_load_rendering_or_publication(self):
code = "import generator.composition, sys, json; print(json.dumps(sorted(sys.modules)))"
result = subprocess.run([sys.executable, '-c', code], cwd=ROOT, capture_output=True, text=True, check=True)
forbidden = ('PIL', 'fontTools', 'generator.engraving', 'generator.production', 'generator.score_io')
self.assertFalse([name for name in json.loads(result.stdout) if name.startswith(forbidden)])
def test_all_previously_missing_role_dependencies_change_the_fingerprint(self):
baseline = generator.role_cache.code_key()
read = pathlib.Path.read_bytes
for name in ('solo/policy.py', 'source.py', 'accompaniment.py'):
changed = ROOT / 'generator' / name
with self.subTest(dependency=name):
def contents(path):
return read(path) + (b'\n# changed implementation\n' if path == changed else b'')
with unittest.mock.patch.object(pathlib.Path, 'read_bytes', contents):
self.assertNotEqual(generator.role_cache.code_key(), baseline)
def test_context_owns_immutable_resources_and_survives_caller_mutation(self):
resources = generator.vocabulary.load()
context = generator.vocabulary.CompositionContext.create(resources, 'electric_accompaniment')
expected = context.role()['center']
resources['roles']['roles']['electric_accompaniment']['center'] = 999
self.assertEqual(context.role()['center'], expected)
with self.assertRaises(TypeError):
context.data('roles')['roles']['electric_accompaniment']['center'] = 999
self.assertNotEqual(generator.vocabulary.load()['roles']['roles']['electric_accompaniment']['center'], 999)
def test_role_generation_does_not_consult_ambient_policy(self):
source = generator.source.SourceAnalysis.read(tests.solo_fixtures.song(count=1, chords=('CM7',)))
with unittest.mock.patch('generator.vocabulary.current', side_effect=AssertionError('implicit policy dependency')):
for role in generator.composition_types.ROLES:
with self.subTest(role=role):
generator.composition.generate_role(source, role, ROOT / 'data', {'predict': True})