From a2baf489c337907d9f609ca6fa1d6d581b14b87f Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Sun, 13 Apr 2025 09:16:49 +0100 Subject: [PATCH] Remove sessions from test_playlistmodel.py --- tests/X_test_file_importer.py | 480 ---------------------------------- tests/test_playlistmodel.py | 25 +- 2 files changed, 1 insertion(+), 504 deletions(-) delete mode 100644 tests/X_test_file_importer.py diff --git a/tests/X_test_file_importer.py b/tests/X_test_file_importer.py deleted file mode 100644 index 90216c9..0000000 --- a/tests/X_test_file_importer.py +++ /dev/null @@ -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) == [] diff --git a/tests/test_playlistmodel.py b/tests/test_playlistmodel.py index 84185c8..3a71409 100644 --- a/tests/test_playlistmodel.py +++ b/tests/test_playlistmodel.py @@ -9,11 +9,7 @@ from PyQt6.QtCore import Qt, QModelIndex # App imports from app.helpers import get_all_track_metadata from app import ds, playlistmodel -from app.models import ( - db, - Playlists, - Tracks, -) +from app.models import db from classes import ( TrackAndPlaylist, ) @@ -294,22 +290,3 @@ class TestMMMiscRowMove(unittest.TestCase): 9, 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 -# # )