import pytest from mido import Message from solo_tool.midi_controller_launchpad_mini import MidiController from fixtures import songPool, soloTool, mockPlayer, testSongs LED_RED = 3 LED_YELLOW = 126 LED_GREEN = 124 LED_OFF = 0 nextSongButton = 55 fwd25PcButton = 54 fwd5PcButton = 53 fwd1PcButton = 52 rwd1PcButton = 51 rwd5PcButton = 50 rwd25PcButton = 49 previousSongButton = 48 playPauseButton = 112 jumpToStartButton = 96 nextKeyPositionButton = 119 previousKeyPositionButton = 118 setKeyPositionButton = 117 jumpToKeyPositionButton = 114 class MidiWrapperMock: def __init__(self): self.callback = None self.connectedDevice = None self.sentMessages = list() def connect(self, deviceName, callback): self.connectedDevice = deviceName self.callback = callback def disconnect(self): self.connectedDevice = None 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: msg = Message("note_on", note=note, velocity=velocity, channel=channel) self.callback(msg) def getLatestMessage(self): return self.sentMessages[-1] @pytest.fixture def midiWrapperMock(): return MidiWrapperMock() @pytest.fixture def uut(soloTool, midiWrapperMock): return MidiController(soloTool, midiWrapperMock) def test_startAndPauseButtons(uut, midiWrapperMock, mockPlayer): uut.connect() assert not mockPlayer.playing midiWrapperMock.simulateInput(playPauseButton) assert mockPlayer.playing assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0) midiWrapperMock.simulateInput(playPauseButton) assert not mockPlayer.playing assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0) def test_startPauseButtonLed(uut, midiWrapperMock, mockPlayer, soloTool): uut.connect() assert not mockPlayer.playing mockPlayer.playing = True mockPlayer.simulatePlayingStateChanged() assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0) mockPlayer.playing = False mockPlayer.simulatePlayingStateChanged() assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0) def test_jumpToKeyPositionButton(uut, midiWrapperMock, soloTool, mockPlayer, testSongs): soloTool.addSong(testSongs[0]) uut.connect() soloTool.keyPoint = 0.5 assert mockPlayer.position == 0.0 midiWrapperMock.simulateInput(jumpToKeyPositionButton) assert mockPlayer.position == 0.5 # TODO implement def test_jumpToStartButton(uut, midiWrapperMock, soloTool, mockPlayer): pass def test_previousAndNextSongButtons(uut, midiWrapperMock, soloTool, mockPlayer, testSongs): for s in testSongs: soloTool.addSong(s) uut.connect() assert mockPlayer.currentSong == testSongs[0] midiWrapperMock.simulateInput(nextSongButton) assert mockPlayer.currentSong == testSongs[1] for _ in testSongs: midiWrapperMock.simulateInput(nextSongButton) assert mockPlayer.currentSong == testSongs[-1] midiWrapperMock.simulateInput(previousSongButton) assert mockPlayer.currentSong == testSongs[-2] for _ in testSongs: midiWrapperMock.simulateInput(previousSongButton) assert mockPlayer.currentSong == testSongs[0] def test_previousAndNextKeyPositionButtons(uut, midiWrapperMock, soloTool, mockPlayer, testSongs): keyPoints = [0.2, 0.1] soloTool.addSong(testSongs[0]) soloTool.keyPoints = keyPoints uut.connect() assert soloTool.keyPoint == 0.0 midiWrapperMock.simulateInput(nextKeyPositionButton) assert soloTool.keyPoint == 0.1 midiWrapperMock.simulateInput(nextKeyPositionButton) assert soloTool.keyPoint == 0.2 midiWrapperMock.simulateInput(previousKeyPositionButton) assert soloTool.keyPoint == 0.1 midiWrapperMock.simulateInput(previousKeyPositionButton) assert soloTool.keyPoint == 0.1 def test_playbackRateButtons(uut, midiWrapperMock, soloTool, mockPlayer): 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 mockPlayer.rate == 1.0 for t, button in enumerate(playbackRateOptions): midiWrapperMock.sentMessages.clear() midiWrapperMock.simulateInput(button) assert mockPlayer.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, mockPlayer): 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 mockPlayer.rate == 1.0 for t, (rate, leds) in enumerate(playbackRateOptions): print(t) midiWrapperMock.sentMessages.clear() soloTool.rate = rate assert mockPlayer.rate == rate for i, colour in enumerate(leds): assert midiWrapperMock.sentMessages[i] == (16 + i, colour, 0) def test_playbackVolumeButtons(uut, midiWrapperMock, soloTool, mockPlayer): 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 mockPlayer.volume == 1.0 for t, button in enumerate(playbackVolumeOptions): midiWrapperMock.sentMessages.clear() midiWrapperMock.simulateInput(button) assert mockPlayer.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, mockPlayer): 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 mockPlayer.volume == 1.0 for t, (volume, leds) in enumerate(playbackVolumeOptions): midiWrapperMock.sentMessages.clear() soloTool.volume = volume assert mockPlayer.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_connectDisconnect(uut, midiWrapperMock): startupMessages = list( [(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 [ (jumpToStartButton, LED_YELLOW, 0), (playPauseButton, LED_YELLOW, 0), (jumpToKeyPositionButton, LED_YELLOW, 0), (previousKeyPositionButton, LED_RED, 0), (nextKeyPositionButton, LED_GREEN, 0), (setKeyPositionButton, LED_YELLOW, 0), (previousSongButton, LED_RED, 0), (rwd1PcButton, LED_RED, 0), (rwd5PcButton, LED_RED, 0), (rwd25PcButton, LED_RED, 0), (nextSongButton, LED_GREEN, 0), (fwd1PcButton, LED_GREEN, 0), (fwd5PcButton, LED_GREEN, 0), (fwd25PcButton, LED_GREEN, 0), ]) teardownMessages = [(int(i / 8) * 16 + (i % 8), LED_OFF, 0) for i in range(0, 64)] # clear all expectedDevice = "Launchpad Mini MIDI 1" uut.connect() assert midiWrapperMock.connectedDevice == expectedDevice assert set(midiWrapperMock.sentMessages) == set(startupMessages) midiWrapperMock.sentMessages.clear() uut.disconnect() assert set(midiWrapperMock.sentMessages) == set(teardownMessages) def test_playingFeedbackWhenChangingSong(uut, midiWrapperMock, soloTool, mockPlayer, testSongs): for s in testSongs: soloTool.addSong(s) uut.connect() soloTool.play() assert mockPlayer.playing assert midiWrapperMock.getLatestMessage() == (playPauseButton, LED_GREEN, 0) soloTool.song = 1 assert not mockPlayer.playing assert midiWrapperMock.getLatestMessage() == (playPauseButton, LED_YELLOW, 0) def test_setKeyPositionButton(uut, midiWrapperMock, soloTool, mockPlayer, testSongs): soloTool.addSong(testSongs[0]) uut.connect() mockPlayer.position = 0.3 midiWrapperMock.simulateInput(setKeyPositionButton) assert soloTool.keyPoint == 0.3 mockPlayer.position = 0.5 midiWrapperMock.simulateInput(setKeyPositionButton) assert soloTool.keyPoint == 0.5 mockPlayer.position = 0.7 midiWrapperMock.simulateInput(jumpToKeyPositionButton) assert mockPlayer.position == 0.5 def test_seekButtons(uut, midiWrapperMock, soloTool, mockPlayer, testSongs): soloTool.addSong(testSongs[0]) uut.connect() assert mockPlayer.position == 0.0 midiWrapperMock.simulateInput(fwd25PcButton) assert mockPlayer.position == 0.25 midiWrapperMock.simulateInput(fwd5PcButton) assert mockPlayer.position == 0.30 midiWrapperMock.simulateInput(fwd1PcButton) assert mockPlayer.position == 0.31 midiWrapperMock.simulateInput(fwd25PcButton) midiWrapperMock.simulateInput(fwd25PcButton) midiWrapperMock.simulateInput(fwd25PcButton) assert mockPlayer.position == 1.0 midiWrapperMock.simulateInput(rwd25PcButton) assert mockPlayer.position == 0.75 midiWrapperMock.simulateInput(rwd5PcButton) assert mockPlayer.position == 0.70 midiWrapperMock.simulateInput(rwd1PcButton) assert mockPlayer.position == 0.69 midiWrapperMock.simulateInput(rwd25PcButton) midiWrapperMock.simulateInput(rwd25PcButton) midiWrapperMock.simulateInput(rwd25PcButton) assert mockPlayer.position == 0.0