import pytest from midi_controller_launchpad_mini import MidiController from solo_tool import SoloTool from player_mock import Player as PlayerMock class MidiWrapperMock: def __init__(self): from queue import SimpleQueue self.callback = None self.connectedDevice = None self.sentMessages = SimpleQueue() self.latestMessage = None def setCallback(self, callback): self.callback = callback def connect(self, deviceName): self.connectedDevice = deviceName def sendMessage(self, note, velocity, channel): self.latestMessage = (note, velocity, channel) self.sentMessages.put(self.latestMessage) 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.latestMessage @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 msg = midiWrapperMock.getLatestMessage() assert msg == (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_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, 17 : 0.6, 18 : 0.7, 19 : 0.8, 20 : 0.9, 21 : 1.0, 22 : 1.1, 23 : 1.2 } uut.connect() assert playerMock.rate == 1.0 for button in playbackRateOptions: midiWrapperMock.simulateInput(button) assert playerMock.rate == playbackRateOptions[button] def test_playbackVolumeButtons(uut, midiWrapperMock, soloTool, playerMock): playbackVolumeOptions = { 0 : 0.125, 1 : 0.250, 2 : 0.375, 3 : 0.500, 4 : 0.625, 5 : 0.750, 6 : 0.875, 7 : 1.000 } uut.connect() assert playerMock.volume == 1.0 for button in playbackVolumeOptions: midiWrapperMock.simulateInput(button) assert playerMock.volume == playbackVolumeOptions[button] 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): green = 124 yellow = 126 red = 3 off = 0 expectedMessages = set( [(int(i / 8) * 16 + (i % 8), off, 0) for i in range(0, 64)] + # clear all [(i, green, 0) for i in range(0, 8)] + # volume row [(i, yellow, 0) for i in range(16, 22)] + # playback rate row [ (96, red, 0), (101, yellow, 0), (102, red, 0), (103, green, 0), (112, yellow, 0), (118, red, 0), (119, green, 0) ] ) uut.connect() sentMessages = set() while (not midiWrapperMock.sentMessages.empty()): sentMessages.add(midiWrapperMock.sentMessages.get()) assert sentMessages == expectedMessages