Add tests for src module (Pitch, Chord, HarmonicSpace)
This commit is contained in:
parent
8a03269298
commit
4ae83f857b
|
|
@ -1,151 +1,203 @@
|
|||
import pytest
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
sys.path.insert(0, "src")
|
||||
|
||||
import compact_sets_optimized_2 as cs
|
||||
from src.pitch import Pitch, DIMS_4, DIMS_7
|
||||
from src.chord import Chord
|
||||
from src.harmonic_space import HarmonicSpace
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_dims():
|
||||
cs.dims = cs.DIMS_8
|
||||
yield
|
||||
cs.dims = cs.DIMS_8
|
||||
class TestPitch:
|
||||
"""Tests for Pitch class."""
|
||||
|
||||
def test_fundamental(self):
|
||||
p = Pitch((0, 0, 0, 0), DIMS_4)
|
||||
assert p.to_fraction() == 1
|
||||
|
||||
class TestPitchConversion:
|
||||
def test_hs_array_to_fr_fundamental(self):
|
||||
root = (0, 0, 0, 0, 0, 0, 0, 0)
|
||||
assert cs.hs_array_to_fr(root) == 1.0
|
||||
def test_octave(self):
|
||||
p = Pitch((1, 0, 0, 0), DIMS_4)
|
||||
assert p.to_fraction() == 2
|
||||
|
||||
def test_hs_array_to_fr_octave(self):
|
||||
octave = (1, 0, 0, 0, 0, 0, 0, 0)
|
||||
assert cs.hs_array_to_fr(octave) == 2.0
|
||||
def test_fifth(self):
|
||||
p = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
assert p.to_fraction() == 3
|
||||
|
||||
def test_hs_array_to_fr_fifth(self):
|
||||
fifth = (0, 1, 0, 0, 0, 0, 0, 0)
|
||||
assert cs.hs_array_to_fr(fifth) == 3.0
|
||||
def test_compound(self):
|
||||
p = Pitch((1, 1, 0, 0), DIMS_4)
|
||||
assert p.to_fraction() == 6
|
||||
|
||||
def test_hs_array_to_fr_compound(self):
|
||||
pitch = (1, 1, 0, 0, 0, 0, 0, 0)
|
||||
assert cs.hs_array_to_fr(pitch) == 6.0
|
||||
def test_cents_fundamental(self):
|
||||
p = Pitch((0, 0, 0, 0), DIMS_4)
|
||||
assert p.to_cents() == 0.0
|
||||
|
||||
def test_hs_array_to_cents_fundamental(self):
|
||||
root = (0, 0, 0, 0, 0, 0, 0, 0)
|
||||
assert cs.hs_array_to_cents(root) == 0.0
|
||||
def test_cents_octave(self):
|
||||
p = Pitch((1, 0, 0, 0), DIMS_4)
|
||||
assert p.to_cents() == 1200.0
|
||||
|
||||
def test_hs_array_to_cents_octave(self):
|
||||
octave = (1, 0, 0, 0, 0, 0, 0, 0)
|
||||
assert cs.hs_array_to_cents(octave) == 1200.0
|
||||
def test_cents_fifth(self):
|
||||
p = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
assert abs(p.to_cents() - 1901.96) < 0.01
|
||||
|
||||
def test_transpose_positive(self):
|
||||
p = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
trans = Pitch((1, 0, 0, 0), DIMS_4)
|
||||
result = p.transpose(trans)
|
||||
assert result.hs_array == (1, 1, 0, 0)
|
||||
|
||||
class TestPitchManipulation:
|
||||
def test_transpose_pitch(self):
|
||||
pitch = (0, 1, 0, 0, 0, 0, 0, 0)
|
||||
trans = (1, 0, 0, 0, 0, 0, 0, 0)
|
||||
result = cs.transpose_pitch(pitch, trans)
|
||||
assert result == (1, 1, 0, 0, 0, 0, 0, 0)
|
||||
def test_transpose_negative(self):
|
||||
p = Pitch((1, 1, 0, 0), DIMS_4)
|
||||
trans = Pitch((-1, 0, 0, 0), DIMS_4)
|
||||
result = p.transpose(trans)
|
||||
assert result.hs_array == (0, 1, 0, 0)
|
||||
|
||||
def test_transpose_pitch_negative(self):
|
||||
pitch = (1, 1, 0, 0, 0, 0, 0, 0)
|
||||
trans = (-1, 0, 0, 0, 0, 0, 0, 0)
|
||||
result = cs.transpose_pitch(pitch, trans)
|
||||
assert result == (0, 1, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
def test_transpose_chord(self):
|
||||
chord = ((0, 0, 0, 0, 0, 0, 0, 0), (0, 1, 0, 0, 0, 0, 0, 0))
|
||||
trans = (1, 0, 0, 0, 0, 0, 0, 0)
|
||||
result = cs.transpose_chord(chord, trans)
|
||||
assert result == ((1, 0, 0, 0, 0, 0, 0, 0), (1, 1, 0, 0, 0, 0, 0, 0))
|
||||
|
||||
def test_expand_pitch(self):
|
||||
pitch = (0, 1, 0, 0, 0, 0, 0, 0)
|
||||
result = cs.expand_pitch(pitch)
|
||||
assert isinstance(result, tuple)
|
||||
|
||||
def test_collapse_pitch(self):
|
||||
pitch = (3, 1, 0, 0, 0, 0, 0, 0)
|
||||
result = cs.collapse_pitch(pitch)
|
||||
assert result == (0, 1, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
def test_collapse_chord(self):
|
||||
chord = ((3, 1, 0, 0, 0, 0, 0, 0), (2, 0, 1, 0, 0, 0, 0, 0))
|
||||
result = cs.collapse_chord(chord)
|
||||
assert result == ((0, 1, 0, 0, 0, 0, 0, 0), (0, 0, 1, 0, 0, 0, 0, 0))
|
||||
|
||||
|
||||
class TestPitchDifference:
|
||||
def test_pitch_difference(self):
|
||||
p1 = (0, 1, 0, 0, 0, 0, 0, 0)
|
||||
p2 = (0, 0, 0, 0, 0, 0, 0, 0)
|
||||
result = cs.pitch_difference(p1, p2)
|
||||
assert result == (0, 1, 0, 0, 0, 0, 0, 0)
|
||||
p1 = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
p2 = Pitch((0, 0, 0, 0), DIMS_4)
|
||||
result = p1.pitch_difference(p2)
|
||||
assert result.hs_array == (0, 1, 0, 0)
|
||||
|
||||
def test_cent_difference(self):
|
||||
p1 = (0, 0, 0, 0, 0, 0, 0, 0)
|
||||
p2 = (1, 0, 0, 0, 0, 0, 0, 0)
|
||||
result = cs.cent_difference(p1, p2)
|
||||
assert result == 1200.0
|
||||
def test_collapse(self):
|
||||
p = Pitch((3, 1, 0, 0), DIMS_4)
|
||||
collapsed = p.collapse()
|
||||
# Collapse brings to [1, 2) range: 24 -> 1.5 = 2^-1 * 3
|
||||
assert collapsed.hs_array == (-1, 1, 0, 0)
|
||||
|
||||
def test_collapse_below_one(self):
|
||||
p = Pitch((-2, 0, 0, 0), DIMS_4)
|
||||
collapsed = p.collapse()
|
||||
assert collapsed.hs_array == (0, 0, 0, 0)
|
||||
|
||||
def test_hash(self):
|
||||
p1 = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
p2 = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
assert hash(p1) == hash(p2)
|
||||
|
||||
def test_equality(self):
|
||||
p1 = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
p2 = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
assert p1 == p2
|
||||
|
||||
def test_len(self):
|
||||
p = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
assert len(p) == 4
|
||||
|
||||
|
||||
class TestCompactSets:
|
||||
def test_compact_sets_root(self):
|
||||
root = (0, 0, 0, 0)
|
||||
result = list(cs.compact_sets(root, 1, 1))
|
||||
assert len(result) > 0
|
||||
class TestChord:
|
||||
"""Tests for Chord class."""
|
||||
|
||||
def test_compact_sets_single_element(self):
|
||||
root = (0, 0, 0, 0)
|
||||
result = list(cs.compact_sets(root, 1, 1))
|
||||
assert all(len(chord) == 1 for chord in result)
|
||||
def test_create_chord(self):
|
||||
p1 = Pitch((0, 0, 0, 0), DIMS_4)
|
||||
p2 = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
chord = Chord((p1, p2), DIMS_4)
|
||||
assert len(chord) == 2
|
||||
|
||||
def test_compact_sets_returns_tuples(self):
|
||||
root = (0, 0, 0, 0)
|
||||
result = list(cs.compact_sets(root, 1, 1))
|
||||
assert all(isinstance(chord, tuple) for chord in result)
|
||||
def test_transpose(self):
|
||||
p1 = Pitch((0, 0, 0, 0), DIMS_4)
|
||||
p2 = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
chord = Chord((p1, p2), DIMS_4)
|
||||
trans = Pitch((1, 0, 0, 0), DIMS_4)
|
||||
transposed = chord.transpose(trans)
|
||||
assert transposed[0].hs_array == (1, 0, 0, 0)
|
||||
assert transposed[1].hs_array == (1, 1, 0, 0)
|
||||
|
||||
def test_is_connected_single(self):
|
||||
p = Pitch((0, 0, 0, 0), DIMS_4)
|
||||
chord = Chord((p,), DIMS_4)
|
||||
assert chord.is_connected() is True
|
||||
|
||||
def test_is_connected_adjacent(self):
|
||||
p1 = Pitch((0, 0, 0, 0), DIMS_4)
|
||||
p2 = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
chord = Chord((p1, p2), DIMS_4)
|
||||
assert chord.is_connected() is True
|
||||
|
||||
def test_is_connected_not_adjacent(self):
|
||||
p1 = Pitch((0, 0, 0, 0), DIMS_4)
|
||||
p2 = Pitch((0, 2, 0, 0), DIMS_4)
|
||||
chord = Chord((p1, p2), DIMS_4)
|
||||
assert chord.is_connected() is False
|
||||
|
||||
def test_sorted_by_frequency(self):
|
||||
p1 = Pitch((0, 1, 0, 0), DIMS_4) # 3/2
|
||||
p2 = Pitch((0, 0, 0, 0), DIMS_4) # 1/1
|
||||
chord = Chord((p1, p2), DIMS_4)
|
||||
sorted_pitches = chord.sorted_by_frequency()
|
||||
assert sorted_pitches[0].hs_array == (0, 0, 0, 0)
|
||||
assert sorted_pitches[1].hs_array == (0, 1, 0, 0)
|
||||
|
||||
def test_symmetric_difference_size(self):
|
||||
p1 = Pitch((0, 0, 0, 0), DIMS_4)
|
||||
p2 = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
p3 = Pitch((0, 2, 0, 0), DIMS_4)
|
||||
chord1 = Chord((p1, p2), DIMS_4)
|
||||
chord2 = Chord((p1, p3), DIMS_4)
|
||||
# Both have p1, differ on p2 vs p3: symdiff = 2
|
||||
assert chord1.symmetric_difference_size(chord2) == 2
|
||||
|
||||
def test_hash(self):
|
||||
p1 = Pitch((0, 0, 0, 0), DIMS_4)
|
||||
p2 = Pitch((0, 1, 0, 0), DIMS_4)
|
||||
c1 = Chord((p1, p2), DIMS_4)
|
||||
c2 = Chord((p1, p2), DIMS_4)
|
||||
assert hash(c1) == hash(c2)
|
||||
|
||||
|
||||
class TestStochasticHamiltonian:
|
||||
@pytest.mark.skip(reason="Requires full graph setup")
|
||||
def test_stochastic_hamiltonian_from_graph(self, mock_write):
|
||||
pass
|
||||
class TestHarmonicSpace:
|
||||
"""Tests for HarmonicSpace class."""
|
||||
|
||||
@pytest.mark.skip(reason="Requires full chord set setup")
|
||||
def test_stochastic_hamiltonian_using_chord_set(self, mock_write):
|
||||
pass
|
||||
def test_create_space(self):
|
||||
space = HarmonicSpace(DIMS_4)
|
||||
assert space.dims == DIMS_4
|
||||
assert space.collapsed is True
|
||||
|
||||
def test_root(self):
|
||||
space = HarmonicSpace(DIMS_4)
|
||||
root = space.root()
|
||||
assert root.hs_array == (0, 0, 0, 0)
|
||||
|
||||
def test_generate_connected_sets_size_3(self):
|
||||
space = HarmonicSpace(DIMS_4)
|
||||
chords = space.generate_connected_sets(3, 3)
|
||||
assert len(chords) > 0
|
||||
assert all(len(c) == 3 for c in chords)
|
||||
|
||||
def test_generate_connected_sets_all_connected(self):
|
||||
space = HarmonicSpace(DIMS_4)
|
||||
chords = space.generate_connected_sets(3, 3)
|
||||
assert all(c.is_connected() for c in chords)
|
||||
|
||||
def test_generate_connected_sets_size_2(self):
|
||||
space = HarmonicSpace(DIMS_4)
|
||||
chords = space.generate_connected_sets(2, 2)
|
||||
assert len(chords) > 0
|
||||
assert all(len(c) == 2 for c in chords)
|
||||
|
||||
def test_build_voice_leading_graph(self):
|
||||
space = HarmonicSpace(DIMS_4)
|
||||
chords = space.generate_connected_sets(3, 3)
|
||||
graph = space.build_voice_leading_graph(chords, 2, 2)
|
||||
assert graph.number_of_nodes() == len(chords)
|
||||
assert graph.number_of_edges() > 0
|
||||
|
||||
def test_build_graph_symdiff_filter(self):
|
||||
space = HarmonicSpace(DIMS_4)
|
||||
chords = space.generate_connected_sets(3, 3)
|
||||
# Test with symdiff range 1-3
|
||||
graph = space.build_voice_leading_graph(chords, 1, 3)
|
||||
assert graph.number_of_edges() > 0
|
||||
|
||||
|
||||
class TestWeightFunctions:
|
||||
def test_is_bass_rooted_true(self):
|
||||
chord = ((0, 0, 0, 0), (0, 1, 0, 0))
|
||||
assert cs.is_bass_rooted(chord) == True
|
||||
class TestCLI:
|
||||
"""Tests for CLI functionality."""
|
||||
|
||||
def test_is_bass_rooted_false(self):
|
||||
chord = ((0, 0, 0, 0), (0, 2, 0, 0))
|
||||
assert cs.is_bass_rooted(chord) == False
|
||||
def test_args_parsing(self):
|
||||
import argparse
|
||||
from src.io import main
|
||||
|
||||
@pytest.mark.skip(reason="Requires complete edge structure")
|
||||
def test_voice_crossing_weights_no_crossing(self):
|
||||
pass
|
||||
|
||||
def test_contrary_motion_weights(self):
|
||||
edge = [
|
||||
[
|
||||
((0, 0, 0), (0, 1, 0)),
|
||||
((0, 0, 0), (0, 2, 0)),
|
||||
{
|
||||
"transposition": (0, 0, 0),
|
||||
"movements": {
|
||||
(0, 0, 0): {"cent_difference": -100},
|
||||
(0, 1, 0): {"cent_difference": 0},
|
||||
(0, 2, 0): {"cent_difference": 100},
|
||||
},
|
||||
},
|
||||
]
|
||||
]
|
||||
weights = list(cs.contrary_motion_weights(edge))
|
||||
assert weights[0] == 10
|
||||
# This is a basic test - in practice we'd test the parser more thoroughly
|
||||
# For now just verify the module can be imported
|
||||
assert main is not None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
Loading…
Reference in a new issue