feat: wire editor ops (delete/silence/reverse/trim) + undo/redo + save-as
This commit is contained in:
@@ -658,3 +658,74 @@ def test_audio_editor_dialog_scaffold(win):
|
||||
# a bad path decodes to an empty waveform without crashing
|
||||
dlg._reload()
|
||||
dlg.close()
|
||||
|
||||
|
||||
def test_editor_delete_builds_command(win, tmp_path, monkeypatch):
|
||||
import main as m, pytest
|
||||
src = tmp_path / "v0.wav"; src.write_bytes(b"")
|
||||
dlg = m.AudioEditorDialog(str(src), parent=win)
|
||||
dlg._wave.set_view(0.0, 5.0); dlg._wave.set_selection(1.0, 3.0)
|
||||
seen = {}
|
||||
class _Stop(Exception): pass
|
||||
def fake(inp, s, e, out):
|
||||
seen.update(inp=inp, s=s, e=e); raise _Stop
|
||||
monkeypatch.setattr(m, "build_audio_delete_command", fake)
|
||||
with pytest.raises(_Stop):
|
||||
dlg._on_delete()
|
||||
assert seen["inp"] == str(src) and seen["s"] == 1.0 and seen["e"] == 3.0
|
||||
|
||||
|
||||
def test_editor_op_no_region_is_safe(win, tmp_path, monkeypatch):
|
||||
import main as m
|
||||
src = tmp_path / "v0.wav"; src.write_bytes(b"")
|
||||
dlg = m.AudioEditorDialog(str(src), parent=win)
|
||||
dlg._wave.set_view(0.0, 5.0); dlg._wave.set_selection(2.0, 2.0) # zero-width
|
||||
n = {"c": 0}
|
||||
monkeypatch.setattr(m, "build_audio_delete_command",
|
||||
lambda *a: n.__setitem__("c", n["c"] + 1) or [])
|
||||
dlg._on_delete()
|
||||
assert n["c"] == 0 # guarded, builder never called
|
||||
|
||||
|
||||
def test_editor_delete_whole_clip_blocked(win, tmp_path, monkeypatch):
|
||||
import main as m
|
||||
src = tmp_path / "v0.wav"; src.write_bytes(b"")
|
||||
dlg = m.AudioEditorDialog(str(src), parent=win)
|
||||
dlg._clip_dur = 5.0
|
||||
dlg._wave.set_view(0.0, 5.0); dlg._wave.set_selection(0.0, 5.0) # whole clip
|
||||
n = {"c": 0}
|
||||
monkeypatch.setattr(m, "build_audio_delete_command",
|
||||
lambda *a: n.__setitem__("c", n["c"] + 1) or [])
|
||||
dlg._on_delete()
|
||||
assert n["c"] == 0 # can't delete the whole clip
|
||||
|
||||
|
||||
def test_editor_op_uses_clip_duration_not_view(win, tmp_path, monkeypatch):
|
||||
import main as m, pytest
|
||||
src = tmp_path / "v0.wav"; src.write_bytes(b"")
|
||||
dlg = m.AudioEditorDialog(str(src), parent=win)
|
||||
dlg._clip_dur = 5.0
|
||||
dlg._wave.set_view(0.5, 4.0) # zoomed sub-window (view ends at 4.5)
|
||||
dlg._wave.set_selection(1.0, 4.5)
|
||||
seen = {}
|
||||
class _Stop(Exception): pass
|
||||
def fake(inp, s, e, out):
|
||||
seen.update(s=s, e=e); raise _Stop
|
||||
monkeypatch.setattr(m, "build_audio_silence_command", fake)
|
||||
with pytest.raises(_Stop):
|
||||
dlg._on_silence()
|
||||
assert seen["e"] == 4.5 # clip duration, not the 4.0 view span
|
||||
|
||||
|
||||
def test_editor_undo_redo(win, tmp_path, monkeypatch):
|
||||
import main as m
|
||||
src = tmp_path / "v0.wav"; src.write_bytes(b"")
|
||||
dlg = m.AudioEditorDialog(str(src), parent=win)
|
||||
monkeypatch.setattr(dlg, "_reload", lambda: None)
|
||||
dlg._versions = [str(src), "/t/v1.wav", "/t/v2.wav"]; dlg._ver_idx = 2
|
||||
dlg._on_undo(); assert dlg._ver_idx == 1
|
||||
dlg._on_undo(); assert dlg._ver_idx == 0
|
||||
dlg._on_undo(); assert dlg._ver_idx == 0 # clamped at base
|
||||
dlg._on_redo(); assert dlg._ver_idx == 1
|
||||
dlg._on_redo(); assert dlg._ver_idx == 2
|
||||
dlg._on_redo(); assert dlg._ver_idx == 2 # clamped at top
|
||||
|
||||
Reference in New Issue
Block a user