import pytest from midi_controller_launchpad_mini import MidiController from solo_tool import SoloTool from player_mock import Player as PlayerMock LED_RED = 3 LED_YELLOW = 126 LED_GREEN = 124 LED_OFF = 0 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): playPauseButton = 112 stopButton = 96 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): playPauseButton = 112 stopButton = 96 uut.connect() assert playerMock.state == PlayerMock.STOPPED soloTool.play() assert playerMock.state == PlayerMock.PLAYING assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0) soloTool.stop() assert playerMock.state == PlayerMock.STOPPED assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0) soloTool.pause() assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0) assert playerMock.state == PlayerMock.PAUSED soloTool.play() assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0) assert playerMock.state == PlayerMock.PLAYING 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(101) assert playerMock.position == ab[0] def test_previousAndNextSongButtons(uut, midiWrapperMock, soloTool, playerMock): nextSongButton = 119 previousSongButton = 118 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): nextLimitButton = 103 previousLimitButton = 102 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_playbackVolumeButtons(uut, midiWrapperMock, soloTool, playerMock): playbackVolumeOptions = { 0 : (0.125, [LED_GREEN] * 1 + [LED_OFF] * 7), 1 : (0.250, [LED_GREEN] * 2 + [LED_OFF] * 6), 2 : (0.375, [LED_GREEN] * 3 + [LED_OFF] * 5), 3 : (0.500, [LED_GREEN] * 4 + [LED_OFF] * 4), 4 : (0.625, [LED_GREEN] * 5 + [LED_OFF] * 3), 5 : (0.750, [LED_GREEN] * 6 + [LED_OFF] * 2), 6 : (0.875, [LED_GREEN] * 7 + [LED_OFF] * 1), 7 : (1.000, [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_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, 8)] + # volume row [(i, LED_YELLOW, 0) for i in range(16, 22)] + # playback rate row [ (96, LED_RED, 0), (101, LED_YELLOW, 0), (102, LED_RED, 0), (103, LED_GREEN, 0), (112, LED_YELLOW, 0), (118, LED_RED, 0), (119, LED_GREEN, 0) ] ) uut.connect() sentMessagesSet = set(midiWrapperMock.sentMessages) assert sentMessagesSet == expectedMessages