Skip to content

Commit 1499b64

Browse files
committed
Add graph drag interaction tests
1 parent d2bbfb2 commit 1499b64

1 file changed

Lines changed: 197 additions & 0 deletions

File tree

tests/test_mini_eq_window_graph.py

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from types import SimpleNamespace
44

5+
import pytest
6+
57
from tests._mini_eq_imports import import_mini_eq_module
68

79
core = import_mini_eq_module("core")
@@ -94,6 +96,117 @@ def get_value(self) -> float:
9496
return self.value
9597

9698

99+
class FakeGraphArea:
100+
def __init__(self, width: int = 640, height: int = 240) -> None:
101+
self.width = width
102+
self.height = height
103+
104+
def get_allocated_width(self) -> int:
105+
return self.width
106+
107+
def get_allocated_height(self) -> int:
108+
return self.height
109+
110+
111+
class FakeGraphDragGesture:
112+
def __init__(self, start_x: float, start_y: float, state=0) -> None:
113+
self.start_x = start_x
114+
self.start_y = start_y
115+
self.state = state
116+
117+
def get_start_point(self) -> tuple[bool, float, float]:
118+
return True, self.start_x, self.start_y
119+
120+
def get_current_event_state(self):
121+
return self.state
122+
123+
124+
class FakeGraphController:
125+
def __init__(self, bands: list[core.EqBand], preamp_db: float = 0.0) -> None:
126+
self.bands = bands
127+
self.preamp_db = preamp_db
128+
self.frequency_updates: list[tuple[int, float]] = []
129+
self.gain_updates: list[tuple[int, float]] = []
130+
self.q_updates: list[tuple[int, float]] = []
131+
132+
def set_band_frequency(self, index: int, frequency: float, *, apply: bool = True) -> bool:
133+
self.frequency_updates.append((index, frequency))
134+
if self.bands[index].frequency == frequency:
135+
return False
136+
self.bands[index].frequency = frequency
137+
return True
138+
139+
def set_band_gain(self, index: int, gain_db: float, *, apply: bool = True) -> bool:
140+
self.gain_updates.append((index, gain_db))
141+
if self.bands[index].gain_db == gain_db:
142+
return False
143+
self.bands[index].gain_db = gain_db
144+
return True
145+
146+
def set_band_q(self, index: int, q_value: float, *, apply: bool = True) -> bool:
147+
self.q_updates.append((index, q_value))
148+
if self.bands[index].q == q_value:
149+
return False
150+
self.bands[index].q = q_value
151+
return True
152+
153+
154+
class GraphInteractionWindow(window_graph.MiniEqWindowGraphMixin):
155+
def __init__(self, bands: list[core.EqBand]) -> None:
156+
self.controller = FakeGraphController(bands)
157+
self.graph_area = FakeGraphArea()
158+
self.visible_band_count = len(bands)
159+
self.selected_band_index = None
160+
self.updating_ui = False
161+
self.drag_band_index = None
162+
self.drag_start_q = None
163+
self.engine_updates: list[int] = []
164+
self.ui_updates: list[object] = []
165+
166+
def select_band(self, index: int) -> None:
167+
self.selected_band_index = index
168+
169+
def schedule_band_engine_update(self, index: int) -> None:
170+
self.engine_updates.append(index)
171+
172+
def update_band_fader(self, index: int, solo_active: bool | None = None) -> None:
173+
self.ui_updates.append(("fader", index))
174+
175+
def update_focus_summary(self) -> None:
176+
self.ui_updates.append("focus")
177+
178+
def update_selected_band_editor(self) -> None:
179+
self.ui_updates.append("editor")
180+
181+
def invalidate_graph_response_cache(self) -> None:
182+
self.ui_updates.append("invalidate")
183+
184+
def queue_response_draw(self) -> None:
185+
self.ui_updates.append("draw")
186+
187+
def schedule_curve_metadata_refresh(self) -> None:
188+
self.ui_updates.append("metadata")
189+
190+
def band_point(self, index: int) -> tuple[float, float]:
191+
width = self.graph_area.get_allocated_width()
192+
height = self.graph_area.get_allocated_height()
193+
width_f, height_f, left, right, top, bottom = self.graph_plot_bounds(width, height)
194+
band = self.controller.bands[index]
195+
x = self.frequency_to_x(band.frequency, width_f, left, right)
196+
y = self.db_to_y(
197+
window_graph.total_response_db(
198+
self.controller.bands,
199+
self.controller.preamp_db,
200+
core.SAMPLE_RATE,
201+
band.frequency,
202+
),
203+
height_f,
204+
top,
205+
bottom,
206+
)
207+
return x, y
208+
209+
97210
class FocusSummaryWindow(window_graph.MiniEqWindowGraphMixin):
98211
def __init__(
99212
self,
@@ -306,3 +419,87 @@ def test_curve_metadata_refresh_idle_notifies_control_clients() -> None:
306419
assert keep_source is False
307420
assert test_window.curve_metadata_refresh_source_id == 0
308421
assert calls == ["status", "preset-state", "control-state"]
422+
423+
424+
def test_graph_press_uses_shared_plot_bounds_for_frequency_mapping() -> None:
425+
window = GraphInteractionWindow(
426+
[
427+
core.EqBand(core.FILTER_TYPES["Bell"], 100.0),
428+
core.EqBand(core.FILTER_TYPES["Bell"], 1000.0),
429+
]
430+
)
431+
calls: list[tuple[float, float, float, float]] = []
432+
433+
def graph_plot_bounds(width: int, height: int) -> tuple[float, float, float, float, float, float]:
434+
assert (width, height) == (640, 240)
435+
return 640.0, 240.0, 11.0, 77.0, 13.0, 17.0
436+
437+
def x_to_frequency(x: float, width: float, left: float, right: float) -> float:
438+
calls.append((x, width, left, right))
439+
return 1000.0
440+
441+
window.graph_plot_bounds = graph_plot_bounds
442+
window.x_to_frequency = x_to_frequency
443+
444+
window.on_graph_pressed(None, 1, 240.0, 120.0)
445+
446+
assert calls == [(240.0, 640.0, 11.0, 77.0)]
447+
assert window.selected_band_index == 1
448+
449+
450+
def test_graph_drag_preserves_solo_context_when_calculating_other_response() -> None:
451+
window = GraphInteractionWindow(
452+
[
453+
core.EqBand(core.FILTER_TYPES["Bell"], 1000.0, gain_db=6.0, solo=True),
454+
core.EqBand(core.FILTER_TYPES["Bell"], 1000.0, gain_db=6.0),
455+
]
456+
)
457+
start_x, start_y = window.band_point(0)
458+
window.drag_band_index = 0
459+
window.drag_start_q = window.controller.bands[0].q
460+
461+
window.on_graph_drag_update(FakeGraphDragGesture(start_x, start_y), 0.0, 0.0)
462+
463+
assert window.controller.gain_updates
464+
assert window.controller.bands[0].gain_db == pytest.approx(6.0)
465+
466+
467+
def test_graph_drag_does_not_change_gain_for_non_gain_filters() -> None:
468+
window = GraphInteractionWindow(
469+
[
470+
core.EqBand(core.FILTER_TYPES["Hi-pass"], 1000.0, gain_db=3.0),
471+
]
472+
)
473+
start_x, start_y = window.band_point(0)
474+
window.drag_band_index = 0
475+
window.drag_start_q = window.controller.bands[0].q
476+
477+
window.on_graph_drag_update(FakeGraphDragGesture(start_x, start_y), 40.0, 80.0)
478+
479+
assert window.controller.frequency_updates
480+
assert window.controller.gain_updates == []
481+
assert window.controller.bands[0].gain_db == 3.0
482+
483+
484+
def test_graph_shift_drag_changes_q_without_changing_frequency_or_gain() -> None:
485+
window = GraphInteractionWindow(
486+
[
487+
core.EqBand(core.FILTER_TYPES["Bell"], 1000.0, gain_db=3.0, q=1.0),
488+
]
489+
)
490+
start_x, start_y = window.band_point(0)
491+
window.drag_band_index = 0
492+
window.drag_start_q = 1.0
493+
494+
window.on_graph_drag_update(
495+
FakeGraphDragGesture(start_x, start_y, window_graph.Gdk.ModifierType.SHIFT_MASK),
496+
90.0,
497+
-100.0,
498+
)
499+
500+
assert window.controller.frequency_updates == []
501+
assert window.controller.gain_updates == []
502+
assert window.controller.q_updates == [(0, pytest.approx(1.5))]
503+
assert window.controller.bands[0].frequency == 1000.0
504+
assert window.controller.bands[0].gain_db == 3.0
505+
assert window.controller.bands[0].q == pytest.approx(1.5)

0 commit comments

Comments
 (0)