aboutsummaryrefslogtreecommitdiffstats
path: root/solo-tool-project/test/midi_launchpad_mini_integrationtest.py
diff options
context:
space:
mode:
authorEddy Pedroni <epedroni@pm.me>2024-11-09 20:35:56 +0100
committerEddy Pedroni <epedroni@pm.me>2024-11-09 20:35:56 +0100
commitcda8197669409689be291660f93cb288ab2d31b3 (patch)
tree81db9b0c7c0491e0737cbffb39af6b935c0dfeb8 /solo-tool-project/test/midi_launchpad_mini_integrationtest.py
parenta2257a900d4fffd6f94b73f1c48c62370ed1d684 (diff)
Migrate to project-based structure
Diffstat (limited to 'solo-tool-project/test/midi_launchpad_mini_integrationtest.py')
-rw-r--r--solo-tool-project/test/midi_launchpad_mini_integrationtest.py387
1 files changed, 387 insertions, 0 deletions
diff --git a/solo-tool-project/test/midi_launchpad_mini_integrationtest.py b/solo-tool-project/test/midi_launchpad_mini_integrationtest.py
new file mode 100644
index 0000000..8542aae
--- /dev/null
+++ b/solo-tool-project/test/midi_launchpad_mini_integrationtest.py
@@ -0,0 +1,387 @@
+import pytest
+
+from solo_tool.midi_controller_launchpad_mini import MidiController
+from solo_tool.solo_tool import SoloTool
+from player_mock import Player as PlayerMock
+
+LED_RED = 3
+LED_YELLOW = 126
+LED_GREEN = 124
+LED_OFF = 0
+
+nextSongButton = 119
+previousSongButton = 118
+playPauseButton = 112
+stopButton = 96
+nextLimitButton = 103
+previousLimitButton = 102
+abToggleButton = 101
+jumpToAButton = 99
+
+class MidiWrapperMock:
+ def __init__(self):
+ self.callback = None
+ self.connectedDevice = None
+ self.sentMessages = list()
+
+ def setCallback(self, callback):
+ self.callback = callback
+
+ def connect(self, deviceName):
+ self.connectedDevice = deviceName
+
+ def sendMessage(self, note, velocity, channel):
+ self.sentMessages.append((note, velocity, channel))
+
+ def simulateInput(self, note, velocity=127, channel=0):
+ if self.callback is not None:
+ from mido import Message
+ msg = Message("note_on", note=note, velocity=velocity, channel=channel)
+ self.callback(msg)
+
+ def getLatestMessage(self):
+ return self.sentMessages[-1]
+
+@pytest.fixture
+def playerMock():
+ return PlayerMock()
+
+@pytest.fixture
+def soloTool(playerMock):
+ return SoloTool(playerMock)
+
+@pytest.fixture
+def midiWrapperMock():
+ return MidiWrapperMock()
+
+@pytest.fixture
+def uut(soloTool, midiWrapperMock):
+ return MidiController(soloTool, midiWrapperMock)
+
+def test_connect(uut, midiWrapperMock):
+ expectedDevice = "Launchpad Mini MIDI 1"
+ uut.connect()
+
+ assert midiWrapperMock.connectedDevice == expectedDevice
+
+def test_startStopAndPauseButtons(uut, midiWrapperMock, playerMock):
+ uut.connect()
+
+ assert playerMock.state == PlayerMock.STOPPED
+
+ midiWrapperMock.simulateInput(playPauseButton)
+ assert playerMock.state == PlayerMock.PLAYING
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0)
+
+ midiWrapperMock.simulateInput(stopButton)
+ assert playerMock.state == PlayerMock.STOPPED
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0)
+
+ midiWrapperMock.simulateInput(playPauseButton)
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0)
+
+ midiWrapperMock.simulateInput(playPauseButton)
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0)
+ assert playerMock.state == PlayerMock.PAUSED
+
+ midiWrapperMock.simulateInput(playPauseButton)
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0)
+ assert playerMock.state == PlayerMock.PLAYING
+
+ midiWrapperMock.simulateInput(playPauseButton)
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0)
+
+ midiWrapperMock.simulateInput(stopButton)
+ assert playerMock.state == PlayerMock.STOPPED
+
+def test_startPauseButtonLed(uut, midiWrapperMock, playerMock, soloTool):
+ uut.connect()
+
+ assert playerMock.state == PlayerMock.STOPPED
+
+ playerMock.state = PlayerMock.PLAYING
+ playerMock.simulatePlayingStateChanged()
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0)
+
+ playerMock.state = PlayerMock.STOPPED
+ playerMock.simulatePlayingStateChanged()
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0)
+
+ playerMock.state = PlayerMock.PAUSED
+ playerMock.simulatePlayingStateChanged()
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0)
+
+ playerMock.state = PlayerMock.PLAYING
+ playerMock.simulatePlayingStateChanged()
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0)
+
+def test_abToggleButton(uut, midiWrapperMock, soloTool):
+ uut.connect()
+
+ midiWrapperMock.simulateInput(abToggleButton)
+ assert soloTool.isAbLimitEnabled()
+ assert midiWrapperMock.getLatestMessage() == (abToggleButton, MidiController.LED_GREEN, 0)
+
+ midiWrapperMock.simulateInput(abToggleButton)
+ assert not soloTool.isAbLimitEnabled()
+ assert midiWrapperMock.getLatestMessage() == (abToggleButton, MidiController.LED_RED, 0)
+
+def test_abToggleButtonLed(uut, midiWrapperMock, soloTool):
+ uut.connect()
+
+ soloTool.setAbLimitEnable(True)
+ assert midiWrapperMock.getLatestMessage() == (abToggleButton, MidiController.LED_GREEN, 0)
+
+ soloTool.setAbLimitEnable(False)
+ assert midiWrapperMock.getLatestMessage() == (abToggleButton, MidiController.LED_RED, 0)
+
+def test_jumpToAButton(uut, midiWrapperMock, soloTool, playerMock):
+ ab = (0.5, 0.6)
+ uut.connect()
+
+ soloTool.setAbLimits(ab[0], ab[1])
+ assert playerMock.position == 0.0
+
+ midiWrapperMock.simulateInput(jumpToAButton)
+ assert playerMock.position == ab[0]
+
+def test_previousAndNextSongButtons(uut, midiWrapperMock, soloTool, playerMock):
+ songs = [
+ "test.flac",
+ "test.mp3"
+ ]
+ for s in songs:
+ soloTool.addSong(s)
+ uut.connect()
+
+ assert playerMock.currentSong == None
+ midiWrapperMock.simulateInput(nextSongButton)
+ assert playerMock.currentSong == songs[0]
+
+ midiWrapperMock.simulateInput(nextSongButton)
+ assert playerMock.currentSong == songs[1]
+
+ midiWrapperMock.simulateInput(previousSongButton)
+ assert playerMock.currentSong == songs[0]
+
+ midiWrapperMock.simulateInput(previousSongButton)
+ assert playerMock.currentSong == songs[0]
+
+def test_previousAndNextAbButtons(uut, midiWrapperMock, soloTool, playerMock):
+ song = "test.flac"
+ abLimits = [
+ [0.2, 0.4],
+ [0.1, 0.3]
+ ]
+
+ soloTool.addSong(song)
+ soloTool.setSong(0)
+ soloTool.setAbLimitEnable(True)
+
+ for ab in abLimits:
+ soloTool.storeAbLimits(ab[0], ab[1])
+
+ uut.connect()
+
+ def checkLimit(aLimit, bLimit):
+ playerMock.position = bLimit - 0.1
+ soloTool.tick()
+ assert playerMock.position == bLimit - 0.1
+
+ playerMock.position = bLimit + 0.1
+ soloTool.tick()
+ assert playerMock.position == aLimit
+
+ checkLimit(0.0, 0.0)
+
+ midiWrapperMock.simulateInput(nextLimitButton)
+ checkLimit(abLimits[0][0], abLimits[0][1])
+
+ midiWrapperMock.simulateInput(nextLimitButton)
+ checkLimit(abLimits[1][0], abLimits[1][1])
+
+ midiWrapperMock.simulateInput(nextLimitButton)
+ checkLimit(abLimits[1][0], abLimits[1][1])
+
+ midiWrapperMock.simulateInput(previousLimitButton)
+ checkLimit(abLimits[0][0], abLimits[0][1])
+
+ midiWrapperMock.simulateInput(previousLimitButton)
+ checkLimit(abLimits[0][0], abLimits[0][1])
+
+def test_playbackRateButtons(uut, midiWrapperMock, soloTool, playerMock):
+ playbackRateOptions = {
+ 16 : (0.5, [LED_YELLOW] * 1 + [LED_OFF] * 7),
+ 17 : (0.6, [LED_YELLOW] * 2 + [LED_OFF] * 6),
+ 18 : (0.7, [LED_YELLOW] * 3 + [LED_OFF] * 5),
+ 19 : (0.8, [LED_YELLOW] * 4 + [LED_OFF] * 4),
+ 20 : (0.9, [LED_YELLOW] * 5 + [LED_OFF] * 3),
+ 21 : (1.0, [LED_YELLOW] * 6 + [LED_OFF] * 2),
+ 22 : (1.1, [LED_YELLOW] * 7 + [LED_OFF] * 1),
+ 23 : (1.2, [LED_YELLOW] * 8)
+ }
+ uut.connect()
+ assert playerMock.rate == 1.0
+
+ for t, button in enumerate(playbackRateOptions):
+ midiWrapperMock.sentMessages.clear()
+
+ midiWrapperMock.simulateInput(button)
+ assert playerMock.rate == playbackRateOptions[button][0]
+
+ for i, colour in enumerate(playbackRateOptions[button][1]):
+ assert midiWrapperMock.sentMessages[i] == (16 + i, colour, 0)
+
+def test_playbackRateLeds(uut, midiWrapperMock, soloTool, playerMock):
+ playbackRateOptions = [
+ (0.00, [LED_OFF] * 8),
+ (0.49, [LED_OFF] * 8),
+
+ (0.50, [LED_YELLOW] * 1 + [LED_OFF] * 7),
+ (0.59, [LED_YELLOW] * 1 + [LED_OFF] * 7),
+
+ (0.60, [LED_YELLOW] * 2 + [LED_OFF] * 6),
+ (0.69, [LED_YELLOW] * 2 + [LED_OFF] * 6),
+
+ (0.70, [LED_YELLOW] * 3 + [LED_OFF] * 5),
+ (0.79, [LED_YELLOW] * 3 + [LED_OFF] * 5),
+
+ (0.80, [LED_YELLOW] * 4 + [LED_OFF] * 4),
+ (0.89, [LED_YELLOW] * 4 + [LED_OFF] * 4),
+
+ (0.90, [LED_YELLOW] * 5 + [LED_OFF] * 3),
+ (0.99, [LED_YELLOW] * 5 + [LED_OFF] * 3),
+
+ (1.00, [LED_YELLOW] * 6 + [LED_OFF] * 2),
+ (1.09, [LED_YELLOW] * 6 + [LED_OFF] * 2),
+
+ (1.10, [LED_YELLOW] * 7 + [LED_OFF] * 1),
+ (1.19, [LED_YELLOW] * 7 + [LED_OFF] * 1),
+
+ (1.2, [LED_YELLOW] * 8),
+ (1.5, [LED_YELLOW] * 8)
+ ]
+ uut.connect()
+ assert playerMock.rate == 1.0
+
+ for t, (rate, leds) in enumerate(playbackRateOptions):
+ midiWrapperMock.sentMessages.clear()
+
+ soloTool.setPlaybackRate(rate)
+ assert playerMock.rate == rate
+
+ for i, colour in enumerate(leds):
+ assert midiWrapperMock.sentMessages[i] == (16 + i, colour, 0)
+
+def test_playbackVolumeButtons(uut, midiWrapperMock, soloTool, playerMock):
+ playbackVolumeOptions = {
+ 0 : (0.5, [LED_GREEN] * 1 + [LED_OFF] * 7),
+ 1 : (0.6, [LED_GREEN] * 2 + [LED_OFF] * 6),
+ 2 : (0.7, [LED_GREEN] * 3 + [LED_OFF] * 5),
+ 3 : (0.8, [LED_GREEN] * 4 + [LED_OFF] * 4),
+ 4 : (0.9, [LED_GREEN] * 5 + [LED_OFF] * 3),
+ 5 : (1.0, [LED_GREEN] * 6 + [LED_OFF] * 2),
+ 6 : (1.1, [LED_GREEN] * 7 + [LED_OFF] * 1),
+ 7 : (1.2, [LED_GREEN] * 8)
+ }
+ uut.connect()
+ assert playerMock.volume == 1.0
+
+ for t, button in enumerate(playbackVolumeOptions):
+ midiWrapperMock.sentMessages.clear()
+
+ midiWrapperMock.simulateInput(button)
+ assert playerMock.volume == playbackVolumeOptions[button][0]
+
+ for i, colour in enumerate(playbackVolumeOptions[button][1]):
+ assert midiWrapperMock.sentMessages[i] == (i, colour, 0)
+
+def test_playbackVolumeLeds(uut, midiWrapperMock, soloTool, playerMock):
+ playbackVolumeOptions = [
+ (0.00, [LED_OFF] * 8),
+ (0.49, [LED_OFF] * 8),
+
+ (0.50, [LED_GREEN] * 1 + [LED_OFF] * 7),
+ (0.59, [LED_GREEN] * 1 + [LED_OFF] * 7),
+
+ (0.60, [LED_GREEN] * 2 + [LED_OFF] * 6),
+ (0.69, [LED_GREEN] * 2 + [LED_OFF] * 6),
+
+ (0.70, [LED_GREEN] * 3 + [LED_OFF] * 5),
+ (0.79, [LED_GREEN] * 3 + [LED_OFF] * 5),
+
+ (0.80, [LED_GREEN] * 4 + [LED_OFF] * 4),
+ (0.89, [LED_GREEN] * 4 + [LED_OFF] * 4),
+
+ (0.90, [LED_GREEN] * 5 + [LED_OFF] * 3),
+ (0.99, [LED_GREEN] * 5 + [LED_OFF] * 3),
+
+ (1.00, [LED_GREEN] * 6 + [LED_OFF] * 2),
+ (1.09, [LED_GREEN] * 6 + [LED_OFF] * 2),
+
+ (1.10, [LED_GREEN] * 7 + [LED_OFF] * 1),
+ (1.19, [LED_GREEN] * 7 + [LED_OFF] * 1),
+
+ (1.2, [LED_GREEN] * 8),
+ (1.5, [LED_GREEN] * 8)
+ ]
+ uut.connect()
+ assert playerMock.volume == 1.0
+
+ for t, (volume, leds) in enumerate(playbackVolumeOptions):
+ midiWrapperMock.sentMessages.clear()
+
+ soloTool.setPlaybackVolume(volume)
+ assert playerMock.volume == volume
+
+ for i, colour in enumerate(leds):
+ assert midiWrapperMock.sentMessages[i] == (i, colour, 0)
+
+def test_unassignedButton(uut, midiWrapperMock):
+ unassignedButton = 48
+ uut.connect()
+
+ # expect no crash
+ midiWrapperMock.simulateInput(unassignedButton)
+ # XXX would be better to assert that nothing changed in the solo tool
+
+def test_initializationMessages(uut, midiWrapperMock):
+ expectedMessages = set(
+ [(int(i / 8) * 16 + (i % 8), LED_OFF, 0) for i in range(0, 64)] + # clear all
+ [(i, LED_GREEN, 0) for i in range(0, 6)] + # volume row
+ [(i, LED_YELLOW, 0) for i in range(16, 22)] + # playback rate row
+ [
+ (stopButton, LED_RED, 0),
+ (playPauseButton, LED_YELLOW, 0),
+ (abToggleButton, LED_RED, 0),
+ (jumpToAButton, LED_YELLOW, 0),
+ (previousLimitButton, LED_RED, 0),
+ (nextLimitButton, LED_GREEN, 0),
+ (previousSongButton, LED_RED, 0),
+ (nextSongButton, LED_GREEN, 0)
+ ]
+ )
+
+ uut.connect()
+
+ sentMessagesSet = set(midiWrapperMock.sentMessages)
+ assert sentMessagesSet == expectedMessages
+
+def test_playingFeedbackWhenChangingSong(uut, midiWrapperMock, soloTool, playerMock):
+ songs = [
+ "test.flac",
+ "test.mp3"
+ ]
+ for s in songs:
+ soloTool.addSong(s)
+ uut.connect()
+
+ soloTool.setSong(0)
+ soloTool.play()
+ assert playerMock.state == PlayerMock.PLAYING
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, LED_GREEN, 0)
+
+ soloTool.nextSong()
+ assert playerMock.state == PlayerMock.STOPPED
+ assert midiWrapperMock.getLatestMessage() == (playPauseButton, LED_YELLOW, 0)
+