Compare commits

...

6 Commits

Author SHA1 Message Date
Keith Edmunds
324dd770df test_ui tests pass 2025-04-13 12:59:26 +01:00
Keith Edmunds
11400536b5 test_file_importer tests pass 2025-04-13 12:22:41 +01:00
Keith Edmunds
d596792375 All test_queries tests pass 2025-04-13 10:29:54 +01:00
Keith Edmunds
a8791f925d All test_playlistmodel tests pass 2025-04-13 10:17:58 +01:00
Keith Edmunds
5317ecdf18 Remove sessions from test_misc.py 2025-04-13 09:21:36 +01:00
Keith Edmunds
a2baf489c3 Remove sessions from test_playlistmodel.py 2025-04-13 09:16:49 +01:00
14 changed files with 164 additions and 900 deletions

View File

@ -459,16 +459,17 @@ def track_update(
with db.Session() as session: with db.Session() as session:
track = session.get(Tracks, track_id) track = session.get(Tracks, track_id)
if not track: if not track:
raise ApplicationError(f"Can't retrieve Track ({track_id=})") raise ApplicationError(f"Can't retrieve Track ({track_id=})")
track.path = str(metadata["path"]) track.path = str(metadata["path"])
track.title = str(metadata["title"]), track.title = str(metadata["title"])
track.artist = str(metadata["artist"]), track.artist = str(metadata["artist"])
track.duration = int(metadata["duration"]), track.duration = int(metadata["duration"])
track.start_gap = int(metadata["start_gap"]), track.start_gap = int(metadata["start_gap"])
track.fade_at = int(metadata["fade_at"]), track.fade_at = int(metadata["fade_at"])
track.silence_at = int(metadata["silence_at"]), track.silence_at = int(metadata["silence_at"])
track.bitrate = int(metadata["bitrate"]), track.bitrate = int(metadata["bitrate"])
session.commit() session.commit()
@ -837,7 +838,7 @@ def playlist_save_as_template(playlist_id: int, template_name: str) -> None:
new_template = playlist_create(template_name, 0, as_template=True) new_template = playlist_create(template_name, 0, as_template=True)
playlist_copy(playlist_id, new_template.id) playlist_copy(playlist_id, new_template.playlist_id)
def playlists_templates_all() -> list[PlaylistDTO]: def playlists_templates_all() -> list[PlaylistDTO]:
@ -853,14 +854,17 @@ def playlists_template_by_id(playlist_id: int) -> PlaylistDTO | None:
Return a list of closed playlists Return a list of closed playlists
""" """
playlist_list = _playlists_where( playlist_list = _playlists_where(Playlists.playlist_id == playlist_id)
Playlists.playlist_id == playlist_id, Playlists.is_template.is_(True)
)
if not playlist_list: if not playlist_list:
return None return None
if len(playlist_list) > 1: if len(playlist_list) > 1:
raise ApplicationError(f"Duplicate {playlist_id=}") raise ApplicationError(f"Duplicate {playlist_id=}")
return playlist_list[0] template = playlist_list[0]
if template.is_template is False:
raise ApplicationError(f"Playlist {playlist_id=} is not a template")
return template
# @log_call # @log_call
@ -1051,13 +1055,13 @@ def playdates_get_last(track_id: int, limit: int = 5) -> str:
) )
def playdates_update(track_id: int) -> None: def playdates_update(track_id: int, when: dt.datetime | None = None) -> None:
""" """
Update playdates for passed track Update playdates for passed track
""" """
with db.Session() as session: with db.Session() as session:
_ = Playdates(session, track_id) _ = Playdates(session, track_id, when)
def playdates_between_dates( def playdates_between_dates(

View File

@ -142,7 +142,7 @@ class FileImporter:
# Refresh list of existing tracks as they may have been updated # Refresh list of existing tracks as they may have been updated
# by previous imports # by previous imports
self.existing_tracks = ds.get_all_tracks() self.existing_tracks = ds.tracks_all()
for infile in [ for infile in [
os.path.join(Config.REPLACE_FILES_DEFAULT_SOURCE, f) os.path.join(Config.REPLACE_FILES_DEFAULT_SOURCE, f)
@ -638,7 +638,7 @@ class DoTrackImport(QThread):
# Update databse # Update databse
metadata = get_all_track_metadata(self.destination_track_path) metadata = get_all_track_metadata(self.destination_track_path)
if self.track_id == 0: if self.track_id == 0:
track_dto = ds.create_track(self.destination_track_path, metadata) track_dto = ds.track_create(metadata)
else: else:
track_dto = ds.track_update( track_dto = ds.track_update(
self.destination_track_path, self.track_id, metadata self.destination_track_path, self.track_id, metadata

View File

@ -153,7 +153,7 @@ class NoteColours(dbtables.NoteColoursTable):
class Playdates(dbtables.PlaydatesTable): class Playdates(dbtables.PlaydatesTable):
def __init__( def __init__(
self, session: Session, track_id: int, when: Optional[dt.datetime] = None self, session: Session, track_id: int, when: dt.datetime | None = None
) -> None: ) -> None:
"""Record that track was played""" """Record that track was played"""

View File

@ -76,7 +76,6 @@ from dialogs import TrackInsertDialog
from file_importer import FileImporter from file_importer import FileImporter
from helpers import ask_yes_no, file_is_unreadable, get_name from helpers import ask_yes_no, file_is_unreadable, get_name
from log import log, log_call from log import log, log_call
from models import db, Playdates, PlaylistRows, Playlists, Queries, Settings, Tracks
from playlistrow import PlaylistRow, TrackSequence from playlistrow import PlaylistRow, TrackSequence
from playlistmodel import PlaylistModel, PlaylistProxyModel from playlistmodel import PlaylistModel, PlaylistProxyModel
from playlists import PlaylistTab from playlists import PlaylistTab
@ -899,7 +898,7 @@ class QueryDialog(QDialog):
querylist_y=self.y(), querylist_y=self.y(),
) )
for name, value in attributes_to_save.items(): for name, value in attributes_to_save.items():
ds.set_setting(name, value) ds.setting_set(name, value)
header = self.table_view.horizontalHeader() header = self.table_view.horizontalHeader()
if header is None: if header is None:
@ -909,7 +908,7 @@ class QueryDialog(QDialog):
return return
for column_number in range(column_count - 1): for column_number in range(column_count - 1):
attr_name = f"querylist_col_{column_number}_width" attr_name = f"querylist_col_{column_number}_width"
ds.set_setting( ds.setting_set(
attr_name, self.table_view.columnWidth(column_number) attr_name, self.table_view.columnWidth(column_number)
) )
@ -962,10 +961,10 @@ class QueryDialog(QDialog):
def set_window_size(self) -> None: def set_window_size(self) -> None:
"""Set window sizes""" """Set window sizes"""
x = ds.get_setting("querylist_x") or 100 x = ds.setting_get("querylist_x") or 100
y = ds.get_setting("querylist_y") or 100 y = ds.setting_get("querylist_y") or 100
width = ds.get_setting("querylist_width") or 100 width = ds.setting_get("querylist_width") or 100
height = ds.get_setting("querylist_height") or 100 height = ds.setting_get("querylist_height") or 100
self.setGeometry(x, y, width, height) self.setGeometry(x, y, width, height)
def set_column_sizes(self) -> None: def set_column_sizes(self) -> None:
@ -981,7 +980,7 @@ class QueryDialog(QDialog):
# Last column is set to stretch so ignore it here # Last column is set to stretch so ignore it here
for column_number in range(column_count - 1): for column_number in range(column_count - 1):
attr_name = f"querylist_col_{column_number}_width" attr_name = f"querylist_col_{column_number}_width"
width = ds.get_setting(attr_name) or Config.DEFAULT_COLUMN_WIDTH width = ds.setting_get(attr_name) or Config.DEFAULT_COLUMN_WIDTH
self.table_view.setColumnWidth(column_number, width) self.table_view.setColumnWidth(column_number, width)
@ -998,8 +997,8 @@ class SelectPlaylistDialog(QDialog):
self.ui.buttonBox.rejected.connect(self.close) self.ui.buttonBox.rejected.connect(self.close)
self.playlist = None self.playlist = None
width = ds.get_setting("select_playlist_dialog_width") or 800 width = ds.setting_get("select_playlist_dialog_width") or 800
height = ds.get_setting("select_playlist_dialog_height") or 800 height = ds.setting_get("select_playlist_dialog_height") or 800
self.resize(width, height) self.resize(width, height)
for playlist in playlists: for playlist in playlists:
@ -1009,8 +1008,8 @@ class SelectPlaylistDialog(QDialog):
self.ui.lstPlaylists.addItem(p) self.ui.lstPlaylists.addItem(p)
def __del__(self): # review def __del__(self): # review
ds.set_setting("select_playlist_dialog_height", self.height()) ds.setting_set("select_playlist_dialog_height", self.height())
ds.set_setting("select_playlist_dialog_width", self.width()) ds.setting_set("select_playlist_dialog_width", self.width())
def list_doubleclick(self, entry): # review def list_doubleclick(self, entry): # review
self.playlist = entry.data(Qt.ItemDataRole.UserRole) self.playlist = entry.data(Qt.ItemDataRole.UserRole)
@ -1207,7 +1206,7 @@ class Window(QMainWindow):
active_tab=self.playlist_section.tabPlaylist.currentIndex(), active_tab=self.playlist_section.tabPlaylist.currentIndex(),
) )
for name, value in attributes_to_save.items(): for name, value in attributes_to_save.items():
ds.set_setting(name, value) ds.setting_set(name, value)
event.accept() event.accept()
@ -1404,13 +1403,13 @@ class Window(QMainWindow):
# # # # # # # # # # Playlist management functions # # # # # # # # # # # # # # # # # # # # Playlist management functions # # # # # # # # # #
# @log_call # @log_call
def _create_playlist(self, name: str, template_id: int) -> Playlists: def _create_playlist(self, name: str, template_id: int) -> PlaylistDTO:
""" """
Create a playlist in the database, populate it from the template Create a playlist in the database, populate it from the template
if template_id > 0, and return the Playlists object. if template_id > 0, and return the Playlists object.
""" """
return ds.create_playlist(name, template_id) return ds.playlist_create(name, template_id)
# @log_call # @log_call
def _open_playlist(self, playlist: PlaylistDTO, is_template: bool = False) -> int: def _open_playlist(self, playlist: PlaylistDTO, is_template: bool = False) -> int:
@ -1916,7 +1915,7 @@ class Window(QMainWindow):
playlist_ids.append(self._open_playlist(playlist)) playlist_ids.append(self._open_playlist(playlist))
# Set active tab # Set active tab
value = ds.get_setting("active_tab") value = ds.setting_get("active_tab")
if value is not None and value >= 0: if value is not None and value >= 0:
self.playlist_section.tabPlaylist.setCurrentIndex(value) self.playlist_section.tabPlaylist.setCurrentIndex(value)
@ -2416,10 +2415,10 @@ class Window(QMainWindow):
def set_main_window_size(self) -> None: def set_main_window_size(self) -> None:
"""Set size of window from database""" """Set size of window from database"""
x = ds.get_setting("mainwindow_x") or 100 x = ds.setting_get("mainwindow_x") or 100
y = ds.get_setting("mainwindow_y") or 100 y = ds.setting_get("mainwindow_y") or 100
width = ds.get_setting("mainwindow_width") or 100 width = ds.setting_get("mainwindow_width") or 100
height = ds.get_setting("mainwindow_height") or 100 height = ds.setting_get("mainwindow_height") or 100
self.setGeometry(x, y, width, height) self.setGeometry(x, y, width, height)
# @log_call # @log_call

View File

@ -196,7 +196,7 @@ class PlaylistModel(QAbstractTableModel):
if self.is_header_row(row): if self.is_header_row(row):
# Check for specific header colouring # Check for specific header colouring
if plr.row_bg is None: if plr.row_bg is None:
plr.row_bg = ds.get_colour(plr.note) plr.row_bg = ds.notecolours_get_colour(plr.note)
if plr.row_bg: if plr.row_bg:
return QBrush(QColor(plr.row_bg)) return QBrush(QColor(plr.row_bg))
else: else:
@ -233,7 +233,7 @@ class PlaylistModel(QAbstractTableModel):
if column == Col.NOTE.value: if column == Col.NOTE.value:
if plr.note: if plr.note:
if plr.note_bg is None: if plr.note_bg is None:
plr.row_bg = ds.get_colour(plr.note) plr.row_bg = ds.notecolours_get_colour(plr.note)
if plr.note_bg: if plr.note_bg:
return QBrush(QColor(plr.note_bg)) return QBrush(QColor(plr.note_bg))
@ -395,7 +395,7 @@ class PlaylistModel(QAbstractTableModel):
# Signal that rows will be removed # Signal that rows will be removed
super().beginRemoveRows(QModelIndex(), min(row_group), max(row_group)) super().beginRemoveRows(QModelIndex(), min(row_group), max(row_group))
# Remove rows from data store # Remove rows from data store
ds.remove_rows(self.playlist_id, row_group) ds.playlist_remove_rows(self.playlist_id, row_group)
# Signal that data store has been updated # Signal that data store has been updated
super().endRemoveRows() super().endRemoveRows()
@ -500,7 +500,7 @@ class PlaylistModel(QAbstractTableModel):
def _foreground_role(self, row: int, column: int, plr: PlaylistRow) -> QBrush: def _foreground_role(self, row: int, column: int, plr: PlaylistRow) -> QBrush:
"""Return header foreground colour or QBrush() if none""" """Return header foreground colour or QBrush() if none"""
plr.row_fg = ds.get_colour(plr.note, foreground=True) plr.row_fg = ds.notecolours_get_colour(plr.note, foreground=True)
if plr.row_fg: if plr.row_fg:
return QBrush(QColor(plr.row_fg)) return QBrush(QColor(plr.row_fg))
return QBrush() return QBrush()
@ -838,7 +838,7 @@ class PlaylistModel(QAbstractTableModel):
# Notify model going to change # Notify model going to change
self.beginResetModel() self.beginResetModel()
# Update database # Update database
ds.move_rows(from_rows, self.playlist_id, to_row_number) ds.playlist_move_rows(from_rows, self.playlist_id, to_row_number)
# Notify model changed # Notify model changed
self.endResetModel() self.endResetModel()
@ -857,7 +857,7 @@ class PlaylistModel(QAbstractTableModel):
# @log_call # @log_call
def move_rows_between_playlists( def move_rows_between_playlists(
self, self,
from_rows: list[PlaylistRow], from_rows: list[int],
to_row_number: int, to_row_number: int,
to_playlist_id: int, to_playlist_id: int,
) -> None: ) -> None:
@ -878,7 +878,7 @@ class PlaylistModel(QAbstractTableModel):
# and the row range must be contiguous. Process the highest rows # and the row range must be contiguous. Process the highest rows
# first so the lower row numbers are unchanged # first so the lower row numbers are unchanged
row_groups = self._reversed_contiguous_row_groups([a.row_number for a in from_rows]) row_groups = self._reversed_contiguous_row_groups(from_rows)
# Handle the moves in row_group chunks # Handle the moves in row_group chunks
@ -891,7 +891,7 @@ class PlaylistModel(QAbstractTableModel):
to_row_number + len(row_group) to_row_number + len(row_group)
) )
self.signals.signal_begin_insert_rows.emit(insert_rows) self.signals.signal_begin_insert_rows.emit(insert_rows)
ds.move_rows(from_rows=row_group, ds.playlist_move_rows(from_rows=row_group,
from_playlist_id=self.playlist_id, from_playlist_id=self.playlist_id,
to_row=to_row_number, to_row=to_row_number,
to_playlist_id=to_playlist_id) to_playlist_id=to_playlist_id)
@ -921,6 +921,7 @@ class PlaylistModel(QAbstractTableModel):
return return
super().endInsertRows() super().endInsertRows()
self.refresh_data()
# @log_call # @log_call
def move_track_add_note( def move_track_add_note(
@ -1035,7 +1036,7 @@ class PlaylistModel(QAbstractTableModel):
"""Populate dict for one row from database""" """Populate dict for one row from database"""
plrid = self.playlist_rows[row_number].playlistrow_id plrid = self.playlist_rows[row_number].playlistrow_id
refreshed_row = ds.get_playlist_row(plrid) refreshed_row = ds.playlistrow_by_id(plrid)
if not refreshed_row: if not refreshed_row:
raise ApplicationError(f"Failed to retrieve row {self.playlist_id=}, {row_number=}") raise ApplicationError(f"Failed to retrieve row {self.playlist_id=}, {row_number=}")
@ -1062,7 +1063,7 @@ class PlaylistModel(QAbstractTableModel):
track = self.playlist_rows[row_number] track = self.playlist_rows[row_number]
metadata = get_all_track_metadata(track.path) metadata = get_all_track_metadata(track.path)
_ = ds.update_track(track.path, track.track_id, metadata) _ = ds.track_update(track.path, track.track_id, metadata)
roles = [ roles = [
Qt.ItemDataRole.BackgroundRole, Qt.ItemDataRole.BackgroundRole,
@ -1102,7 +1103,7 @@ class PlaylistModel(QAbstractTableModel):
): ):
return return
ds.remove_comments(self.playlist_id, row_numbers) ds.playlist_remove_comments(self.playlist_id, row_numbers)
# only invalidate required roles # only invalidate required roles
roles = [ roles = [
@ -1161,7 +1162,7 @@ class PlaylistModel(QAbstractTableModel):
header_text = header_text[0:-1] header_text = header_text[0:-1]
# Parse passed header text and remove the first colour match string # Parse passed header text and remove the first colour match string
return ds.remove_colour_substring(header_text) return ds.notecolours_remove_colour_substring(header_text)
def rowCount(self, index: QModelIndex = QModelIndex()) -> int: def rowCount(self, index: QModelIndex = QModelIndex()) -> int:
"""Standard function for view""" """Standard function for view"""
@ -1445,7 +1446,7 @@ class PlaylistModel(QAbstractTableModel):
if not track_id: if not track_id:
return "" return ""
return ds.get_last_played_dates(track_id) return ds.playdates_get_last(track_id)
# @log_call # @log_call
def update_or_insert(self, track_id: int, row_number: int) -> None: def update_or_insert(self, track_id: int, row_number: int) -> None:

View File

@ -161,7 +161,7 @@ class PlaylistRow:
if self.track_id > 0: if self.track_id > 0:
raise ApplicationError("Attempting to add track to row with existing track ({self=}") raise ApplicationError("Attempting to add track to row with existing track ({self=}")
ds.add_track_to_header(playlistrow_id=self.playlistrow_id, track_id=track_id) ds.track_add_to_header(playlistrow_id=self.playlistrow_id, track_id=track_id)
# Need to update with track information # Need to update with track information
track = ds.track_by_id(track_id) track = ds.track_by_id(track_id)

View File

@ -1073,7 +1073,7 @@ class PlaylistTab(QTableView):
# Last column is set to stretch so ignore it here # Last column is set to stretch so ignore it here
for column_number in range(header.count() - 1): for column_number in range(header.count() - 1):
attr_name = f"playlist_col_{column_number}_width" attr_name = f"playlist_col_{column_number}_width"
value = ds.get_setting(attr_name) value = ds.setting_get(attr_name)
if value is not None: if value is not None:
self.setColumnWidth(column_number, value) self.setColumnWidth(column_number, value)
else: else:

View File

@ -1,480 +0,0 @@
"""
Tests are named 'test_nnn_xxxx' where 'nn n' is a number. This is used to ensure that
the tests run in order as we rely (in some cases) upon the results of an earlier test.
Yes, we shouldn't do that.
"""
# Standard library imports
import os
import shutil
import tempfile
import unittest
from unittest.mock import MagicMock, patch
# PyQt imports
from PyQt6.QtWidgets import QDialog, QFileDialog
# Third party imports
from mutagen.mp3 import MP3 # type: ignore
import pytest
from pytestqt.plugin import QtBot # type: ignore
# App imports
from app import ds, musicmuster
from app.models import (
db,
Tracks,
)
from config import Config
from file_importer import FileImporter
# Custom fixture to adapt qtbot for use with unittest.TestCase
@pytest.fixture(scope="class")
def qtbot_adapter(qapp, request):
"""Adapt qtbot fixture for usefixtures and unittest.TestCase"""
request.cls.qtbot = QtBot(request)
# Fixture for tmp_path to be available in the class
@pytest.fixture(scope="class")
def class_tmp_path(request, tmp_path_factory):
"""Provide a class-wide tmp_path"""
request.cls.tmp_path = tmp_path_factory.mktemp("pytest_tmp")
@pytest.mark.usefixtures("qtbot_adapter", "class_tmp_path")
class MyTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
"""Runs once before any test in this class"""
db.create_all()
cls.widget = musicmuster.Window()
# Create a playlist for all tests
playlist_name = "file importer playlist"
playlist = ds.playlist_create(name=playlist_name, template_id=0)
cls.widget._open_playlist(playlist)
# Create our musicstore
cls.import_source = tempfile.mkdtemp(suffix="_MMsource_pytest", dir="/tmp")
Config.REPLACE_FILES_DEFAULT_SOURCE = cls.import_source
cls.musicstore = tempfile.mkdtemp(suffix="_MMstore_pytest", dir="/tmp")
Config.IMPORT_DESTINATION = cls.musicstore
@classmethod
def tearDownClass(cls):
"""Runs once after all tests"""
db.drop_all()
shutil.rmtree(cls.musicstore)
shutil.rmtree(cls.import_source)
def setUp(self):
"""Runs before each test"""
with self.qtbot.waitExposed(self.widget):
self.widget.show()
def tearDown(self):
"""Runs after each test"""
self.widget.close() # Close UI to prevent side effects
def wait_for_workers(self, timeout: int = 10000):
"""
Let import threads workers run to completion
"""
def workers_empty():
assert FileImporter.workers == {}
self.qtbot.waitUntil(workers_empty, timeout=timeout)
def test_001_import_no_files(self):
"""Try importing with no files to import"""
with patch("file_importer.show_OK") as mock_show_ok:
self.widget.import_files_wrapper()
mock_show_ok.assert_called_once_with(
"File import",
f"No files in {Config.REPLACE_FILES_DEFAULT_SOURCE} to import",
None,
)
def test_002_import_file_and_cancel(self):
"""Cancel file import"""
test_track_path = "testdata/isa.mp3"
shutil.copy(test_track_path, self.import_source)
with (
patch("file_importer.PickMatch") as MockPickMatch,
patch("file_importer.show_OK") as mock_show_ok,
):
# Create a mock instance of PickMatch
mock_dialog_instance = MagicMock()
MockPickMatch.return_value = mock_dialog_instance
# Simulate the user clicking OK in the dialog
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Rejected
mock_dialog_instance.selected_track_id = -1 # Simulated return value
self.widget.import_files_wrapper()
# Ensure PickMatch was instantiated correctly
MockPickMatch.assert_called_once_with(
new_track_description="I'm So Afraid (Fleetwood Mac)",
choices=[("Do not import", -1, ""), ("Import as new track", 0, "")],
default=1,
)
# Verify exec() was called
mock_dialog_instance.exec.assert_called_once()
# Ensure selected_track_id was accessed after dialog.exec()
assert mock_dialog_instance.selected_track_id < 0
mock_show_ok.assert_called_once_with(
"File not imported",
"isa.mp3 will not be imported because you asked not to import this file",
)
def test_003_import_first_file(self):
"""Import file into empty directory"""
test_track_path = "testdata/isa.mp3"
shutil.copy(test_track_path, self.import_source)
with patch("file_importer.PickMatch") as MockPickMatch:
# Create a mock instance of PickMatch
mock_dialog_instance = MagicMock()
MockPickMatch.return_value = mock_dialog_instance
# Simulate the user clicking OK in the dialog
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Accepted
mock_dialog_instance.selected_track_id = 0 # Simulated return value
self.widget.import_files_wrapper()
# Ensure PickMatch was instantiated correctly
MockPickMatch.assert_called_once_with(
new_track_description="I'm So Afraid (Fleetwood Mac)",
choices=[("Do not import", -1, ""), ("Import as new track", 0, "")],
default=1,
)
# Verify exec() was called
mock_dialog_instance.exec.assert_called_once()
# Ensure selected_track_id was accessed after dialog.exec()
assert mock_dialog_instance.selected_track_id == 0
self.wait_for_workers()
# Check track was imported
tracks = ds.get_all_tracks()
assert len(tracks) == 1
track = tracks[0]
assert track.title == "I'm So Afraid"
assert track.artist == "Fleetwood Mac"
track_file = os.path.join(
self.musicstore, os.path.basename(test_track_path)
)
assert track.path == track_file
assert os.path.exists(track_file)
assert os.listdir(self.import_source) == []
def test_004_import_second_file(self):
"""Import a second file"""
test_track_path = "testdata/lovecats.mp3"
shutil.copy(test_track_path, self.import_source)
with patch("file_importer.PickMatch") as MockPickMatch:
# Create a mock instance of PickMatch
mock_dialog_instance = MagicMock()
MockPickMatch.return_value = mock_dialog_instance
# Simulate the user clicking OK in the dialog
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Accepted
mock_dialog_instance.selected_track_id = 0 # Simulated return value
self.widget.import_files_wrapper()
# Ensure PickMatch was instantiated correctly
MockPickMatch.assert_called_once_with(
new_track_description="The Lovecats (The Cure)",
choices=[("Do not import", -1, ""), ("Import as new track", 0, "")],
default=1,
)
# Verify exec() was called
mock_dialog_instance.exec.assert_called_once()
# Ensure selected_track_id was accessed after dialog.exec()
assert mock_dialog_instance.selected_track_id == 0
self.wait_for_workers()
# Check track was imported
tracks = ds.get_all_tracks()
assert len(tracks) == 2
track = tracks[1]
assert track.title == "The Lovecats"
assert track.artist == "The Cure"
track_file = os.path.join(
self.musicstore, os.path.basename(test_track_path)
)
assert track.path == track_file
assert os.path.exists(track_file)
assert os.listdir(self.import_source) == []
def test_005_replace_file(self):
"""Import the same file again and update existing track"""
test_track_path = "testdata/lovecats.mp3"
shutil.copy(test_track_path, self.import_source)
with patch("file_importer.PickMatch") as MockPickMatch:
# Create a mock instance of PickMatch
mock_dialog_instance = MagicMock()
MockPickMatch.return_value = mock_dialog_instance
# Simulate the user clicking OK in the dialog
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Accepted
mock_dialog_instance.selected_track_id = 2 # Simulated return value
self.widget.import_files_wrapper()
# Ensure PickMatch was instantiated correctly
MockPickMatch.assert_called_once_with(
new_track_description="The Lovecats (The Cure)",
choices=[
("Do not import", -1, ""),
("Import as new track", 0, ""),
(
"The Lovecats (The Cure) (100%)",
2,
os.path.join(
self.musicstore, os.path.basename(test_track_path)
),
),
],
default=2,
)
# Verify exec() was called
mock_dialog_instance.exec.assert_called_once()
self.wait_for_workers()
# Check track was imported
tracks = ds.get_all_tracks()
assert len(tracks) == 2
track = tracks[1]
assert track.title == "The Lovecats"
assert track.artist == "The Cure"
assert track.track_id == 2
track_file = os.path.join(
self.musicstore, os.path.basename(test_track_path)
)
assert track.path == track_file
assert os.path.exists(track_file)
assert os.listdir(self.import_source) == []
def test_006_import_file_no_tags(self) -> None:
"""Try to import untagged file"""
test_track_path = "testdata/lovecats.mp3"
test_filename = os.path.basename(test_track_path)
shutil.copy(test_track_path, self.import_source)
import_file = os.path.join(self.import_source, test_filename)
assert os.path.exists(import_file)
# Remove tags
src = MP3(import_file)
src.delete()
src.save()
with patch("file_importer.show_OK") as mock_show_ok:
self.widget.import_files_wrapper()
mock_show_ok.assert_called_once_with(
"File not imported",
f"{test_filename} will not be imported because of tag errors "
f"(Missing tags in {import_file})",
)
def test_007_import_unreadable_file(self) -> None:
"""Import unreadable file"""
test_track_path = "testdata/lovecats.mp3"
test_filename = os.path.basename(test_track_path)
shutil.copy(test_track_path, self.import_source)
import_file = os.path.join(self.import_source, test_filename)
assert os.path.exists(import_file)
# Make undreadable
os.chmod(import_file, 0)
with patch("file_importer.show_OK") as mock_show_ok:
self.widget.import_files_wrapper()
mock_show_ok.assert_called_once_with(
"File not imported",
f"{test_filename} will not be imported because {import_file} is unreadable",
)
# clean up
os.chmod(import_file, 0o777)
os.unlink(import_file)
def test_008_import_new_file_existing_destination(self) -> None:
"""Import duplicate file"""
test_track_path = "testdata/lovecats.mp3"
test_filename = os.path.basename(test_track_path)
new_destination = os.path.join(self.musicstore, "lc2.mp3")
shutil.copy(test_track_path, self.import_source)
import_file = os.path.join(self.import_source, test_filename)
assert os.path.exists(import_file)
with (
patch("file_importer.PickMatch") as MockPickMatch,
patch.object(
QFileDialog, "getSaveFileName", return_value=(new_destination, "")
) as mock_file_dialog,
patch("file_importer.show_OK") as mock_show_ok,
):
mock_file_dialog.return_value = (
new_destination,
"",
) # Ensure mock correctly returns expected value
# Create a mock instance of PickMatch
mock_dialog_instance = MagicMock()
MockPickMatch.return_value = mock_dialog_instance
# Simulate the user clicking OK in the dialog
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Accepted
mock_dialog_instance.selected_track_id = 0 # Simulated return value
self.widget.import_files_wrapper()
# Ensure PickMatch was instantiated correctly
MockPickMatch.assert_called_once_with(
new_track_description="The Lovecats (The Cure)",
choices=[
("Do not import", -1, ""),
("Import as new track", 0, ""),
(
"The Lovecats (The Cure) (100%)",
2,
os.path.join(
self.musicstore, os.path.basename(test_track_path)
),
),
],
default=2,
)
# Verify exec() was called
mock_dialog_instance.exec.assert_called_once()
destination = os.path.join(self.musicstore, test_filename)
mock_show_ok.assert_called_once_with(
title="Desintation path exists",
msg=f"New import requested but default destination path ({destination}) "
"already exists. Click OK and choose where to save this track",
parent=None,
)
self.wait_for_workers()
# Ensure QFileDialog was called and returned expected value
assert mock_file_dialog.called # Ensure the mock was used
result = mock_file_dialog()
assert result[0] == new_destination # Validate return value
# Check track was imported
tracks = ds.get_all_tracks()
track = tracks[2]
assert track.title == "The Lovecats"
assert track.artist == "The Cure"
assert track.track_id == 3
assert track.path == new_destination
assert os.path.exists(new_destination)
assert os.listdir(self.import_source) == []
# Remove file so as not to interfere with later tests
ds.delete(track)
tracks = ds.get_all_tracks()
assert len(tracks) == 2
os.unlink(new_destination)
assert not os.path.exists(new_destination)
def test_009_import_similar_file(self) -> None:
"""Import file with similar, but different, title"""
test_track_path = "testdata/lovecats.mp3"
test_filename = os.path.basename(test_track_path)
shutil.copy(test_track_path, self.import_source)
import_file = os.path.join(self.import_source, test_filename)
assert os.path.exists(import_file)
# Change title tag
src = MP3(import_file)
src["TIT2"].text[0] += " xyz"
src.save()
with patch("file_importer.PickMatch") as MockPickMatch:
# Create a mock instance of PickMatch
mock_dialog_instance = MagicMock()
MockPickMatch.return_value = mock_dialog_instance
# Simulate the user clicking OK in the dialog
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Accepted
mock_dialog_instance.selected_track_id = 2 # Simulated return value
self.widget.import_files_wrapper()
# Ensure PickMatch was instantiated correctly
MockPickMatch.assert_called_once_with(
new_track_description="The Lovecats xyz (The Cure)",
choices=[
("Do not import", -1, ""),
("Import as new track", 0, ""),
(
"The Lovecats (The Cure) (93%)",
2,
os.path.join(
self.musicstore, os.path.basename(test_track_path)
),
),
],
default=2,
)
# Verify exec() was called
mock_dialog_instance.exec.assert_called_once()
self.wait_for_workers()
# Check track was imported
tracks = ds.get_all_tracks()
assert len(tracks) == 2
track = tracks[1]
assert track.title == "The Lovecats xyz"
assert track.artist == "The Cure"
assert track.track_id == 2
track_file = os.path.join(
self.musicstore, os.path.basename(test_track_path)
)
assert track.path == track_file
assert os.path.exists(track_file)
assert os.listdir(self.import_source) == []

View File

@ -79,7 +79,8 @@ class MyTestCase(unittest.TestCase):
"""Runs after each test""" """Runs after each test"""
self.widget.close() # Close UI to prevent side effects self.widget.close() # Close UI to prevent side effects
def wait_for_workers(self, timeout: int = 10000): # def wait_for_workers(self, timeout: int = 10000):
def wait_for_workers(self, timeout: int = 1000000):
""" """
Let import threads workers run to completion Let import threads workers run to completion
""" """
@ -171,7 +172,7 @@ class MyTestCase(unittest.TestCase):
self.wait_for_workers() self.wait_for_workers()
# Check track was imported # Check track was imported
tracks = ds.get_all_tracks() tracks = ds.tracks_all()
assert len(tracks) == 1 assert len(tracks) == 1
track = tracks[0] track = tracks[0]
assert track.title == "I'm So Afraid" assert track.title == "I'm So Afraid"
@ -216,7 +217,7 @@ class MyTestCase(unittest.TestCase):
self.wait_for_workers() self.wait_for_workers()
# Check track was imported # Check track was imported
tracks = ds.get_all_tracks() tracks = ds.tracks_all()
assert len(tracks) == 2 assert len(tracks) == 2
track = tracks[1] track = tracks[1]
assert track.title == "The Lovecats" assert track.title == "The Lovecats"
@ -268,7 +269,7 @@ class MyTestCase(unittest.TestCase):
self.wait_for_workers() self.wait_for_workers()
# Check track was imported # Check track was imported
tracks = ds.get_all_tracks() tracks = ds.tracks_all()
assert len(tracks) == 2 assert len(tracks) == 2
track = tracks[1] track = tracks[1]
assert track.title == "The Lovecats" assert track.title == "The Lovecats"
@ -397,7 +398,7 @@ class MyTestCase(unittest.TestCase):
assert result[0] == new_destination # Validate return value assert result[0] == new_destination # Validate return value
# Check track was imported # Check track was imported
tracks = ds.get_all_tracks() tracks = ds.tracks_all()
track = tracks[2] track = tracks[2]
assert track.title == "The Lovecats" assert track.title == "The Lovecats"
assert track.artist == "The Cure" assert track.artist == "The Cure"
@ -407,8 +408,8 @@ class MyTestCase(unittest.TestCase):
assert os.listdir(self.import_source) == [] assert os.listdir(self.import_source) == []
# Remove file so as not to interfere with later tests # Remove file so as not to interfere with later tests
ds.delete(track) ds.track_delete(track.track_id)
tracks = ds.get_all_tracks() tracks = ds.tracks_all()
assert len(tracks) == 2 assert len(tracks) == 2
os.unlink(new_destination) os.unlink(new_destination)
@ -463,7 +464,7 @@ class MyTestCase(unittest.TestCase):
self.wait_for_workers() self.wait_for_workers()
# Check track was imported # Check track was imported
tracks = ds.get_all_tracks() tracks = ds.tracks_all()
assert len(tracks) == 2 assert len(tracks) == 2
track = tracks[1] track = tracks[1]
assert track.title == "The Lovecats xyz" assert track.title == "The Lovecats xyz"

View File

@ -7,7 +7,8 @@ import unittest
import pytest import pytest
# App imports # App imports
from app.models import db, Settings from app.models import db
import ds
class TestMMMisc(unittest.TestCase): class TestMMMisc(unittest.TestCase):
@ -28,13 +29,9 @@ class TestMMMisc(unittest.TestCase):
NO_SUCH_SETTING = "abc" NO_SUCH_SETTING = "abc"
VALUE = 3 VALUE = 3
with db.Session() as session: test_non_existant = ds.setting_get(SETTING_NAME)
setting = Settings(session, SETTING_NAME) assert test_non_existant is None
# test repr
_ = str(setting) ds.setting_set(SETTING_NAME, VALUE)
setting.f_int = VALUE test_ok = ds.setting_get(SETTING_NAME)
test = Settings.get_setting(session, SETTING_NAME) assert test_ok == VALUE
assert test.name == SETTING_NAME
assert test.f_int == VALUE
test_new = Settings.get_setting(session, NO_SUCH_SETTING)
assert test_new.name == NO_SUCH_SETTING

View File

@ -9,11 +9,7 @@ from PyQt6.QtCore import Qt, QModelIndex
# App imports # App imports
from app.helpers import get_all_track_metadata from app.helpers import get_all_track_metadata
from app import ds, playlistmodel from app import ds, playlistmodel
from app.models import ( from app.models import db
db,
Playlists,
Tracks,
)
from classes import ( from classes import (
TrackAndPlaylist, TrackAndPlaylist,
) )
@ -41,8 +37,9 @@ class TestMMMiscTracks(unittest.TestCase):
for row in range(len(self.test_tracks)): for row in range(len(self.test_tracks)):
track_path = self.test_tracks[row % len(self.test_tracks)] track_path = self.test_tracks[row % len(self.test_tracks)]
track = ds.track_create(**get_all_track_metadata(track_path)) metadata = get_all_track_metadata(track_path)
self.model.insert_row(track_id=track.id, note=f"{row=}") track = ds.track_create(metadata)
self.model.insert_row(track_id=track.track_id, note=f"{row=}")
def tearDown(self): def tearDown(self):
db.drop_all() db.drop_all()
@ -95,14 +92,14 @@ class TestMMMiscNoPlaylist(unittest.TestCase):
# insert a track into a new playlist # insert a track into a new playlist
playlist = ds.playlist_create(self.PLAYLIST_NAME, template_id=0) playlist = ds.playlist_create(self.PLAYLIST_NAME, template_id=0)
# Create a model # Create a model
model = playlistmodel.PlaylistModel(playlist.id, is_template=False) model = playlistmodel.PlaylistModel(playlist.playlist_id, is_template=False)
# test repr # test repr
_ = str(model) _ = str(model)
track_path = self.test_tracks[0] track_path = self.test_tracks[0]
metadata = get_all_track_metadata(track_path) metadata = get_all_track_metadata(track_path)
track = ds.track_create(metadata) track = ds.track_create(metadata)
model.insert_row(track_id=track.id) model.insert_row(track_id=track.track_id)
prd = model.playlist_rows[model.rowCount() - 1] prd = model.playlist_rows[model.rowCount() - 1]
# test repr # test repr
@ -122,7 +119,7 @@ class TestMMMiscRowMove(unittest.TestCase):
db.create_all() db.create_all()
self.playlist = ds.playlist_create(self.PLAYLIST_NAME, template_id=0) self.playlist = ds.playlist_create(self.PLAYLIST_NAME, template_id=0)
self.model = playlistmodel.PlaylistModel(self.playlist.id, is_template=False) self.model = playlistmodel.PlaylistModel(self.playlist.playlist_id, is_template=False)
for row in range(self.ROWS_TO_CREATE): for row in range(self.ROWS_TO_CREATE):
self.model.insert_row(note=str(row)) self.model.insert_row(note=str(row))
@ -211,12 +208,10 @@ class TestMMMiscRowMove(unittest.TestCase):
for row in range(self.ROWS_TO_CREATE): for row in range(self.ROWS_TO_CREATE):
model_dst.insert_row(note=str(row)) model_dst.insert_row(note=str(row))
ds.playlist_move_rows( model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
from_rows, self.playlist.playlist_id, to_row, playlist_dst.playlist_id
)
assert len(model_src.playlist_rows) == self.ROWS_TO_CREATE - len(from_rows) assert model_src.rowCount() == self.ROWS_TO_CREATE - len(from_rows)
assert len(model_dst.playlist_rows) == self.ROWS_TO_CREATE + len(from_rows) assert model_dst.rowCount() == self.ROWS_TO_CREATE + len(from_rows)
assert sorted([a.row_number for a in model_src.playlist_rows.values()]) == list( assert sorted([a.row_number for a in model_src.playlist_rows.values()]) == list(
range(len(model_src.playlist_rows)) range(len(model_src.playlist_rows))
) )
@ -234,9 +229,7 @@ class TestMMMiscRowMove(unittest.TestCase):
for row in range(self.ROWS_TO_CREATE): for row in range(self.ROWS_TO_CREATE):
model_dst.insert_row(note=str(row)) model_dst.insert_row(note=str(row))
ds.playlist_move_rows( model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
from_rows, self.playlist.playlist_id, to_row, playlist_dst.playlist_id
)
# Check the rows of the destination model # Check the rows of the destination model
row_notes = [] row_notes = []
@ -246,8 +239,8 @@ class TestMMMiscRowMove(unittest.TestCase):
) )
row_notes.append(model_dst.data(index, Qt.ItemDataRole.EditRole)) row_notes.append(model_dst.data(index, Qt.ItemDataRole.EditRole))
assert len(model_src.playlist_rows) == self.ROWS_TO_CREATE - len(from_rows) assert model_src.rowCount() == self.ROWS_TO_CREATE - len(from_rows)
assert len(model_dst.playlist_rows) == self.ROWS_TO_CREATE + len(from_rows) assert model_dst.rowCount() == self.ROWS_TO_CREATE + len(from_rows)
assert [int(a) for a in row_notes] == [0, 1, 3, 2, 3, 4, 5, 6, 7, 8, 9, 10] assert [int(a) for a in row_notes] == [0, 1, 3, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def test_move_multiple_rows_between_playlists_to_end(self): def test_move_multiple_rows_between_playlists_to_end(self):
@ -264,9 +257,7 @@ class TestMMMiscRowMove(unittest.TestCase):
for row in range(self.ROWS_TO_CREATE): for row in range(self.ROWS_TO_CREATE):
model_dst.insert_row(note=str(row)) model_dst.insert_row(note=str(row))
ds.playlist_move_rows( model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
from_rows, self.playlist.id, playlist_dst.playlist_id, to_row
)
# Check the rows of the destination model # Check the rows of the destination model
row_notes = [] row_notes = []
@ -276,8 +267,8 @@ class TestMMMiscRowMove(unittest.TestCase):
) )
row_notes.append(model_dst.data(index, Qt.ItemDataRole.EditRole)) row_notes.append(model_dst.data(index, Qt.ItemDataRole.EditRole))
assert len(model_src.playlist_rows) == self.ROWS_TO_CREATE - len(from_rows) assert model_src.rowCount() == self.ROWS_TO_CREATE - len(from_rows)
assert len(model_dst.playlist_rows) == self.ROWS_TO_CREATE + len(from_rows) assert model_dst.rowCount() == self.ROWS_TO_CREATE + len(from_rows)
assert [int(a) for a in row_notes] == [ assert [int(a) for a in row_notes] == [
0, 0,
1, 1,
@ -294,22 +285,3 @@ class TestMMMiscRowMove(unittest.TestCase):
9, 9,
10, 10,
] ]
# # def test_edit_header(monkeypatch, session): # edit header row in middle of playlist
# # monkeypatch.setattr(playlistmodel, "Session", session)
# # note_text = "test text"
# # initial_row_count = 11
# # insert_row = 6
# # model = create_model_with_playlist_rows(session, initial_row_count)
# # model.insert_header_row(insert_row, note_text)
# # assert model.rowCount() == initial_row_count + 1
# # prd = model.playlist_rows[insert_row]
# # # Test against edit_role because display_role for headers is
# # # handled differently (sets up row span)
# # assert (
# # model.edit_role(model.rowCount(), playlistmodel.Col.NOTE.value, prd)
# # == note_text
# # )

View File

@ -10,12 +10,12 @@ import unittest
# App imports # App imports
from app.models import ( from app.models import (
db, db,
Playdates,
Tracks, Tracks,
) )
from classes import ( from classes import (
Filter, Filter,
) )
import ds
class MyTestCase(unittest.TestCase): class MyTestCase(unittest.TestCase):
@ -25,10 +25,8 @@ class MyTestCase(unittest.TestCase):
db.create_all() db.create_all()
with db.Session() as session:
# Create some track entries # Create some track entries
_ = Tracks(**dict( track1_meta = dict(
session=session,
artist="a", artist="a",
bitrate=0, bitrate=0,
duration=100, duration=100,
@ -37,9 +35,9 @@ class MyTestCase(unittest.TestCase):
silence_at=0, silence_at=0,
start_gap=0, start_gap=0,
title="abc" title="abc"
)) )
track2 = Tracks(**dict( _ = ds.track_create(track1_meta)
session=session, track2_meta = dict(
artist="a", artist="a",
bitrate=0, bitrate=0,
duration=100, duration=100,
@ -48,12 +46,13 @@ class MyTestCase(unittest.TestCase):
silence_at=0, silence_at=0,
start_gap=0, start_gap=0,
title="xyz" title="xyz"
)) )
track2_id = track2.id track2 = ds.track_create(track2_meta)
# Add playdates # Add playdates
# Track 2 played just over a year ago # Track 2 played just over a year ago
just_over_a_year_ago = dt.datetime.now() - dt.timedelta(days=367) just_over_a_year_ago = dt.datetime.now() - dt.timedelta(days=367)
_ = Playdates(session, track2_id, when=just_over_a_year_ago) ds.playdates_update(track2.track_id, when=just_over_a_year_ago)
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
@ -76,8 +75,7 @@ class MyTestCase(unittest.TestCase):
filter = Filter(path="alpha", last_played_comparator="never") filter = Filter(path="alpha", last_played_comparator="never")
with db.Session() as session: results = ds.tracks_filtered(filter)
results = Tracks.get_filtered_tracks(session, filter)
assert len(results) == 1 assert len(results) == 1
assert 'alpha' in results[0].path assert 'alpha' in results[0].path
@ -86,8 +84,7 @@ class MyTestCase(unittest.TestCase):
filter = Filter(path="xray", last_played_comparator="never") filter = Filter(path="xray", last_played_comparator="never")
with db.Session() as session: results = ds.tracks_filtered(filter)
results = Tracks.get_filtered_tracks(session, filter)
assert len(results) == 0 assert len(results) == 0
def test_played_over_a_year_ago(self): def test_played_over_a_year_ago(self):
@ -95,8 +92,7 @@ class MyTestCase(unittest.TestCase):
filter = Filter(last_played_unit="years", last_played_number=1) filter = Filter(last_played_unit="years", last_played_number=1)
with db.Session() as session: results = ds.tracks_filtered(filter)
results = Tracks.get_filtered_tracks(session, filter)
assert len(results) == 1 assert len(results) == 1
assert 'zulu' in results[0].path assert 'zulu' in results[0].path
@ -105,8 +101,7 @@ class MyTestCase(unittest.TestCase):
filter = Filter(last_played_unit="years", last_played_number=2) filter = Filter(last_played_unit="years", last_played_number=2)
with db.Session() as session: results = ds.tracks_filtered(filter)
results = Tracks.get_filtered_tracks(session, filter)
assert len(results) == 0 assert len(results) == 0
def test_never_played(self): def test_never_played(self):
@ -114,8 +109,7 @@ class MyTestCase(unittest.TestCase):
filter = Filter(last_played_comparator="never") filter = Filter(last_played_comparator="never")
with db.Session() as session: results = ds.tracks_filtered(filter)
results = Tracks.get_filtered_tracks(session, filter)
assert len(results) == 1 assert len(results) == 1
assert 'alpha' in results[0].path assert 'alpha' in results[0].path
@ -124,7 +118,6 @@ class MyTestCase(unittest.TestCase):
filter = Filter(last_played_comparator="Any time") filter = Filter(last_played_comparator="Any time")
with db.Session() as session: results = ds.tracks_filtered(filter)
results = Tracks.get_filtered_tracks(session, filter)
assert len(results) == 1 assert len(results) == 1
assert 'zulu' in results[0].path assert 'zulu' in results[0].path

View File

@ -15,7 +15,7 @@ from app.models import (
Playlists, Playlists,
Tracks, Tracks,
) )
from app import musicmuster from app import ds, musicmuster
# Custom fixture to adapt qtbot for use with unittest.TestCase # Custom fixture to adapt qtbot for use with unittest.TestCase
@ -49,8 +49,8 @@ class MyTestCase(unittest.TestCase):
# self.widget.show() # self.widget.show()
# Add two tracks to database # Add two tracks to database
self.tracks = { self.track1 = ds.track_create(
1: { {
"path": "testdata/isa.mp3", "path": "testdata/isa.mp3",
"title": "I'm so afraid", "title": "I'm so afraid",
"artist": "Fleetwood Mac", "artist": "Fleetwood Mac",
@ -59,8 +59,10 @@ class MyTestCase(unittest.TestCase):
"start_gap": 60, "start_gap": 60,
"fade_at": 236263, "fade_at": 236263,
"silence_at": 260343, "silence_at": 260343,
}, }
2: { )
self.track2 = ds.track_create(
{
"path": "testdata/mom.mp3", "path": "testdata/mom.mp3",
"title": "Man of Mystery", "title": "Man of Mystery",
"artist": "The Shadows", "artist": "The Shadows",
@ -69,16 +71,8 @@ class MyTestCase(unittest.TestCase):
"start_gap": 70, "start_gap": 70,
"fade_at": 115000, "fade_at": 115000,
"silence_at": 118000, "silence_at": 118000,
},
} }
)
with db.Session() as session:
for track in self.tracks.values():
db_track = Tracks(session=session, **track)
session.add(db_track)
track["id"] = db_track.id
session.commit()
def down(self): def down(self):
db.drop_all() db.drop_all()
@ -89,8 +83,7 @@ class MyTestCase(unittest.TestCase):
playlist_name = "test_init playlist" playlist_name = "test_init playlist"
with db.Session() as session: playlist = ds.playlist_create(playlist_name, template_id=0)
playlist = Playlists(session, playlist_name, template_id=0)
self.widget._open_playlist(playlist, is_template=False) self.widget._open_playlist(playlist, is_template=False)
with self.qtbot.waitExposed(self.widget): with self.qtbot.waitExposed(self.widget):
self.widget.show() self.widget.show()
@ -102,26 +95,21 @@ class MyTestCase(unittest.TestCase):
note_text = "my note" note_text = "my note"
playlist_name = "test_save_and_restore playlist" playlist_name = "test_save_and_restore playlist"
with db.Session() as session: playlist = ds.playlist_create(playlist_name, template_id=0)
playlist = Playlists(session, playlist_name, template_id=0) model = playlistmodel.PlaylistModel(playlist.playlist_id, is_template=False)
model = playlistmodel.PlaylistModel(playlist.id, is_template=False)
# Add a track with a note # Add a track with a note
model.insert_row( model.insert_row(track_id=self.track1.track_id, note=note_text)
proposed_row_number=0, track_id=self.tracks[1]["id"], note=note_text
)
# We need to commit the session before re-querying
session.commit()
# Retrieve playlist # Retrieve playlist
all_playlists = Playlists.get_all(session) all_playlists = ds.playlists_all()
assert len(all_playlists) == 1 assert len(all_playlists) == 1
retrieved_playlist = all_playlists[0] retrieved_playlist = all_playlists[0]
assert len(retrieved_playlist.rows) == 1 playlist_rows = ds.playlistrows_by_playlist(retrieved_playlist.playlist_id)
paths = [a.track.path for a in retrieved_playlist.rows] assert len(playlist_rows) == 1
assert self.tracks[1]["path"] in paths paths = [a.track.path for a in playlist_rows]
notes = [a.note for a in retrieved_playlist.rows] assert self.track1.path in paths
notes = [a.note for a in playlist_rows]
assert note_text in notes assert note_text in notes
@with_updown @with_updown
@ -134,214 +122,3 @@ class MyTestCase(unittest.TestCase):
utilities.check_db() utilities.check_db()
utilities.update_bitrates() utilities.update_bitrates()
# def test_meta_all_clear(qtbot, session):
# # Create playlist
# playlist = models.Playlists(session, "my playlist", template_id=0)
# playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
# # Add some tracks
# # Need to commit session after each one so that new row is found
# # for subsequent inserts
# track1_path = "/a/b/c"
# track1 = models.Tracks(session, track1_path)
# playlist_tab.insert_track(session, track1)
# session.commit()
# track2_path = "/d/e/f"
# track2 = models.Tracks(session, track2_path)
# playlist_tab.insert_track(session, track2)
# session.commit()
# track3_path = "/h/i/j"
# track3 = models.Tracks(session, track3_path)
# playlist_tab.insert_track(session, track3)
# session.commit()
# assert playlist_tab._get_current_track_row() is None
# assert playlist_tab._get_next_track_row() is None
# assert playlist_tab._get_notes_rows() == []
# assert playlist_tab._get_played_track_rows() == []
# assert len(playlist_tab._get_unreadable_track_rows()) == 3
# def test_meta(qtbot, session):
# # Create playlist
# playlist = playlists.Playlists(session, "my playlist",
# template_id=0)
# playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
# # Add some tracks
# track1_path = "/a/b/c"
# track1 = models.Tracks(session, track1_path)
# playlist_tab.insert_track(session, track1)
# session.commit()
# track2_path = "/d/e/f"
# track2 = models.Tracks(session, track2_path)
# playlist_tab.insert_track(session, track2)
# session.commit()
# track3_path = "/h/i/j"
# track3 = models.Tracks(session, track3_path)
# playlist_tab.insert_track(session, track3)
# session.commit()
# assert len(playlist_tab._get_unreadable_track_rows()) == 3
# assert playlist_tab._get_played_track_rows() == []
# assert playlist_tab._get_current_track_row() is None
# assert playlist_tab._get_next_track_row() is None
# assert playlist_tab._get_notes_rows() == []
# playlist_tab._set_played_row(0)
# assert playlist_tab._get_played_track_rows() == [0]
# assert playlist_tab._get_current_track_row() is None
# assert playlist_tab._get_next_track_row() is None
# assert playlist_tab._get_notes_rows() == []
# # Add a note
# note_text = "my note"
# note_row = 7 # will be added as row 3
# note = models.Notes(session, playlist.id, note_row, note_text)
# playlist_tab._insert_note(session, note)
# assert playlist_tab._get_played_track_rows() == [0]
# assert playlist_tab._get_current_track_row() is None
# assert playlist_tab._get_next_track_row() is None
# assert playlist_tab._get_notes_rows() == [3]
# playlist_tab._set_next_track_row(1)
# assert playlist_tab._get_played_track_rows() == [0]
# assert playlist_tab._get_current_track_row() is None
# assert playlist_tab._get_next_track_row() == 1
# assert playlist_tab._get_notes_rows() == [3]
# playlist_tab._set_current_track_row(2)
# assert playlist_tab._get_played_track_rows() == [0]
# assert playlist_tab._get_current_track_row() == 2
# assert playlist_tab._get_next_track_row() == 1
# assert playlist_tab._get_notes_rows() == [3]
# playlist_tab._clear_played_row_status(0)
# assert playlist_tab._get_played_track_rows() == []
# assert playlist_tab._get_current_track_row() == 2
# assert playlist_tab._get_next_track_row() == 1
# assert playlist_tab._get_notes_rows() == [3]
# playlist_tab._meta_clear_next()
# assert playlist_tab._get_played_track_rows() == []
# assert playlist_tab._get_current_track_row() == 2
# assert playlist_tab._get_next_track_row() is None
# assert playlist_tab._get_notes_rows() == [3]
# playlist_tab._clear_current_track_row()
# assert playlist_tab._get_played_track_rows() == []
# assert playlist_tab._get_current_track_row() is None
# assert playlist_tab._get_next_track_row() is None
# assert playlist_tab._get_notes_rows() == [3]
# # Test clearing again has no effect
# playlist_tab._clear_current_track_row()
# assert playlist_tab._get_played_track_rows() == []
# assert playlist_tab._get_current_track_row() is None
# assert playlist_tab._get_next_track_row() is None
# assert playlist_tab._get_notes_rows() == [3]
# def test_clear_next(qtbot, session):
# # Create playlist
# playlist = models.Playlists(session, "my playlist", template_id=0)
# playlist_tab = playlists.PlaylistTab(None, session, playlist.id)
# # Add some tracks
# track1_path = "/a/b/c"
# track1 = models.Tracks(session, track1_path)
# playlist_tab.insert_track(session, track1)
# session.commit()
# track2_path = "/d/e/f"
# track2 = models.Tracks(session, track2_path)
# playlist_tab.insert_track(session, track2)
# session.commit()
# playlist_tab._set_next_track_row(1)
# assert playlist_tab._get_next_track_row() == 1
# playlist_tab.clear_next(session)
# assert playlist_tab._get_next_track_row() is None
# def test_get_selected_row(qtbot, monkeypatch, session):
# monkeypatch.setattr(musicmuster, "Session", session)
# monkeypatch.setattr(playlists, "Session", session)
# # Create playlist and playlist_tab
# window = musicmuster.Window()
# playlist = models.Playlists(session, "test playlist", template_id=0)
# playlist_tab = playlists.PlaylistTab(window, session, playlist.id)
# # Add some tracks
# track1_path = "/a/b/c"
# track1 = models.Tracks(session, track1_path)
# playlist_tab.insert_track(session, track1)
# session.commit()
# track2_path = "/d/e/f"
# track2 = models.Tracks(session, track2_path)
# playlist_tab.insert_track(session, track2)
# session.commit()
# qtbot.addWidget(playlist_tab)
# with qtbot.waitExposed(window):
# window.show()
# row0_item0 = playlist_tab.item(0, 0)
# assert row0_item0 is not None
# rect = playlist_tab.visualItemRect(row0_item0)
# qtbot.mouseClick(playlist_tab.viewport(), Qt.LeftButton, pos=rect.center())
# row_number = playlist_tab.get_selected_row()
# assert row_number == 0
# def test_set_next(qtbot, monkeypatch, session):
# monkeypatch.setattr(musicmuster, "Session", session)
# monkeypatch.setattr(playlists, "Session", session)
# seed2tracks(session)
# playlist_name = "test playlist"
# # Create testing playlist
# window = musicmuster.Window()
# playlist = models.Playlists(session, playlist_name, template_id=0)
# playlist_tab = playlists.PlaylistTab(window, session, playlist.id)
# idx = window.tabPlaylist.addTab(playlist_tab, playlist_name)
# window.tabPlaylist.setCurrentIndex(idx)
# qtbot.addWidget(playlist_tab)
# # Add some tracks
# track1 = models.Tracks.get_by_filename(session, "isa.mp3")
# track1_title = track1.title
# assert track1_title
# playlist_tab.insert_track(session, track1)
# session.commit()
# track2 = models.Tracks.get_by_filename(session, "mom.mp3")
# playlist_tab.insert_track(session, track2)
# with qtbot.waitExposed(window):
# window.show()
# row0_item2 = playlist_tab.item(0, 2)
# assert row0_item2 is not None
# rect = playlist_tab.visualItemRect(row0_item2)
# qtbot.mouseClick(playlist_tab.viewport(), Qt.LeftButton, pos=rect.center())
# selected_title = playlist_tab.get_selected_title()
# assert selected_title == track1_title
# qtbot.keyPress(playlist_tab.viewport(), "N", modifier=Qt.ControlModifier)
# qtbot.wait(1000)
# def test_kae(monkeypatch, session):
# # monkeypatch.setattr(dbconfig, "Session", session)
# monkeypatch.setattr(musicmuster, "Session", session)
# musicmuster.Window.kae()
# # monkeypatch.setattr(musicmuster, "Session", session)
# # monkeypatch.setattr(dbconfig, "Session", session)
# # monkeypatch.setattr(models, "Session", session)
# # monkeypatch.setattr(playlists, "Session", session)