Files
reaper-control/tests/test_preset_transform.py
renato97 014e636889 feat: professional reggaeton production engine — 7 SDD changes, 302 tests
- section-energy: track activity matrix + volume/velocity multipliers per section
- smart-chords: ChordEngine with voice leading, inversions, 4 emotion modes
- hook-melody: melody engine with hook/stabs/smooth styles, call-and-response
- mix-calibration: Calibrator module (LUFS volumes, HPF/LPF, stereo, sends, master)
- transitions-fx: FX track with risers/impacts/sweeps at section boundaries
- sidechain: MIDI CC11 bass ducking on kick hits via DrumLoopAnalyzer
- presets-pack: role-aware plugin presets (Serum/Decapitator/Omnisphere per role)

Full SDD pipeline (propose→spec→design→tasks→apply→verify) for all 7 changes.
302/302 tests passing.
2026-05-03 23:54:29 -03:00

195 lines
8.0 KiB
Python

"""Tests for role-aware preset system (presets-pack).
Covers PresetTransformer, role-aware PLUGIN_PRESETS lookup,
and thread-through via make_plugin → PluginDef.role → _build_plugin.
"""
from __future__ import annotations
import base64
import json
import pytest
from src.reaper_builder.preset_transformer import PresetTransformer
from src.reaper_builder import PLUGIN_PRESETS, PLUGIN_REGISTRY
# ---------------------------------------------------------------------------
# PresetTransformer unit tests
# ---------------------------------------------------------------------------
class TestPresetTransformer:
"""PresetTransformer derives role-specific preset data."""
def test_derive_serum_returns_list(self):
"""derive() for Serum_2 returns a list of chunks."""
default = PLUGIN_PRESETS.get(("Serum_2", ""))
assert default is not None, "Serum_2 default preset must exist"
result = PresetTransformer.derive("Serum_2", default, "bass")
assert isinstance(result, list)
assert len(result) == len(default)
assert all(isinstance(c, str) for c in result)
def test_derive_decapitator_returns_list(self):
"""derive() for Decapitator returns a list of chunks."""
default = PLUGIN_PRESETS.get(("Decapitator", ""))
assert default is not None, "Decapitator default preset must exist"
result = PresetTransformer.derive("Decapitator", default, "drums")
assert isinstance(result, list)
assert len(result) == len(default)
def test_derive_omnisphere_returns_list(self):
"""derive() for Omnisphere returns a list of chunks."""
default = PLUGIN_PRESETS.get(("Omnisphere", ""))
assert default is not None, "Omnisphere default preset must exist"
result = PresetTransformer.derive("Omnisphere", default, "pad")
assert isinstance(result, list)
assert len(result) == len(default)
def test_derive_unknown_plugin_returns_default(self):
"""derive() for unsupported plugin returns original chunks."""
chunks = ["mock_chunk_1", "mock_chunk_2"]
result = PresetTransformer.derive("NonexistentPlugin", chunks, "lead")
assert result == chunks
def test_derive_preserves_chunk_count(self):
"""derive() output has same number of chunks as input."""
for plugin in ["Serum_2", "Decapitator", "Omnisphere"]:
default = PLUGIN_PRESETS.get((plugin, ""))
if not default:
continue
for role in ["bass", "lead", "drums", "pad"]:
result = PresetTransformer.derive(plugin, default, role)
assert len(result) == len(default), (
f"{plugin}/{role} chunk count mismatch: "
f"got {len(result)}, expected {len(default)}"
)
# ---------------------------------------------------------------------------
# Role-aware preset structure tests
# ---------------------------------------------------------------------------
class TestRoleAwarePresets:
"""PLUGIN_PRESETS is structured as {(plugin, role): chunks}."""
def test_default_role_entries_exist(self):
"""All known plugins have a "" (default) role entry."""
flat_keys = set()
for (name, role), _ in PLUGIN_PRESETS.items():
if role == "":
flat_keys.add(name)
# At minimum, the multi-role targets must have defaults
for name in ["Serum_2", "Decapitator", "Omnisphere"]:
assert name in flat_keys, f"{name} must have default role entry"
def test_role_specific_entries_exist(self):
"""Multi-role plugins have their role-specific entries."""
roles = {
"Serum_2": ["bass", "lead"],
"Decapitator": ["drumloop", "bass", "clap", "perc"],
"Omnisphere": ["chords", "pad"],
}
for plugin, expected_roles in roles.items():
for role in expected_roles:
assert (plugin, role) in PLUGIN_PRESETS, (
f"Missing ({plugin}, {role}) in PLUGIN_PRESETS"
)
def test_role_entries_non_empty(self):
"""Role-specific entries contain non-empty preset data."""
for (name, role), chunks in PLUGIN_PRESETS.items():
if role != "" and name in ("Serum_2", "Decapitator", "Omnisphere"):
assert len(chunks) > 0, (
f"({name}, {role}) has empty preset data"
)
def test_unknown_role_falls_back_to_default(self):
"""Role not present in PLUGIN_PRESETS → fall back to default."""
# Simulate: a plugin has only default entry, no "pad" role
# The lookup should return the "" entry
from src.reaper_builder import _resolve_preset
# Gullfoss_Master doesn't have multi-role entries, only ""
result = _resolve_preset("Gullfoss_Master", "pad")
default = PLUGIN_PRESETS.get(("Gullfoss_Master", ""))
assert result == default, (
"Unknown role should fall back to default preset"
)
# ---------------------------------------------------------------------------
# Integration: make_plugin + role threading
# ---------------------------------------------------------------------------
class TestMakePluginRoleThreading:
"""make_plugin with role correctly sets PluginDef.role and preset_data."""
def test_make_plugin_with_role_sets_role_field(self):
"""make_plugin(key, idx, role=...) sets PluginDef.role."""
from scripts.compose import make_plugin
p = make_plugin("Serum_2", 0, role="bass")
assert p.role == "bass"
assert p.preset_data is not None
def test_make_plugin_without_role_defaults_to_empty(self):
"""make_plugin(key, idx) without role sets role="" (backward compat)."""
from scripts.compose import make_plugin
p = make_plugin("Decapitator", 0)
assert p.role == ""
def test_make_plugin_different_roles_lookup_correct_entries(self):
"""Different roles resolve to their respective PLUGIN_PRESETS entries."""
from scripts.compose import make_plugin
# Both should return data — in MVP they're identical but structure is correct
p_bass = make_plugin("Serum_2", 0, role="bass")
p_lead = make_plugin("Serum_2", 0, role="lead")
# Both should have non-None preset data
assert p_bass.preset_data is not None, "bass role should have preset_data"
assert p_lead.preset_data is not None, "lead role should have preset_data"
# Both should be lists with content
assert isinstance(p_bass.preset_data, list)
assert isinstance(p_lead.preset_data, list)
assert len(p_bass.preset_data) > 0
assert len(p_lead.preset_data) > 0
def test_make_plugin_unknown_role_falls_back(self):
"""Unknown role returns preset from "" entry (if available)."""
from scripts.compose import make_plugin
# "pad" role is not valid for Serum_2 (Omnisphere handles pad)
p = make_plugin("Serum_2", 0, role="pad")
# Should still get the default Serum_2 preset
assert p.preset_data is not None
assert p.role == "pad"
# ---------------------------------------------------------------------------
# Backward compatibility
# ---------------------------------------------------------------------------
class TestBackwardCompatibility:
"""Existing behavior preserved with no role."""
def test_make_plugin_known_key_still_works(self):
"""make_plugin with known registry key (no role) works as before."""
from scripts.compose import make_plugin
p = make_plugin("Decapitator", 0)
assert p.name == "Decapitator"
assert p.index == 0
assert p.role == ""
def test_make_plugin_unknown_key_still_works(self):
"""make_plugin with unknown key (no role) returns PluginDef."""
from scripts.compose import make_plugin
p = make_plugin("NonExistent", 2)
assert p.name == "NonExistent"
assert p.index == 2
assert p.role == ""