🐛 | Fix SonarQube issues across codebase
All checks were successful
SonarQube Code Quality Scan / SonarQube Scan (push) Successful in 1m41s
All checks were successful
SonarQube Code Quality Scan / SonarQube Scan (push) Successful in 1m41s
- Add docstrings to all empty Qt stub methods (S1186) - Fix BLOCKER S1845: rename playbackState to get_playback_state - Rename enum fields to snake_case with CamelCase aliases (S116) - Rename stub methods to snake_case (set_source, set_position, etc.) (S100) - Rename unused timeout param to msecs (S1172) - Remove unused state variables in tests (S1481) - Replace 'is' with '==' for enum comparisons (S5795) - Remove unnecessary list() calls (S7504) - Rename QTh to qt_cls (S117) - Remove commented-out code blocks (S125) - Add docstring to FallbackServiceInterface.emit_properties_changed (S1186)
This commit is contained in:
2
mpris.py
2
mpris.py
@@ -52,7 +52,7 @@ except ImportError:
|
|||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
self._interface_name = name
|
self._interface_name = name
|
||||||
def emit_properties_changed(self, changed, invalidated) -> None:
|
def emit_properties_changed(self, changed, invalidated) -> None:
|
||||||
pass
|
"""Stub — no-op; real implementation emits D-Bus PropertiesChanged."""
|
||||||
|
|
||||||
ServiceInterface = _FallbackServiceInterface # type: ignore[assignment,misc]
|
ServiceInterface = _FallbackServiceInterface # type: ignore[assignment,misc]
|
||||||
dbus_method = lambda **kw: lambda f: f
|
dbus_method = lambda **kw: lambda f: f
|
||||||
|
|||||||
103
player.py
103
player.py
@@ -61,9 +61,13 @@ except (ImportError, OSError):
|
|||||||
|
|
||||||
class _StubQThread:
|
class _StubQThread:
|
||||||
"""Stub replacement for PySide6.QtCore.QThread."""
|
"""Stub replacement for PySide6.QtCore.QThread."""
|
||||||
def start(self) -> None: pass
|
def start(self) -> None:
|
||||||
def quit(self) -> None: pass
|
"""Stub — no-op; real QThread would start the event loop."""
|
||||||
def wait(self, timeout: int = 3000) -> bool: return True
|
def quit(self) -> None:
|
||||||
|
"""Stub — no-op; real QThread would exit the event loop."""
|
||||||
|
def wait(self, msecs: int = 3000) -> bool:
|
||||||
|
"""Stub — returns True immediately without waiting."""
|
||||||
|
return True
|
||||||
QThread = _StubQThread
|
QThread = _StubQThread
|
||||||
|
|
||||||
Signal = lambda *a: lambda f: f
|
Signal = lambda *a: lambda f: f
|
||||||
@@ -71,47 +75,84 @@ except (ImportError, OSError):
|
|||||||
QUrl = None
|
QUrl = None
|
||||||
|
|
||||||
class _StubPlaybackState:
|
class _StubPlaybackState:
|
||||||
StoppedState = 0
|
"""Stub mirroring QMediaPlayer.PlaybackState enum values."""
|
||||||
PlayingState = 1
|
stopped_state = 0
|
||||||
PausedState = 2
|
playing_state = 1
|
||||||
|
paused_state = 2
|
||||||
|
# Backward-compatible aliases for code that uses Qt CamelCase names.
|
||||||
|
StoppedState = stopped_state
|
||||||
|
PlayingState = playing_state
|
||||||
|
PausedState = paused_state
|
||||||
|
|
||||||
class _StubMediaStatus:
|
class _StubMediaStatus:
|
||||||
NoMedia = 0
|
"""Stub mirroring QMediaPlayer.MediaStatus enum values."""
|
||||||
LoadingMedia = 1
|
no_media = 0
|
||||||
LoadedMedia = 2
|
loading_media = 1
|
||||||
StalledMedia = 3
|
loaded_media = 2
|
||||||
BufferingMedia = 4
|
stalled_media = 3
|
||||||
BufferedMedia = 5
|
buffering_media = 4
|
||||||
EndOfMedia = 6
|
buffered_media = 5
|
||||||
InvalidMedia = 7
|
end_of_media = 6
|
||||||
|
invalid_media = 7
|
||||||
|
# Backward-compatible aliases for code that uses Qt CamelCase names.
|
||||||
|
NoMedia = no_media
|
||||||
|
LoadingMedia = loading_media
|
||||||
|
LoadedMedia = loaded_media
|
||||||
|
StalledMedia = stalled_media
|
||||||
|
BufferingMedia = buffering_media
|
||||||
|
BufferedMedia = buffered_media
|
||||||
|
EndOfMedia = end_of_media
|
||||||
|
InvalidMedia = invalid_media
|
||||||
|
|
||||||
class _StubError:
|
class _StubError:
|
||||||
NoError = 0
|
"""Stub mirroring QMediaPlayer.Error enum values."""
|
||||||
ResourceError = 1
|
no_error = 0
|
||||||
FormatError = 2
|
resource_error = 1
|
||||||
NetworkError = 3
|
format_error = 2
|
||||||
AccessDeniedError = 4
|
network_error = 3
|
||||||
ServiceMissingError = 5
|
access_denied_error = 4
|
||||||
|
service_missing_error = 5
|
||||||
|
# Backward-compatible aliases for code that uses Qt CamelCase names.
|
||||||
|
NoError = no_error
|
||||||
|
ResourceError = resource_error
|
||||||
|
FormatError = format_error
|
||||||
|
NetworkError = network_error
|
||||||
|
AccessDeniedError = access_denied_error
|
||||||
|
ServiceMissingError = service_missing_error
|
||||||
|
|
||||||
class _StubQMediaPlayer:
|
class _StubQMediaPlayer:
|
||||||
|
"""Stub replacement for PySide6.QtMultimedia.QMediaPlayer."""
|
||||||
PlaybackState = _StubPlaybackState
|
PlaybackState = _StubPlaybackState
|
||||||
MediaStatus = _StubMediaStatus
|
MediaStatus = _StubMediaStatus
|
||||||
Error = _StubError
|
Error = _StubError
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._playback_state = _StubPlaybackState.StoppedState
|
self._playback_state = _StubPlaybackState.stopped_state
|
||||||
|
|
||||||
def setSource(self, url) -> None: pass
|
def set_source(self, url) -> None:
|
||||||
def play(self) -> None: pass
|
"""Stub — no-op; real QMediaPlayer would load media from *url*."""
|
||||||
def pause(self) -> None: pass
|
def play(self) -> None:
|
||||||
def stop(self) -> None: pass
|
"""Stub — no-op; real QMediaPlayer would start playback."""
|
||||||
def setPosition(self, ms: int) -> None: pass
|
def pause(self) -> None:
|
||||||
def position(self) -> int: return 0
|
"""Stub — no-op; real QMediaPlayer would pause playback."""
|
||||||
def duration(self) -> int: return 0
|
def stop(self) -> None:
|
||||||
def playbackState(self) -> int:
|
"""Stub — no-op; real QMediaPlayer would stop playback."""
|
||||||
|
def set_position(self, ms: int) -> None:
|
||||||
|
"""Stub — no-op; real QMediaPlayer would seek to *ms*."""
|
||||||
|
def position(self) -> int:
|
||||||
|
"""Stub — returns 0; real QMediaPlayer returns current position in ms."""
|
||||||
|
return 0
|
||||||
|
def duration(self) -> int:
|
||||||
|
"""Stub — returns 0; real QMediaPlayer returns media duration in ms."""
|
||||||
|
return 0
|
||||||
|
def get_playback_state(self) -> int:
|
||||||
|
"""Stub — returns the current playback state value."""
|
||||||
return self._playback_state
|
return self._playback_state
|
||||||
def audioOutput(self): return None
|
def get_audio_output(self):
|
||||||
def setAudioOutput(self, output) -> None: pass
|
"""Stub — returns None; real QMediaPlayer returns its QAudioOutput."""
|
||||||
|
return None
|
||||||
|
def set_audio_output(self, output) -> None:
|
||||||
|
"""Stub — no-op; real QMediaPlayer would set its audio output."""
|
||||||
|
|
||||||
QMediaPlayer = _StubQMediaPlayer
|
QMediaPlayer = _StubQMediaPlayer
|
||||||
QAudioOutput = None
|
QAudioOutput = None
|
||||||
|
|||||||
@@ -72,11 +72,6 @@ def visualizer_mock():
|
|||||||
return mock.MagicMock(name="visualizer")
|
return mock.MagicMock(name="visualizer")
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
# AudioVisualizer
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
|
|
||||||
class TestAudioVisualizer:
|
class TestAudioVisualizer:
|
||||||
|
|
||||||
def test_reset_activity(self, app):
|
def test_reset_activity(self, app):
|
||||||
@@ -90,11 +85,6 @@ class TestAudioVisualizer:
|
|||||||
_cleanup(viz, app)
|
_cleanup(viz, app)
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
# PlaybackBar
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
|
|
||||||
class TestPlaybackBar:
|
class TestPlaybackBar:
|
||||||
|
|
||||||
def test_clear_seek_flag(self, app, player_mock, visualizer_mock):
|
def test_clear_seek_flag(self, app, player_mock, visualizer_mock):
|
||||||
@@ -154,11 +144,6 @@ class TestPlaybackBar:
|
|||||||
_cleanup(bar, app)
|
_cleanup(bar, app)
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
# SearchPage
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
|
|
||||||
class TestSearchPage:
|
class TestSearchPage:
|
||||||
|
|
||||||
def test_on_show_more_clicked_from_button(self, app):
|
def test_on_show_more_clicked_from_button(self, app):
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import pytest
|
|||||||
def _clean_pyside_modules():
|
def _clean_pyside_modules():
|
||||||
"""Remove PySide6 and target modules from sys.modules."""
|
"""Remove PySide6 and target modules from sys.modules."""
|
||||||
keys_to_del = []
|
keys_to_del = []
|
||||||
for key in list(sys.modules.keys()):
|
for key in sys.modules:
|
||||||
if key.startswith("PySide6") or key in ("player", "gui", "discord_rpc"):
|
if key.startswith("PySide6") or key in ("player", "gui", "discord_rpc"):
|
||||||
keys_to_del.append(key)
|
keys_to_del.append(key)
|
||||||
for key in keys_to_del:
|
for key in keys_to_del:
|
||||||
@@ -73,7 +73,7 @@ class TestPlayerQtFallback:
|
|||||||
sys.modules.pop("player", None)
|
sys.modules.pop("player", None)
|
||||||
# PySide6 modules may have been removed, restore them by clearing
|
# PySide6 modules may have been removed, restore them by clearing
|
||||||
# the cached import so that a fresh import re-imports the real lib.
|
# the cached import so that a fresh import re-imports the real lib.
|
||||||
for key in list(sys.modules.keys()):
|
for key in sys.modules:
|
||||||
if key.startswith("PySide6") and key not in _ORIGINAL_PYSIDE_MODS:
|
if key.startswith("PySide6") and key not in _ORIGINAL_PYSIDE_MODS:
|
||||||
del sys.modules[key]
|
del sys.modules[key]
|
||||||
|
|
||||||
@@ -145,8 +145,8 @@ class TestPlayerQtFallback:
|
|||||||
assert callable(player.QThread.quit)
|
assert callable(player.QThread.quit)
|
||||||
assert callable(player.QThread.wait)
|
assert callable(player.QThread.wait)
|
||||||
# Quick functional check
|
# Quick functional check
|
||||||
QTh = player.QThread
|
qt_cls = player.QThread
|
||||||
inst = QTh()
|
inst = qt_cls()
|
||||||
inst.start() # should not raise
|
inst.start() # should not raise
|
||||||
inst.quit() # should not raise
|
inst.quit() # should not raise
|
||||||
assert inst.wait() is True
|
assert inst.wait() is True
|
||||||
@@ -212,7 +212,7 @@ class TestGuiQtFallback:
|
|||||||
"""Remove stale fallback modules so subsequent tests get the real ones."""
|
"""Remove stale fallback modules so subsequent tests get the real ones."""
|
||||||
for mod_name in ("gui", "player", "discord_rpc"):
|
for mod_name in ("gui", "player", "discord_rpc"):
|
||||||
sys.modules.pop(mod_name, None)
|
sys.modules.pop(mod_name, None)
|
||||||
for key in list(sys.modules.keys()):
|
for key in sys.modules:
|
||||||
if key.startswith("PySide6") and key not in _ORIGINAL_PYSIDE_MODS:
|
if key.startswith("PySide6") and key not in _ORIGINAL_PYSIDE_MODS:
|
||||||
del sys.modules[key]
|
del sys.modules[key]
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,6 @@ from unittest import mock
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
# Helpers
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
def _reload_main():
|
def _reload_main():
|
||||||
"""Re-import the ``main`` module with a fresh module object.
|
"""Re-import the ``main`` module with a fresh module object.
|
||||||
|
|
||||||
@@ -113,13 +109,13 @@ class TestModuleConstants:
|
|||||||
"""``_LOG_LEVEL`` is ``logging.INFO`` when not verbose."""
|
"""``_LOG_LEVEL`` is ``logging.INFO`` when not verbose."""
|
||||||
with mock.patch.dict("os.environ", {}, clear=True):
|
with mock.patch.dict("os.environ", {}, clear=True):
|
||||||
main_mod = _reload_main()
|
main_mod = _reload_main()
|
||||||
assert main_mod._LOG_LEVEL is logging.INFO
|
assert main_mod._LOG_LEVEL == logging.INFO
|
||||||
|
|
||||||
def test_log_level_debug_when_verbose(self):
|
def test_log_level_debug_when_verbose(self):
|
||||||
"""``_LOG_LEVEL`` is ``logging.DEBUG`` when verbose."""
|
"""``_LOG_LEVEL`` is ``logging.DEBUG`` when verbose."""
|
||||||
with mock.patch.dict("os.environ", {"TUNETTI_VERBOSE": "1"}, clear=True):
|
with mock.patch.dict("os.environ", {"TUNETTI_VERBOSE": "1"}, clear=True):
|
||||||
main_mod = _reload_main()
|
main_mod = _reload_main()
|
||||||
assert main_mod._LOG_LEVEL is logging.DEBUG
|
assert main_mod._LOG_LEVEL == logging.DEBUG
|
||||||
|
|
||||||
# ── _LOG_FORMAT ──────────────────────────────────────────────────────
|
# ── _LOG_FORMAT ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class TestMprisPlayerInterface:
|
|||||||
assert iface.CanControl is True
|
assert iface.CanControl is True
|
||||||
|
|
||||||
def test_methods_emit_via_bridge(self):
|
def test_methods_emit_via_bridge(self):
|
||||||
state, bridge, iface = self._make()
|
_, bridge, iface = self._make()
|
||||||
for method_name, signal_name in [
|
for method_name, signal_name in [
|
||||||
("Play", "play_requested"),
|
("Play", "play_requested"),
|
||||||
("Pause", "pause_requested"),
|
("Pause", "pause_requested"),
|
||||||
@@ -231,7 +231,7 @@ class TestMprisPlayerInterface:
|
|||||||
mock_sig.emit.assert_called_once()
|
mock_sig.emit.assert_called_once()
|
||||||
|
|
||||||
def test_seek_emits_with_offset(self):
|
def test_seek_emits_with_offset(self):
|
||||||
state, bridge, iface = self._make()
|
_, bridge, iface = self._make()
|
||||||
with mock.patch.object(bridge, "seek_requested") as mock_sig:
|
with mock.patch.object(bridge, "seek_requested") as mock_sig:
|
||||||
iface.Seek(5000)
|
iface.Seek(5000)
|
||||||
mock_sig.emit.assert_called_once_with(5000)
|
mock_sig.emit.assert_called_once_with(5000)
|
||||||
|
|||||||
Reference in New Issue
Block a user