From cda8197669409689be291660f93cb288ab2d31b3 Mon Sep 17 00:00:00 2001 From: Eddy Pedroni Date: Sat, 9 Nov 2024 20:35:56 +0100 Subject: Migrate to project-based structure --- solo-tool-project/test/abcontroller_unittest.py | 272 ++++++++++ .../test/midi_launchpad_mini_integrationtest.py | 387 ++++++++++++++ solo-tool-project/test/notifier_unittest.py | 100 ++++ solo-tool-project/test/player_mock.py | 71 +++ solo-tool-project/test/playlist_unittest.py | 148 +++++ solo-tool-project/test/session_manager_unittest.py | 163 ++++++ .../test/solo_tool_integrationtest.py | 594 +++++++++++++++++++++ solo-tool-project/test/test.flac | Bin 0 -> 31743252 bytes solo-tool-project/test/test.mp3 | Bin 0 -> 5389533 bytes solo-tool-project/test/test_session.json | 13 + 10 files changed, 1748 insertions(+) create mode 100644 solo-tool-project/test/abcontroller_unittest.py create mode 100644 solo-tool-project/test/midi_launchpad_mini_integrationtest.py create mode 100644 solo-tool-project/test/notifier_unittest.py create mode 100644 solo-tool-project/test/player_mock.py create mode 100644 solo-tool-project/test/playlist_unittest.py create mode 100644 solo-tool-project/test/session_manager_unittest.py create mode 100644 solo-tool-project/test/solo_tool_integrationtest.py create mode 100644 solo-tool-project/test/test.flac create mode 100644 solo-tool-project/test/test.mp3 create mode 100644 solo-tool-project/test/test_session.json (limited to 'solo-tool-project/test') diff --git a/solo-tool-project/test/abcontroller_unittest.py b/solo-tool-project/test/abcontroller_unittest.py new file mode 100644 index 0000000..d2b7d31 --- /dev/null +++ b/solo-tool-project/test/abcontroller_unittest.py @@ -0,0 +1,272 @@ +from solo_tool.abcontroller import ABController +from collections import namedtuple + +TCase = namedtuple("TCase", ["currentPosition", "requestedPosition"]) +AB = namedtuple("AB", ["a", "b"]) +abLimits = AB(0.2, 0.4) + +def _checkLimits(uut, tests): + requestedPosition = None + def callback(newPosition): + nonlocal requestedPosition + requestedPosition = newPosition + + originalCallback = uut._setPositionCallback + uut._setPositionCallback = callback + + for t in tests: + uut.positionChanged(t.currentPosition) + assert requestedPosition == t.requestedPosition + + uut._setPositionCallback = originalCallback + +def checkLimits(uut, aLimit, bLimit, fail=False): + tests = [ + TCase(aLimit - 0.1, None), + TCase(aLimit, None), + TCase(bLimit - 0.1, None), + TCase(bLimit, None), + TCase(bLimit + 0.1, aLimit if not fail else None) + ] + _checkLimits(uut, tests) + if not fail: + assert uut.getCurrentLimits()[0] == aLimit + assert uut.getCurrentLimits()[1] == bLimit + +def checkDefaultLimits(uut): + tests = [ + TCase(0.0, None), + TCase(0.1, 0.0), + TCase(0.5, 0.0) + ] + _checkLimits(uut, tests) + +def test_oneSetOfLimits(): + song = "/path/to/song" + + uut = ABController() + uut.setCurrentSong(song) + uut.storeLimits(abLimits.a, abLimits.b) + uut.loadLimits(0) + assert uut.getLoadedIndex() == 0 + + checkLimits(uut, abLimits.a, abLimits.b) + assert uut.getStoredLimits(song) == [abLimits] + +def test_multipleSetsOfLimits(): + song = "/path/to/song" + abLimits = [ + AB(0.2, 0.4), + AB(0.3, 0.5), + AB(0.0, 1.2) + ] + + uut = ABController() + uut.setCurrentSong(song) + for l in abLimits: + uut.storeLimits(l.a, l.b) + + for i, l in enumerate(abLimits): + uut.loadLimits(i) + assert uut.getLoadedIndex() == i + checkLimits(uut, l.a, l.b) + + assert uut.getStoredLimits(song) == abLimits + +def test_multipleSongs(): + songs = [ + "/path/to/song", + "/path/to/another/song" + ] + abLimits = [ + AB(0.2, 0.4), + AB(0.3, 0.5) + ] + uut = ABController() + for i, s in enumerate(songs): + uut.storeLimits(abLimits[i].a, abLimits[i].b, s) + + for i, s in enumerate(songs): + uut.setCurrentSong(s) + uut.loadLimits(0) + assert uut.getLoadedIndex() == 0 + + checkLimits(uut, abLimits[i].a, abLimits[i].b) + assert uut.getStoredLimits(s) == [abLimits[i]] + +def test_disableAbRepeat(): + song = "/path/to/song" + + uut = ABController() + uut.setCurrentSong(song) + uut.storeLimits(abLimits.a, abLimits.b) + uut.loadLimits(0) + assert uut.getLoadedIndex() == 0 + + assert uut.isEnabled() + + uut.setEnable(False) + checkLimits(uut, abLimits.a, abLimits.b, fail=True) + assert not uut.isEnabled() + + uut.setEnable(True) + checkLimits(uut, abLimits.a, abLimits.b) + assert uut.isEnabled() + +def test_storeLimitsToSpecificSong(): + song = "/path/to/song" + + uut = ABController() + uut.storeLimits(abLimits.a, abLimits.b, song) + uut.setCurrentSong(song) + uut.loadLimits(0) + assert uut.getLoadedIndex() == 0 + + checkLimits(uut, abLimits.a, abLimits.b) + +def test_storeLimitsWithoutCurrentSong(): + uut = ABController() + uut.storeLimits(abLimits.a, abLimits.b) + uut.loadLimits(0) + assert uut.getLoadedIndex() == None + + checkDefaultLimits(uut) + +def test_storeLimitsToSongWithoutCurrentSong(): + song = "/path/to/song" + uut = ABController() + uut.storeLimits(abLimits.a, abLimits.b, song) + uut.loadLimits(0) + assert uut.getLoadedIndex() == None + + checkDefaultLimits(uut) + + uut.setCurrentSong(song) + + checkDefaultLimits(uut) + + uut.loadLimits(0) + assert uut.getLoadedIndex() == 0 + + checkLimits(uut, abLimits.a, abLimits.b) + +def test_storeLimitsToCurrentSongButDoNotSetCurrentLimits(): + song = "/path/to/song" + uut = ABController() + uut.setCurrentSong(song) + uut.storeLimits(abLimits.a, abLimits.b) + assert uut.getLoadedIndex() == None + + checkDefaultLimits(uut) + + uut.loadLimits(0) + assert uut.getLoadedIndex() == 0 + + checkLimits(uut, abLimits.a, abLimits.b) + +def test_getStoredLimitsOfInexistentSong(): + song = "/path/to/song" + uut = ABController() + assert uut.getStoredLimits(song) == None + +def test_clearAbController(): + songsWithLimits = [ + ("/path/to/song", AB(0.2, 0.4)), + ("/path/to/another/song", AB(0.3, 0.5)) + ] + + uut = ABController() + for s in songsWithLimits: + uut.storeLimits(s[1].a, s[1].b, s[0]) + + for i, s in enumerate(songsWithLimits): + assert uut.getStoredLimits(s[0]) == [s[1]] + + uut.clear() + + for i, s in enumerate(songsWithLimits): + assert uut.getStoredLimits(s[0]) == None + +def test_setTemporaryLimits(): + abLimits = [ + AB(0.2, 0.4), + AB(0.3, 0.5), + AB(0.0, 1.2) + ] + uut = ABController() + + for l in abLimits: + uut.setLimits(l.a, l.b) + assert uut.getLoadedIndex() == None + checkLimits(uut, l.a, l.b) + +def test_setTemporaryLimitsWithCurrentSong(): + songLimits = AB(0.2, 0.4) + abLimits = [ + AB(0.2, 0.4), + AB(0.3, 0.5), + AB(0.0, 1.2) + ] + song = "/path/to/song" + uut = ABController() + uut.setCurrentSong(song) + uut.storeLimits(songLimits.a, songLimits.b) + uut.loadLimits(0) + assert uut.getLoadedIndex() == 0 + + for l in abLimits: + uut.setLimits(l.a, l.b) + checkLimits(uut, l.a, l.b) + +def test_defaultBehaviour(): + uut = ABController() + checkDefaultLimits(uut) + +def test_nextStoredLimit(): + song = "/path/to/song" + abLimits = [ + AB(0.2, 0.4), + AB(0.3, 0.5) + ] + + uut = ABController() + uut.setCurrentSong(song) + for l in abLimits: + uut.storeLimits(l.a, l.b) + + checkDefaultLimits(uut) + + uut.nextStoredAbLimits() + checkLimits(uut, abLimits[0].a, abLimits[0].b) + + uut.nextStoredAbLimits() + checkLimits(uut, abLimits[1].a, abLimits[1].b) + + uut.nextStoredAbLimits() + checkLimits(uut, abLimits[1].a, abLimits[1].b) + +def test_previousStoredLimit(): + song = "/path/to/song" + abLimits = [ + AB(0.2, 0.4), + AB(0.3, 0.5) + ] + + uut = ABController() + uut.setCurrentSong(song) + for l in abLimits: + uut.storeLimits(l.a, l.b) + + checkDefaultLimits(uut) + + uut.previousStoredAbLimits() + checkLimits(uut, abLimits[0].a, abLimits[0].b) + + uut.previousStoredAbLimits() + checkLimits(uut, abLimits[0].a, abLimits[0].b) + + uut.loadLimits(1) + checkLimits(uut, abLimits[1].a, abLimits[1].b) + + uut.previousStoredAbLimits() + checkLimits(uut, abLimits[0].a, abLimits[0].b) 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) + diff --git a/solo-tool-project/test/notifier_unittest.py b/solo-tool-project/test/notifier_unittest.py new file mode 100644 index 0000000..8a6e988 --- /dev/null +++ b/solo-tool-project/test/notifier_unittest.py @@ -0,0 +1,100 @@ +import pytest + +from solo_tool.notifier import Notifier +from player_mock import Player + +@pytest.fixture +def mockPlayer(): + return Player() + +@pytest.fixture +def uut(mockPlayer): + return Notifier(mockPlayer) + +def test_allEvents(uut): + def checkEvent(uut, event): + callbacks = 2 + calledFlags = [False] * callbacks + values = [None] * callbacks + + def createCallback(i): + def cb(value): + nonlocal calledFlags, values + calledFlags[i] = True + values[i] = value + + return cb + + for i in range(0, callbacks): + uut.registerCallback(event, createCallback(i)) + + assert not any(calledFlags) + uut.notify(event, 123) + assert all(calledFlags) + assert values == [123] * callbacks + + checkEvent(uut, Notifier.PLAYING_STATE_EVENT) + checkEvent(uut, Notifier.PLAYBACK_VOLUME_EVENT) + checkEvent(uut, Notifier.PLAYBACK_RATE_EVENT) + checkEvent(uut, Notifier.CURRENT_SONG_EVENT) + checkEvent(uut, Notifier.CURRENT_AB_EVENT) + checkEvent(uut, Notifier.AB_LIMIT_ENABLED_EVENT) + +def test_eventWithoutRegisteredCallbacks(uut): + uut.notify(Notifier.PLAYING_STATE_EVENT, 0) + # expect no crash + +def test_eventsWithMockPlayer(uut, mockPlayer): + def checkEvent(eventCode, simulateEvent, expectedValue): + called = False + receivedValue = None + def callback(value): + nonlocal called, receivedValue + called = True + receivedValue = value + + uut.registerCallback(eventCode, callback) + + assert not called + simulateEvent() + assert called + assert receivedValue == expectedValue + + mockPlayer.state = 1 + mockPlayer.volume = 75 + + checkEvent(Notifier.PLAYING_STATE_EVENT, mockPlayer.simulatePlayingStateChanged, True) + checkEvent(Notifier.PLAYBACK_VOLUME_EVENT, mockPlayer.simulatePlaybackVolumeChanged, 75) + +def test_singleEventNotification(uut): + playingStateCalled = False + def playingStateCallback(value): + nonlocal playingStateCalled + playingStateCalled = True + + volumeCalled = False + def volumeCallback(value): + nonlocal volumeCalled + volumeCalled = True + + uut.registerCallback(Notifier.PLAYING_STATE_EVENT, playingStateCallback) + uut.registerCallback(Notifier.PLAYBACK_VOLUME_EVENT, volumeCallback) + + assert not playingStateCalled + assert not volumeCalled + + uut.notify(Notifier.PLAYING_STATE_EVENT, 0) + assert playingStateCalled + assert not volumeCalled + + playingStateCalled = False + + uut.notify(Notifier.PLAYBACK_VOLUME_EVENT, 0) + assert not playingStateCalled + assert volumeCalled + + volumeCalled = False + + uut.notify(Notifier.PLAYBACK_RATE_EVENT, 0) + assert not playingStateCalled + assert not volumeCalled diff --git a/solo-tool-project/test/player_mock.py b/solo-tool-project/test/player_mock.py new file mode 100644 index 0000000..3162e0f --- /dev/null +++ b/solo-tool-project/test/player_mock.py @@ -0,0 +1,71 @@ +class Player(): + STOPPED = 0 + PLAYING = 1 + PAUSED = 2 + + def __init__(self): + self.state = Player.STOPPED + self.rate = 1.0 + self.position = 0.0 + self.volume = 1.0 + self.currentSong = None + self.playingStateChangedCallback = None + self.playbackVolumeChangedCallback = None + + def play(self): + previousState = self.state + self.state = Player.PLAYING + if previousState != Player.PLAYING: + self.playingStateChangedCallback() + + def stop(self): + previousState = self.state + self.state = Player.STOPPED + if previousState != Player.STOPPED: + self.playingStateChangedCallback() + + def pause(self): + previousState = self.state + self.state = Player.PAUSED + if previousState != Player.PAUSED: + self.playingStateChangedCallback() + + def isPlaying(self): + return self.state == Player.PLAYING + + def setPlaybackRate(self, rate): + self.rate = rate + + def getPlaybackRate(self): + return self.rate + + def setPlaybackPosition(self, position): + self.position = position + + def getPlaybackPosition(self): + return self.position + + def setPlaybackVolume(self, volume): + changed = self.volume != volume + self.volume = volume + if changed: + self.playbackVolumeChangedCallback() + + def getPlaybackVolume(self): + return self.volume + + def setCurrentSong(self, path): + self.stop() + self.currentSong = path + + def setPlayingStateChangedCallback(self, callback): + self.playingStateChangedCallback = callback + + def simulatePlayingStateChanged(self): + self.playingStateChangedCallback() + + def setPlaybackVolumeChangedCallback(self, callback): + self.playbackVolumeChangedCallback = callback + + def simulatePlaybackVolumeChanged(self): + self.playbackVolumeChangedCallback() diff --git a/solo-tool-project/test/playlist_unittest.py b/solo-tool-project/test/playlist_unittest.py new file mode 100644 index 0000000..842ce51 --- /dev/null +++ b/solo-tool-project/test/playlist_unittest.py @@ -0,0 +1,148 @@ +from solo_tool.playlist import Playlist + +def test_addAndSelectOneSong(): + songAddedByUser = "/path/to/song" + songSetByCallback = None + + def testCallback(song): + nonlocal songSetByCallback + songSetByCallback = song + + uut = Playlist(testCallback) + uut.addSong(songAddedByUser) + uut.setCurrentSong(0) + + assert songAddedByUser == songSetByCallback + assert uut.getCurrentSong() == songAddedByUser + assert uut.getCurrentSongIndex() == 0 + assert uut.getSongs() == [songAddedByUser] + +def test_addTwoSongsAndSelectBoth(): + songAddedByUser = ["/path/to/song", "/path/to/second/song"] + songSetByCallback = None + + def testCallback(song): + nonlocal songSetByCallback + songSetByCallback = song + + uut = Playlist(testCallback) + uut.addSong(songAddedByUser[0]) + uut.addSong(songAddedByUser[1]) + assert uut.getSongs() == songAddedByUser + + uut.setCurrentSong(0) + assert songAddedByUser[0] == songSetByCallback + assert uut.getCurrentSong() == songAddedByUser[0] + assert uut.getCurrentSongIndex() == 0 + + uut.setCurrentSong(1) + assert songAddedByUser[1] == songSetByCallback + assert uut.getCurrentSong() == songAddedByUser[1] + assert uut.getCurrentSongIndex() == 1 + +def test_firstAddedSongIsNotSelected(): + songAddedByUser = "/path/to/song" + songSetByCallback = None + + def testCallback(song): + nonlocal songSetByCallback + songSetByCallback = song + + uut = Playlist(testCallback) + uut.addSong(songAddedByUser) + + assert songSetByCallback == None + assert uut.getCurrentSong() == None + assert uut.getCurrentSongIndex() == None + assert uut.getSongs() == [songAddedByUser] + +def test_invalidSongSelection(): + songAddedByUser = "/path/to/song" + songSetByCallback = None + + def testCallback(song): + nonlocal songSetByCallback + songSetByCallback = song + + uut = Playlist(testCallback) + assert songSetByCallback == None + assert uut.getCurrentSong() == None + assert uut.getCurrentSongIndex() == None + + uut.setCurrentSong(10) + assert songSetByCallback == None + assert uut.getCurrentSong() == None + assert uut.getCurrentSongIndex() == None + + uut.addSong(songAddedByUser) + uut.setCurrentSong(10) + assert songSetByCallback == None + assert uut.getCurrentSong() == None + assert uut.getCurrentSongIndex() == None + assert uut.getSongs() == [songAddedByUser] + +def test_clearPlaylist(): + songAddedByUser = ["/path/to/song", "/path/to/second/song"] + + def dummy(index): + pass + + uut = Playlist(dummy) + for s in songAddedByUser: + uut.addSong(s) + uut.setCurrentSong(0) + + assert uut.getSongs() == songAddedByUser + assert uut.getCurrentSong() == songAddedByUser[0] + assert uut.getCurrentSongIndex() == 0 + + uut.clear() + + assert uut.getSongs() == [] + assert uut.getCurrentSong() == None + assert uut.getCurrentSongIndex() == None + +def test_nextSong(): + songAddedByUser = ["/path/to/song", "/path/to/second/song"] + + uut = Playlist(lambda index: None) + for s in songAddedByUser: + uut.addSong(s) + assert uut.getCurrentSong() == None + assert uut.getCurrentSongIndex() == None + + uut.nextSong() + assert uut.getCurrentSong() == songAddedByUser[0] + assert uut.getCurrentSongIndex() == 0 + + uut.nextSong() + assert uut.getCurrentSong() == songAddedByUser[1] + assert uut.getCurrentSongIndex() == 1 + + uut.nextSong() + assert uut.getCurrentSong() == songAddedByUser[1] + assert uut.getCurrentSongIndex() == 1 + +def test_previousSong(): + songAddedByUser = ["/path/to/song", "/path/to/second/song"] + + uut = Playlist(lambda index: None) + for s in songAddedByUser: + uut.addSong(s) + assert uut.getCurrentSong() == None + assert uut.getCurrentSongIndex() == None + + uut.previousSong() + assert uut.getCurrentSong() == songAddedByUser[0] + assert uut.getCurrentSongIndex() == 0 + + uut.previousSong() + assert uut.getCurrentSong() == songAddedByUser[0] + assert uut.getCurrentSongIndex() == 0 + + uut.setCurrentSong(1) + assert uut.getCurrentSong() == songAddedByUser[1] + assert uut.getCurrentSongIndex() == 1 + uut.previousSong() + assert uut.getCurrentSong() == songAddedByUser[0] + assert uut.getCurrentSongIndex() == 0 diff --git a/solo-tool-project/test/session_manager_unittest.py b/solo-tool-project/test/session_manager_unittest.py new file mode 100644 index 0000000..5468880 --- /dev/null +++ b/solo-tool-project/test/session_manager_unittest.py @@ -0,0 +1,163 @@ +from solo_tool.session_manager import SessionManager +from json import loads, dumps + +testSession = [ + { + "path" : "/path/to/another/song", + "ab_limits" : None + }, + { + "path" : "/path/to/song", + "ab_limits" : [ + [0.1, 0.2], + [0.3, 0.4] + ] + }, + { + "path" : "/path/to/something", + "ab_limits" : [ + [0.1, 0.2] + ] + } +] + +class PlaylistMock: + def __init__(self): + self.lastAddedSong = None + self.songs = list() + + def addSong(self, s): + self.songs.append(s) + self.lastAddedSong = s + + def getSongs(self): + return self.songs + + def clear(self): + self.__init__() + +class ABControllerMock: + def __init__(self): + self.limits = dict() + + def storeLimits(self, aLimit, bLimit, song="current"): + if song not in self.limits: + self.limits[song] = list() + self.limits[song].append([aLimit, bLimit]) + + def getStoredLimits(self, song): + return self.limits.get(song) + + def clear(self): + self.__init__() + +class MockFile: + def __init__(self, init=""): + self.contents = init + + def open(self, *args): + pass + + def write(self, s): + self.contents += s + + def read(self): + return self.contents + + +def test_addSongs(): + songs = [ + "/path/to/song", + "/path/to/another/song" + ] + + playlistMock = PlaylistMock() + uut = SessionManager(playlistMock, None) + + for s in songs: + uut.addSong(s) + assert playlistMock.lastAddedSong == s + +def test_addAbLimits(): + abLimits = [ + [0.1, 0.2], + [0.3, 0.4] + ] + + abControllerMock = ABControllerMock() + uut = SessionManager(None, abControllerMock) + + for i, ab in enumerate(abLimits): + uut.storeLimits(ab[0], ab[1]) + assert abControllerMock.limits["current"][i] == ab + +def test_loadSession(): + playlistMock = PlaylistMock() + abControllerMock = ABControllerMock() + uut = SessionManager(playlistMock, abControllerMock) + + sessionFile = MockFile(dumps(testSession)) + uut.loadSession(sessionFile) + + for i, entry in enumerate(testSession): + expectedSong = entry["path"] + expectedLimits = entry["ab_limits"] + loadedSong = playlistMock.songs[i] + loadedLimits = abControllerMock.limits.get(expectedSong) + + assert loadedSong == expectedSong + assert loadedLimits == expectedLimits + +def test_saveSession(): + playlistMock = PlaylistMock() + abControllerMock = ABControllerMock() + uut = SessionManager(playlistMock, abControllerMock) + + for i, entry in enumerate(testSession): + song = entry["path"] + playlistMock.addSong(song) + + abLimits = entry["ab_limits"] + if abLimits is not None: + for l in abLimits: + abControllerMock.storeLimits(l[0], l[1], song) + + sessionFile = MockFile() + uut.saveSession(sessionFile) + + savedSession = loads(sessionFile.read()) + assert savedSession == testSession + +def test_loadAndSaveEmptySession(): + playlistMock = PlaylistMock() + abControllerMock = ABControllerMock() + uut = SessionManager(playlistMock, abControllerMock) + + sessionFile = MockFile() + + uut.saveSession(sessionFile) + assert loads(sessionFile.read()) == list() + + uut.loadSession(sessionFile) + + songs = playlistMock.getSongs() + assert songs == list() + for s in songs: + assert abControllerMock.getStoredLimits(s) == None + +def test_loadSessionNotAdditive(): + playlistMock = PlaylistMock() + abControllerMock = ABControllerMock() + uut = SessionManager(playlistMock, abControllerMock) + + sessionFile = MockFile(dumps(testSession)) + uut.loadSession(sessionFile) + uut.loadSession(sessionFile) + + songs = playlistMock.getSongs() + assert len(songs) == len(set(songs)) + for s in songs: + abLimits = abControllerMock.getStoredLimits(s) + if abLimits is not None: + abLimitStr = [f"[{l[0]}, {l[1]}] " for l in abLimits] + assert len(abLimitStr) == len(set(abLimitStr)) diff --git a/solo-tool-project/test/solo_tool_integrationtest.py b/solo-tool-project/test/solo_tool_integrationtest.py new file mode 100644 index 0000000..5903abf --- /dev/null +++ b/solo-tool-project/test/solo_tool_integrationtest.py @@ -0,0 +1,594 @@ +import pathlib +import shutil +import pytest + +from solo_tool.solo_tool import SoloTool +from player_mock import Player as MockPlayer + +@pytest.fixture +def mockPlayer(): + return MockPlayer() + +@pytest.fixture +def uut(mockPlayer): + return SoloTool(mockPlayer) + +@pytest.fixture +def prepared_tmp_path(tmp_path): + testFiles = [ + "test.flac", + "test.mp3", + "test_session.json" + ] + for f in testFiles: + shutil.copy(pathlib.Path(f), tmp_path) + + return tmp_path + +def checkLimit(uut, mockPlayer, aLimit, bLimit): + mockPlayer.position = bLimit - 0.1 + uut.tick() + assert mockPlayer.position == bLimit - 0.1 + + mockPlayer.position = bLimit + 0.1 + uut.tick() + assert mockPlayer.position == aLimit + +def test_playerControls(uut, mockPlayer): + assert mockPlayer.state == MockPlayer.STOPPED + assert uut.isPlaying() == False + uut.play() + assert mockPlayer.state == MockPlayer.PLAYING + assert uut.isPlaying() == True + uut.pause() + assert mockPlayer.state == MockPlayer.PAUSED + assert uut.isPlaying() == False + uut.stop() + assert mockPlayer.state == MockPlayer.STOPPED + assert uut.isPlaying() == False + + assert mockPlayer.rate == 1.0 + uut.setPlaybackRate(0.5) + assert mockPlayer.rate == 0.5 + + assert mockPlayer.position == 0.0 + uut.setPlaybackPosition(0.5) + assert mockPlayer.position == 0.5 + + assert mockPlayer.volume == 1.0 + uut.setPlaybackVolume(0.5) + assert mockPlayer.volume == 0.5 + +def test_addAndSetSongs(uut, mockPlayer): + songs = [ + "test.flac", + "test.mp3" + ] + + for s in songs: + uut.addSong(s) + assert mockPlayer.currentSong == None + + for i, s in enumerate(songs): + uut.setSong(i) + assert mockPlayer.currentSong == songs[i] + +def test_nextAndPreviousSong(uut, mockPlayer): + songs = [ + "test.flac", + "test.mp3" + ] + + for s in songs: + uut.addSong(s) + assert mockPlayer.currentSong == None + + uut.nextSong() + assert mockPlayer.currentSong == songs[0] + + uut.previousSong() + assert mockPlayer.currentSong == songs[0] + + uut.nextSong() + assert mockPlayer.currentSong == songs[1] + + uut.nextSong() + assert mockPlayer.currentSong == songs[1] + +def test_addAndSetAbLimits(uut, mockPlayer): + song = "test.flac" + abLimits = [ + [0.2, 0.4], + [0.1, 0.3] + ] + + uut.addSong(song) + uut.setSong(0) + + for ab in abLimits: + uut.storeAbLimits(ab[0], ab[1]) + + mockPlayer.position = 0.0 + uut.tick() + assert mockPlayer.position == 0.0 + + mockPlayer.position = 0.5 + uut.tick() + assert mockPlayer.position == 0.5 + + uut.loadAbLimits(0) + + uut.tick() + assert mockPlayer.position == 0.5 + + uut.setAbLimitEnable(True) + + uut.tick() + assert mockPlayer.position == 0.2 + + uut.tick() + assert mockPlayer.position == 0.2 + + uut.loadAbLimits(1) + uut.tick() + assert mockPlayer.position == 0.2 + + mockPlayer.position = 0.8 + uut.tick() + assert mockPlayer.position == 0.1 + +def test_abLimitEnabledGetter(uut): + assert not uut.isAbLimitEnabled() + + uut.setAbLimitEnable(True) + assert uut.isAbLimitEnabled() + + uut.setAbLimitEnable(False) + assert not uut.isAbLimitEnabled() + +def test_multipleSongsAndAbLimits(uut, mockPlayer): + songs = [ + "test.flac", + "test.mp3" + ] + abLimits = [ + [0.2, 0.4], + [0.5, 0.7] + ] + + for s in songs: + uut.addSong(s) + + for i, l in enumerate(abLimits): + uut.setSong(i) + uut.storeAbLimits(l[0], l[1]) + + uut.setAbLimitEnable(True) + + for i, l in enumerate(abLimits): + uut.setSong(i) + uut.loadAbLimits(0) + + mockPlayer.position = l[0] + uut.tick() + assert mockPlayer.position == l[0] + + mockPlayer.position = l[1] + 0.1 + uut.tick() + assert mockPlayer.position == l[0] + +def test_storeAbLimitsWithoutSong(uut, mockPlayer): + song = "test.flac" + abLimit = [0.2, 0.4] + overflow = abLimit[1] + 0.1 + default = 0.0 + mockPlayer.position = overflow + uut.setAbLimitEnable(True) + + uut.storeAbLimits(abLimit[0], abLimit[1]) + uut.tick() + assert mockPlayer.position == default + mockPlayer.position = overflow + + uut.loadAbLimits(0) + uut.tick() + assert mockPlayer.position == default + mockPlayer.position = overflow + + uut.addSong(song) + uut.tick() + assert mockPlayer.position == default + mockPlayer.position = overflow + + uut.loadAbLimits(0) + uut.tick() + assert mockPlayer.position == default + mockPlayer.position = overflow + + uut.setSong(0) + uut.tick() + assert mockPlayer.position == default + mockPlayer.position = overflow + + uut.loadAbLimits(0) + uut.tick() + assert mockPlayer.position == default + mockPlayer.position = overflow + + uut.storeAbLimits(abLimit[0], abLimit[1]) + uut.tick() + assert mockPlayer.position == default + mockPlayer.position = overflow + + uut.loadAbLimits(0) + uut.tick() + assert mockPlayer.position == abLimit[0] + +def test_nextAndPreviousAbLimit(uut, mockPlayer): + song = "test.flac" + abLimits = [ + [0.2, 0.4], + [0.1, 0.3] + ] + + uut.addSong(song) + uut.setSong(0) + uut.setAbLimitEnable(True) + + for ab in abLimits: + uut.storeAbLimits(ab[0], ab[1]) + + checkLimit(uut, mockPlayer, 0.0, 0.0) # default limits + + uut.nextStoredAbLimits() + checkLimit(uut, mockPlayer, abLimits[0][0], abLimits[0][1]) + + uut.nextStoredAbLimits() + checkLimit(uut, mockPlayer, abLimits[1][0], abLimits[1][1]) + + uut.nextStoredAbLimits() + checkLimit(uut, mockPlayer, abLimits[1][0], abLimits[1][1]) + + uut.previousStoredAbLimits() + checkLimit(uut, mockPlayer, abLimits[0][0], abLimits[0][1]) + + uut.previousStoredAbLimits() + checkLimit(uut, mockPlayer, abLimits[0][0], abLimits[0][1]) + +def test_abLimitsWhenChangingSongs(uut, mockPlayer): + songs = [ + "test.flac", + "test.mp3" + ] + abLimits = [ + [0.2, 0.4], + [0.1, 0.3], + [0.7, 0.8] + ] + uut.setAbLimitEnable(True) + + for s in songs: + uut.addSong(s) + + uut.setSong(0) + for ab in abLimits: + uut.storeAbLimits(ab[0], ab[1]) + + uut.setSong(1) + uut.storeAbLimits(abLimits[0][0], abLimits[0][1]) + + uut.setSong(0) + uut.loadAbLimits(len(abLimits) - 1) + checkLimit(uut, mockPlayer, abLimits[-1][0], abLimits[-1][1]) + + uut.setSong(1) + checkLimit(uut, mockPlayer, abLimits[-1][0], abLimits[-1][1]) + + uut.previousStoredAbLimits() + checkLimit(uut, mockPlayer, abLimits[0][0], abLimits[0][1]) + +def test_loadAndSaveSession(prepared_tmp_path): + mockPlayer = MockPlayer() + uut = SoloTool(mockPlayer) + + loadedSessionFile = prepared_tmp_path / "test_session.json" + savedSessionFile = prepared_tmp_path / "test_session_save.json" + + uut.loadSession(loadedSessionFile) + uut.saveSession(savedSessionFile) + + import json + with open(loadedSessionFile, "r") as f: + loadedSession = json.loads(f.read()) + + with open(savedSessionFile, "r") as f: + savedSession = json.loads(f.read()) + + assert loadedSession == savedSession + +def test_addInexistentFile(uut, mockPlayer): + song = "not/a/real/file" + + uut.addSong(song) + uut.setSong(0) + + assert mockPlayer.currentSong == None + +def test_getters(uut, mockPlayer): + song = "test.flac" + abLimit = [0.2, 0.4] + + uut.addSong(song) + uut.setSong(0) + uut.storeAbLimits(abLimit[0], abLimit[1]) + + assert uut.getSongs() == [song] + + limits = uut.getStoredAbLimits() + assert len(limits) == 1 + assert limits[0][0] == abLimit[0] + assert limits[0][1] == abLimit[1] + + mockPlayer.position = 0.8 + assert uut.getPlaybackPosition() == 0.8 + + mockPlayer.volume = 0.8 + assert uut.getPlaybackVolume() == 0.8 + + mockPlayer.rate = 0.5 + assert uut.getPlaybackRate() == 0.5 + +def test_setTemporaryLimits(uut, mockPlayer): + song = "test.flac" + abLimits = [ + [0.2, 0.4], + [0.1, 0.4] + ] + overflow = 0.5 + + uut.setAbLimitEnable(True) + mockPlayer.position = overflow + uut.addSong(song) + uut.setSong(0) + uut.storeAbLimits(abLimits[0][0], abLimits[0][1]) + uut.loadAbLimits(0) + + uut.setAbLimits(abLimits[1][0], abLimits[1][1]) + uut.tick() + assert mockPlayer.position == abLimits[1][0] + +def test_jumpToA(uut, mockPlayer): + abLimits = (0.2, 0.4) + initialPosition = 0.8 + + mockPlayer.position = initialPosition + + uut.jumpToA() + assert mockPlayer.position == 0.0 # default AB controller A limit + + uut.setAbLimits(abLimits[0], abLimits[1]) + uut.jumpToA() + assert mockPlayer.position == abLimits[0] + +def test_playingStateNotification(uut, mockPlayer): + song = "test.flac" + uut.addSong(song) + uut.setSong(0) + + called = False + receivedValue = None + def callback(value): + nonlocal called, receivedValue + called = True + receivedValue = value + + uut.registerPlayingStateCallback(callback) + + assert mockPlayer.state == MockPlayer.STOPPED + assert not called + + uut.play() + assert called + assert receivedValue == True + called = False + uut.play() + assert not called + + uut.pause() + assert called + assert receivedValue == False + called = False + uut.pause() + assert not called + + uut.play() + assert called + assert receivedValue == True + called = False + + uut.stop() + assert called + assert receivedValue == False + called = False + uut.stop() + assert not called + +def test_playbackVolumeNotification(uut, mockPlayer): + song = "test.flac" + uut.addSong(song) + uut.setSong(0) + + called = False + receivedValue = None + def callback(value): + nonlocal called, receivedValue + called = True + receivedValue = value + + uut.registerPlaybackVolumeCallback(callback) + + assert not called + + uut.setPlaybackVolume(0.3) + assert called + assert receivedValue == 0.3 + called = False + + uut.setPlaybackVolume(0.3) + assert not called + +def test_playbackRateNotification(uut, mockPlayer): + song = "test.flac" + uut.addSong(song) + uut.setSong(0) + + called = False + receivedValue = None + def callback(value): + nonlocal called, receivedValue + called = True + receivedValue = value + + uut.registerPlaybackRateCallback(callback) + + assert not called + + uut.setPlaybackRate(0.5) + assert called + assert receivedValue == 0.5 + called = False + + uut.setPlaybackRate(0.5) + assert not called + +def test_currentSongNotification(uut): + called = False + receivedValue = None + def callback(value): + nonlocal called, receivedValue + called = True + receivedValue = value + + uut.registerCurrentSongCallback(callback) + assert not called + + songs = [ + "test.flac", + "test.mp3" + ] + uut.addSong(songs[0]) + assert not called + + uut.setSong(0) + assert called + assert receivedValue == 0 + called = False + + uut.addSong(songs[1]) + assert not called + + uut.setSong(0) + assert not called + + uut.setSong(1) + assert called + assert receivedValue == 1 + called = False + + uut.previousSong() + assert called + assert receivedValue == 0 + called = False + + uut.previousSong() + assert not called + + uut.nextSong() + assert called + assert receivedValue == 1 + called = False + + uut.nextSong() + assert not called + +def test_currentAbNotification(uut): + called = False + receivedValue = None + def callback(value): + nonlocal called, receivedValue + called = True + receivedValue = value + + uut.registerCurrentAbLimitsCallback(callback) + assert not called + + song = "test.flac" + uut.addSong(song) + uut.setSong(0) + + abLimits = [ + (0.2, 0.3), + (0.4, 0.5) + ] + uut.storeAbLimits(abLimits[0][0], abLimits[0][1]) + assert not called + uut.storeAbLimits(abLimits[1][0], abLimits[1][1]) + assert not called + + uut.loadAbLimits(0) + assert called + assert receivedValue == 0 + called = False + + uut.loadAbLimits(0) + assert not called + + uut.loadAbLimits(1) + assert called + assert receivedValue == 1 + called = False + + uut.previousStoredAbLimits() + assert called + assert receivedValue == 0 + called = False + + uut.previousStoredAbLimits() + assert not called + + uut.nextStoredAbLimits() + assert called + assert receivedValue == 1 + called = False + + uut.nextStoredAbLimits() + assert not called + +def test_abLimitEnabledNotification(uut): + called = False + receivedValue = None + def callback(value): + nonlocal called, receivedValue + called = True + receivedValue = value + + uut.registerAbLimitEnabledCallback(callback) + assert not called + + uut.setAbLimitEnable(False) + assert not called + assert receivedValue is None + + uut.setAbLimitEnable(True) + assert called + assert receivedValue == True + called = False + receivedValue = None + + uut.setAbLimitEnable(True) + assert not called + assert receivedValue is None + + uut.setAbLimitEnable(False) + assert called + assert receivedValue == False diff --git a/solo-tool-project/test/test.flac b/solo-tool-project/test/test.flac new file mode 100644 index 0000000..9164735 Binary files /dev/null and b/solo-tool-project/test/test.flac differ diff --git a/solo-tool-project/test/test.mp3 b/solo-tool-project/test/test.mp3 new file mode 100644 index 0000000..3c353b7 Binary files /dev/null and b/solo-tool-project/test/test.mp3 differ diff --git a/solo-tool-project/test/test_session.json b/solo-tool-project/test/test_session.json new file mode 100644 index 0000000..f48b792 --- /dev/null +++ b/solo-tool-project/test/test_session.json @@ -0,0 +1,13 @@ +[ + { + "path" : "test.flac", + "ab_limits" : null + }, + { + "path" : "test.mp3", + "ab_limits" : [ + [0.1, 0.2], + [0.3, 0.4] + ] + } +] -- cgit v1.2.3