diff options
author | Eddy Pedroni <epedroni@pm.me> | 2025-02-25 14:58:30 +0100 |
---|---|---|
committer | Eddy Pedroni <epedroni@pm.me> | 2025-02-25 14:58:30 +0100 |
commit | bb94fb1ab32732a354f7df68bf0a056d233b2b69 (patch) | |
tree | 82607687a76cb1f049d225bc3151c33c54f9d61c /solo-tool-project | |
parent | 62490ac2be04aa3b819222f11389e0549f5909e9 (diff) |
Refactor key point implementation and tests
Diffstat (limited to 'solo-tool-project')
-rw-r--r-- | solo-tool-project/src/solo_tool/notifier.py | 2 | ||||
-rw-r--r-- | solo-tool-project/src/solo_tool/solo_tool.py | 29 | ||||
-rw-r--r-- | solo-tool-project/test/notifier_unittest.py | 2 | ||||
-rw-r--r-- | solo-tool-project/test/solo_tool_integrationtest.py | 157 | ||||
-rw-r--r-- | solo-tool-project/test/solo_tool_keypoints_integrationtest.py | 210 | ||||
-rw-r--r-- | solo-tool-project/test/solo_tool_songs_integrationtest.py | 2 |
6 files changed, 230 insertions, 172 deletions
diff --git a/solo-tool-project/src/solo_tool/notifier.py b/solo-tool-project/src/solo_tool/notifier.py index ac1c736..5b3539c 100644 --- a/solo-tool-project/src/solo_tool/notifier.py +++ b/solo-tool-project/src/solo_tool/notifier.py @@ -5,7 +5,7 @@ class Notifier: CURRENT_SONG_EVENT = 3 SONG_LIST_EVENT = 4 CURRENT_KEY_POINT_EVENT = 5 - KEY_POINTS_EVENT = 6 + KEY_POINT_LIST_EVENT = 6 def __init__(self, player): self._callbacks = dict() diff --git a/solo-tool-project/src/solo_tool/solo_tool.py b/solo-tool-project/src/solo_tool/solo_tool.py index bc20013..0489517 100644 --- a/solo-tool-project/src/solo_tool/solo_tool.py +++ b/solo-tool-project/src/solo_tool/solo_tool.py @@ -13,12 +13,18 @@ class SoloTool: self._keyPoint = None def _updateSong(self, index): + previousSong = self._song self._song = index - path = self._songs[index] - self._player.setCurrentSong(path) - self._keyPoint = 0.0 + self._player.setCurrentSong(self._songs[index]) self._notifier.notify(Notifier.CURRENT_SONG_EVENT, index) - self._notifier.notify(Notifier.CURRENT_KEY_POINT_EVENT, index) + + previousKp = self._keyPoint + self._keyPoint = self.keyPoints[0] if len(self.keyPoints) > 0 else 0.0 + if previousKp != self._keyPoint: + self._notifier.notify(Notifier.CURRENT_KEY_POINT_EVENT, self._keyPoint) + + if previousSong is None or self._keyPoints[previousSong] != self._keyPoints[index]: + self._notifier.notify(Notifier.KEY_POINT_LIST_EVENT, self.keyPoints) @staticmethod def _keyPointValid(kp: float) -> bool: @@ -61,8 +67,8 @@ class SoloTool: def keyPoints(self, new: list[float]) -> None: if new is not None and self._song is not None: sanitized = sorted(list(set([p for p in new if SoloTool._keyPointValid(p)]))) - self._keyPoints[self._song] = sanitized - self._notifier.notify(Notifier.KEY_POINTS_EVENT, sanitized.copy()) + self._keyPoints[self._song] = sanitized.copy() + self._notifier.notify(Notifier.KEY_POINT_LIST_EVENT, self.keyPoints) @property def keyPoint(self) -> float: @@ -125,6 +131,12 @@ class SoloTool: def registerSongListCallback(self, callback): self._notifier.registerCallback(Notifier.SONG_LIST_EVENT, callback) + def registerKeyPointSelectionCallback(self, callback): + self._notifier.registerCallback(Notifier.CURRENT_KEY_POINT_EVENT, callback) + + def registerKeyPointListCallback(self, callback): + self._notifier.registerCallback(Notifier.KEY_POINT_LIST_EVENT, callback) + def registerPlayingStateCallback(self, callback): self._notifier.registerCallback(Notifier.PLAYING_STATE_EVENT, callback) @@ -134,8 +146,3 @@ class SoloTool: def registerRateCallback(self, callback): self._notifier.registerCallback(Notifier.PLAYBACK_RATE_EVENT, callback) - def registerCurrentKeyPointCallback(self, callback): - self._notifier.registerCallback(Notifier.CURRENT_KEY_POINT_EVENT, callback) - - def registerKeyPointsCallback(self, callback): - self._notifier.registerCallback(Notifier.KEY_POINTS_EVENT, callback) diff --git a/solo-tool-project/test/notifier_unittest.py b/solo-tool-project/test/notifier_unittest.py index 51d3e48..4ab6096 100644 --- a/solo-tool-project/test/notifier_unittest.py +++ b/solo-tool-project/test/notifier_unittest.py @@ -38,7 +38,7 @@ def test_allEvents(uut): checkEvent(uut, Notifier.PLAYBACK_RATE_EVENT) checkEvent(uut, Notifier.CURRENT_SONG_EVENT) checkEvent(uut, Notifier.CURRENT_KEY_POINT_EVENT) - checkEvent(uut, Notifier.KEY_POINTS_EVENT) + checkEvent(uut, Notifier.KEY_POINT_LIST_EVENT) def test_eventWithoutRegisteredCallbacks(uut): uut.notify(Notifier.PLAYING_STATE_EVENT, 0) diff --git a/solo-tool-project/test/solo_tool_integrationtest.py b/solo-tool-project/test/solo_tool_integrationtest.py index e63ea3f..fb759b0 100644 --- a/solo-tool-project/test/solo_tool_integrationtest.py +++ b/solo-tool-project/test/solo_tool_integrationtest.py @@ -107,95 +107,6 @@ def test_sanitizePlaybackVolume(uut): uut.volume = 150.0 assert uut.volume == 150.0 -def test_addAndJumpToKeyPoints(uut, mockPlayer): - def checkJump(before, expectedAfter): - mockPlayer.position = before - uut.jump() - assert mockPlayer.position == expectedAfter - - # Key points are None as long as no song is selected - uut.keyPoints = [0.1, 0.2] - uut.keyPoint = 0.5 - assert uut.keyPoints is None - assert uut.keyPoint is None - - uut.addSong("test.flac") - uut.addSong("test.mp3") - - # Once a song is selected, jump to start by default - assert uut.keyPoint == 0.0 - checkJump(0.5, 0.0) - - # By default songs have an empty list of key points - assert uut.keyPoints == [] - - uut.keyPoints = [0.2, 0.4, 0.1, 0.2] - - # Added key points are not automatically selected - assert uut.keyPoint == 0.0 - checkJump(0.1, 0.0) - - # Any key point can be selected - uut.keyPoint = uut.keyPoints[0] - checkJump(0.0, uut.keyPoints[0]) - - uut.keyPoint = 0.5 - checkJump(0.0, 0.5) - -def test_sanitizeKeyPoint(uut): - song = "test.flac" - uut.addSong(song) - uut.song = 0 - uut.keyPoints = [0.2, 0.4, 0.1, 0.2, None, -0.5, 1.0, 1.5] - - # Added key points are automatically de-duplicated, sanitized and sorted to ascending order - assert uut.keyPoints == [0.1, 0.2, 0.4] - - # Key point and key point list cannot be none - uut.keyPoint = 0.5 - - uut.keyPoint = None - assert uut.keyPoint == 0.5 - - uut.keyPoints = None - assert uut.keyPoints == [0.1, 0.2, 0.4] - - # Valid key points are in [0, 1) - uut.keyPoint = -0.1 - assert uut.keyPoint == 0.5 - - uut.keyPoint = 1.0 - assert uut.keyPoint == 0.5 - - uut.keyPoint = 0.999 - assert uut.keyPoint == 0.999 - -def test_keyPointsPerSong(uut, mockPlayer): - songs = [ - ("test.flac", [0.0, 0.5]), - ("test.mp3", [0.1]) - ] - - # Key points list is set for the selected song - for i, (song, keyPoints) in enumerate(songs): - uut.addSong(song) - uut.song = i - uut.keyPoints = keyPoints - - # Key points list is automatically loaded when the song selection changes - # Active key point is always reset to 0 when song selection changes - for i, (song, keyPoints) in enumerate(songs): - uut.keyPoint = 0.5 - uut.song = i - assert uut.keyPoints == keyPoints - assert uut.keyPoint == 0.0 - - # Key points are copied, not stored by reference - for i, (song, keyPoints) in enumerate(songs): - uut.song = i - keyPoints.append(1.0) - assert 1.0 not in uut.keyPoints - def test_playingStateNotification(uut, mockPlayer): song = "test.flac" uut.addSong(song) @@ -288,71 +199,3 @@ def test_playbackRateNotification(uut, mockPlayer): assert not called -def test_currentKeyPointNotification(uut): - called = False - receivedValue = None - def callback(value): - nonlocal called, receivedValue - called = True - receivedValue = value - - uut.registerCurrentKeyPointCallback(callback) - assert not called - - song = "test.flac" - uut.addSong(song) - uut.song = 0 - - # Selecting a song for the first time sets the key point to 0.0 - assert called - assert receivedValue == 0.0 - called = False - - # Changing the key point triggers a notification - uut.keyPoint = 0.5 - assert called - assert receivedValue == 0.5 - called = False - - # Adding list of key points does not trigger a notification - uut.keyPoints = [0.2, 0.4] - assert not called - - # Assigning the same key point again does not trigger a notification - uut.keyPoint = 0.5 - assert not called - -def test_keyPointsNotification(uut): - called = False - receivedValue = None - def callback(value): - nonlocal called, receivedValue - called = True - receivedValue = value - - uut.registerKeyPointsCallback(callback) - assert not called - - song = "test.flac" - uut.addSong(song) - uut.song = 0 - assert not called - - # Adding list of key points triggers a notification - uut.keyPoints = [0.2, 0.4] - assert called - assert receivedValue == [0.2, 0.4] - called = False - - # Same list does trigger a notification again - uut.keyPoints = [0.2, 0.4] - assert called - assert receivedValue == [0.2, 0.4] - called = False - - # Incrementing list of key points triggers a notification after sanitization - uut.keyPoints += [0.2, None, 0.1] - assert called - assert receivedValue == [0.1, 0.2, 0.4] - called = False - diff --git a/solo-tool-project/test/solo_tool_keypoints_integrationtest.py b/solo-tool-project/test/solo_tool_keypoints_integrationtest.py new file mode 100644 index 0000000..a0a8663 --- /dev/null +++ b/solo-tool-project/test/solo_tool_keypoints_integrationtest.py @@ -0,0 +1,210 @@ +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 testSongs(): + return [ + "test.flac", + "test.mp3" + ] + +def test_keyPointAndSongSelection(uut, mockPlayer, testSongs): + def checkJump(before, expectedAfter): + mockPlayer.position = before + uut.jump() + assert mockPlayer.position == expectedAfter + + # Key point is initially unset + assert uut.keyPoint is None + + # If no song is selected, setting the key point has no effect + assert uut.song is None + uut.keyPoint = 0.5 + assert uut.keyPoint is None + + # With a song selected, key point can be set and jumping works + uut.addSong(testSongs[0]) + uut.keyPoints = [0.3, 0.5] + + uut.keyPoint = 0.6 + assert uut.keyPoint == 0.6 + checkJump(0.8, 0.6) + + # When another song is selected, the key point is set to 0.0 + uut.addSong(testSongs[1]) + uut.song = 1 + assert uut.keyPoint == 0.0 + checkJump(0.5, 0.0) + + # If the selected song has stored key points, the key point is set to the first one instead + uut.song = 0 + assert uut.keyPoint == 0.3 + checkJump(0.5, 0.3) + +def test_keyPointListAndSongSelection(uut, testSongs): + # Key point list is initially unset, since no song is selected + assert uut.keyPoint is None + + # If no song is selected, setting the key point list has no effect + assert uut.song is None + uut.keyPoints = [0.5] + assert uut.keyPoints is None + + # When a song is added, key point list is initialized to empty + uut.addSong(testSongs[0]) + assert uut.keyPoints == [] + + # A new list can be assigned to the song, but it does not affect the current key point + uut.keyPoints = [0.1, 0.3] + assert uut.keyPoints == [0.1, 0.3] + assert uut.keyPoint == 0.0 + + # Each song has its own list of key points + uut.addSong(testSongs[1]) + uut.song = 1 + uut.keyPoints = [0.4] + + uut.song = 0 + assert uut.keyPoints == [0.1, 0.3] + uut.song = 1 + assert uut.keyPoints == [0.4] + +def test_keyPointEdgeCases(uut, testSongs): + uut.addSong(testSongs[0]) + + # Key point cannot be unset + uut.keyPoint = None + assert uut.keyPoint == 0.0 + + # Valid key points are in [0, 1) + uut.keyPoint = -0.1 + assert uut.keyPoint == 0.0 + + uut.keyPoint = 1.0 + assert uut.keyPoint == 0.0 + + uut.keyPoint = 0.999 + assert uut.keyPoint == 0.999 + +def test_keyPointListEdgeCases(uut, testSongs): + uut.addSong(testSongs[0]) + + # Key point list cannot be unset + uut.keyPoints = None + assert uut.keyPoints == [] + + # Appending to the list has no effect + uut.keyPoints.append(0.5) + assert uut.keyPoints == [] + + # Added key points are automatically de-duplicated, sanitized and sorted to ascending order + uut.keyPoints = [0.2, 0.4, 0.1, 0.2, None, -0.5, 1.0, 1.5] + assert uut.keyPoints == [0.1, 0.2, 0.4] + +def test_keyPointSelectionNotification(uut, testSongs): + called = False + receivedValue = None + def callback(value): + nonlocal called, receivedValue + called = True + receivedValue = value + + uut.registerKeyPointSelectionCallback(callback) + assert not called + + # Selecting a song for the first time sets the key point to 0.0 + uut.addSong(testSongs[0]) + assert called + assert receivedValue == 0.0 + called = False + + # Changing the key point triggers a notification + uut.keyPoint = 0.5 + assert called + assert receivedValue == 0.5 + called = False + + # Adding list of key points does not trigger a notification + uut.keyPoints = [0.2, 0.4] + assert not called + + # Assigning the same key point again does not trigger a notification + uut.keyPoint = 0.5 + assert not called + + # Changing song triggers the notification + uut.addSong(testSongs[1]) + uut.song = 1 + assert called + assert receivedValue == 0.0 + called = False + + # But only if the key point really changes + uut.keyPoint = 0.2 + assert called + assert receivedValue == 0.2 + called = False + + uut.song = 0 + assert not called + +def test_keyPointListNotification(uut, testSongs): + called = False + receivedValue = None + def callback(value): + nonlocal called, receivedValue + called = True + receivedValue = value + + uut.registerKeyPointListCallback(callback) + assert not called + + # Adding the first song triggers since the list is now not None + uut.addSong(testSongs[0]) + assert called + assert receivedValue == [] + called = False + + # Adding list of key points triggers + uut.keyPoints = [0.2, 0.4] + assert called + assert receivedValue == [0.2, 0.4] + called = False + + # Same list does not trigger + uut.keyPoints = [0.2, 0.4] + assert called + assert receivedValue == [0.2, 0.4] + called = False + + # Incrementing list of key points triggers after sanitization + uut.keyPoints += [0.2, None, 0.1] + assert called + assert receivedValue == [0.1, 0.2, 0.4] + called = False + + # Changing song triggers + uut.addSong(testSongs[1]) + uut.song = 1 + assert called + assert receivedValue == [] + called = False + + # But only if the list really changed + uut.keyPoints = [0.1, 0.2, 0.4] + assert called + assert receivedValue == [0.1, 0.2, 0.4] + called = False + + uut.song = 0 + assert not called diff --git a/solo-tool-project/test/solo_tool_songs_integrationtest.py b/solo-tool-project/test/solo_tool_songs_integrationtest.py index 3d9368b..092ea93 100644 --- a/solo-tool-project/test/solo_tool_songs_integrationtest.py +++ b/solo-tool-project/test/solo_tool_songs_integrationtest.py @@ -1,5 +1,3 @@ -import pathlib -import shutil import pytest from solo_tool.solo_tool import SoloTool |