# Standard library imports import unittest # PyQt imports from PyQt6.QtCore import Qt, QModelIndex # Third party imports # App imports from app.helpers import get_all_track_metadata from app import playlistmodel from app.models import ( db, Playlists, Tracks, ) class TestMMMiscTracks(unittest.TestCase): def setUp(self): PLAYLIST_NAME = "tracks playlist" self.test_tracks = [ "testdata/isa.mp3", "testdata/isa_with_gap.mp3", "testdata/loser.mp3", "testdata/lovecats-10seconds.mp3", "testdata/lovecats.mp3", "testdata/mom.mp3", "testdata/sitting.mp3", "testdata/wrb.flac", ] db.create_all() # Create a playlist and model with db.Session() as session: self.playlist = Playlists(session, PLAYLIST_NAME, template_id=0) self.model = playlistmodel.PlaylistModel(self.playlist.id, is_template=False) for row in range(len(self.test_tracks)): track_path = self.test_tracks[row % len(self.test_tracks)] track = Tracks(session, **get_all_track_metadata(track_path)) self.model.insert_row( proposed_row_number=row, track_id=track.id, note=f"{row=}" ) session.commit() def tearDown(self): db.drop_all() def test_8_row_playlist(self): # Test auto-created playlist assert self.model.rowCount() == 8 assert max(self.model.playlist_rows.keys()) == 7 for row in range(self.model.rowCount()): assert row in self.model.playlist_rows assert self.model.playlist_rows[row].row_number == row def test_timing_one_track(self): START_ROW = 0 END_ROW = 2 self.model.insert_row(proposed_row_number=START_ROW, note="start+") self.model.insert_row(proposed_row_number=END_ROW, note="-") prd = self.model.playlist_rows[START_ROW] qv_value = self.model._display_role( START_ROW, playlistmodel.HEADER_NOTES_COLUMN, prd ) assert qv_value == "start [1 tracks, 4:23 unplayed]" class TestMMMiscNoPlaylist(unittest.TestCase): PLAYLIST_NAME = "tracks playlist" test_tracks = [ "testdata/isa.mp3", "testdata/isa_with_gap.mp3", "testdata/loser.mp3", "testdata/lovecats-10seconds.mp3", "testdata/lovecats.mp3", "testdata/mom.mp3", "testdata/sitting.mp3", ] def setUp(self): db.create_all() def tearDown(self): db.drop_all() def test_insert_track_new_playlist(self): # insert a track into a new playlist with db.Session() as session: playlist = Playlists(session, self.PLAYLIST_NAME, template_id=0) # Create a model model = playlistmodel.PlaylistModel(playlist.id, is_template=False) # test repr _ = str(model) track_path = self.test_tracks[0] metadata = get_all_track_metadata(track_path) track = Tracks(session, **metadata) model.insert_row(proposed_row_number=0, track_id=track.id) prd = model.playlist_rows[model.rowCount() - 1] # test repr _ = str(prd) assert ( model._edit_role( model.rowCount() - 1, playlistmodel.Col.TITLE.value, prd ) == metadata["title"] ) class TestMMMiscRowMove(unittest.TestCase): PLAYLIST_NAME = "rowmove playlist" ROWS_TO_CREATE = 11 def setUp(self): db.create_all() with db.Session() as session: self.playlist = Playlists(session, self.PLAYLIST_NAME, template_id=0) self.model = playlistmodel.PlaylistModel(self.playlist.id, is_template=False) for row in range(self.ROWS_TO_CREATE): self.model.insert_row(proposed_row_number=row, note=str(row)) session.commit() def tearDown(self): db.drop_all() def test_move_rows_test2(self): # move row 3 to row 5 self.model.move_rows([3], 5) # Check we have all rows and plr_rownums are correct for row in range(self.model.rowCount()): assert row in self.model.playlist_rows assert self.model.playlist_rows[row].row_number == row if row not in [3, 4, 5]: assert self.model.playlist_rows[row].note == str(row) elif row == 3: assert self.model.playlist_rows[row].note == str(4) elif row == 4: assert self.model.playlist_rows[row].note == str(3) elif row == 5: assert self.model.playlist_rows[row].note == str(5) def test_move_rows_test3(self): # move row 4 to row 3 self.model.move_rows([4], 3) # Check we have all rows and plr_rownums are correct for row in range(self.model.rowCount()): assert row in self.model.playlist_rows assert self.model.playlist_rows[row].row_number == row if row not in [3, 4]: assert self.model.playlist_rows[row].note == str(row) elif row == 3: assert self.model.playlist_rows[row].note == str(4) elif row == 4: assert self.model.playlist_rows[row].note == str(3) def test_move_rows_test4(self): # move row 4 to row 2 self.model.move_rows([4], 2) # Check we have all rows and plr_rownums are correct for row in range(self.model.rowCount()): assert row in self.model.playlist_rows assert self.model.playlist_rows[row].row_number == row if row not in [2, 3, 4]: assert self.model.playlist_rows[row].note == str(row) elif row == 2: assert self.model.playlist_rows[row].note == str(4) elif row == 3: assert self.model.playlist_rows[row].note == str(2) elif row == 4: assert self.model.playlist_rows[row].note == str(3) def test_move_rows_test5(self): # move rows [1, 4, 5, 10] → 8 self.model.move_rows([1, 4, 5, 10], 8) # Check we have all rows and plr_rownums are correct new_order = [] for row in range(self.model.rowCount()): assert row in self.model.playlist_rows assert self.model.playlist_rows[row].row_number == row new_order.append(int(self.model.playlist_rows[row].note)) assert new_order == [0, 2, 3, 6, 7, 1, 4, 5, 10, 8, 9] def test_move_rows_test6(self): # move rows [3, 6] → 5 self.model.move_rows([3, 6], 5) # Check we have all rows and plr_rownums are correct new_order = [] for row in range(self.model.rowCount()): assert row in self.model.playlist_rows assert self.model.playlist_rows[row].row_number == row new_order.append(int(self.model.playlist_rows[row].note)) assert new_order == [0, 1, 2, 4, 3, 6, 5, 7, 8, 9, 10] def test_move_rows_test7(self): # move rows [3, 5, 6] → 8 self.model.move_rows([3, 5, 6], 8) # Check we have all rows and plr_rownums are correct new_order = [] for row in range(self.model.rowCount()): assert row in self.model.playlist_rows assert self.model.playlist_rows[row].row_number == row new_order.append(int(self.model.playlist_rows[row].note)) assert new_order == [0, 1, 2, 4, 7, 3, 5, 6, 8, 9, 10] def test_move_rows_test8(self): # move rows [7, 8, 10] → 5 self.model.move_rows([7, 8, 10], 5) # Check we have all rows and plr_rownums are correct new_order = [] for row in range(self.model.rowCount()): assert row in self.model.playlist_rows assert self.model.playlist_rows[row].row_number == row new_order.append(int(self.model.playlist_rows[row].note)) assert new_order == [0, 1, 2, 3, 4, 7, 8, 10, 5, 6, 9] def test_move_rows_test9(self): # move rows [1, 2, 3] → 0 # Replicate issue 244 self.model.move_rows([0, 1, 2, 3], 0) # Check we have all rows and plr_rownums are correct new_order = [] for row in range(self.model.rowCount()): assert row in self.model.playlist_rows assert self.model.playlist_rows[row].row_number == row new_order.append(int(self.model.playlist_rows[row].note)) assert new_order == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] def test_insert_header_row_end(self): # insert header row at end of playlist note_text = "test text" assert self.model.rowCount() == self.ROWS_TO_CREATE self.model.insert_row(proposed_row_number=None, note=note_text) assert self.model.rowCount() == self.ROWS_TO_CREATE + 1 prd = self.model.playlist_rows[self.model.rowCount() - 1] # Test against edit_role because display_role for headers is # handled differently (sets up row span) assert ( self.model._edit_role( self.model.rowCount() - 1, playlistmodel.Col.NOTE.value, prd ) == note_text ) def test_insert_header_row_middle(self): # insert header row in middle of playlist note_text = "test text" insert_row = 6 self.model.insert_row(proposed_row_number=insert_row, note=note_text) assert self.model.rowCount() == self.ROWS_TO_CREATE + 1 prd = self.model.playlist_rows[insert_row] # Test against edit_role because display_role for headers is # handled differently (sets up row span) assert ( self.model._edit_role( self.model.rowCount() - 1, playlistmodel.Col.NOTE.value, prd ) == note_text ) def test_add_track_to_header(self): note_text = "test text" insert_row = 6 self.model.insert_row(proposed_row_number=insert_row, note=note_text) assert self.model.rowCount() == self.ROWS_TO_CREATE + 1 prd = self.model.playlist_rows[1] self.model.add_track_to_header(insert_row, prd.track_id) def test_reverse_row_groups_one_row(self): rows_to_move = [3] result = self.model._reversed_contiguous_row_groups(rows_to_move) assert len(result) == 1 assert result[0] == [3] def test_reverse_row_groups_multiple_row(self): rows_to_move = [2, 3, 4, 5, 7, 9, 10, 13, 17, 20, 21] result = self.model._reversed_contiguous_row_groups(rows_to_move) assert result == [[20, 21], [17], [13], [9, 10], [7], [2, 3, 4, 5]] def test_move_one_row_between_playlists_to_end(self): from_rows = [3] to_row = self.ROWS_TO_CREATE destination_playlist = "destination" model_src = self.model with db.Session() as session: playlist_dst = Playlists(session, destination_playlist, template_id=0) model_dst = playlistmodel.PlaylistModel(playlist_dst.id, is_template=False) for row in range(self.ROWS_TO_CREATE): model_dst.insert_row(proposed_row_number=row, note=str(row)) model_src.move_rows_between_playlists(from_rows, to_row, model_dst.playlist_id) model_dst.refresh_data(session) assert len(model_src.playlist_rows) == self.ROWS_TO_CREATE - len(from_rows) assert len(model_dst.playlist_rows) == self.ROWS_TO_CREATE + len(from_rows) assert sorted([a.row_number for a in model_src.playlist_rows.values()]) == list( range(len(model_src.playlist_rows)) ) def test_move_one_row_between_playlists_to_middle(self): from_rows = [3] to_row = 2 destination_playlist = "destination" model_src = self.model with db.Session() as session: playlist_dst = Playlists(session, destination_playlist, template_id=0) model_dst = playlistmodel.PlaylistModel(playlist_dst.id, is_template=False) for row in range(self.ROWS_TO_CREATE): model_dst.insert_row(proposed_row_number=row, note=str(row)) model_src.move_rows_between_playlists(from_rows, to_row, model_dst.playlist_id) model_dst.refresh_data(session) # Check the rows of the destination model row_notes = [] for row_number in range(model_dst.rowCount()): index = model_dst.index( row_number, playlistmodel.Col.TITLE.value, QModelIndex() ) row_notes.append(model_dst.data(index, Qt.ItemDataRole.EditRole)) assert len(model_src.playlist_rows) == self.ROWS_TO_CREATE - len(from_rows) assert len(model_dst.playlist_rows) == 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] def test_move_multiple_rows_between_playlists_to_end(self): from_rows = [1, 3, 4] to_row = 2 destination_playlist = "destination" model_src = self.model with db.Session() as session: playlist_dst = Playlists(session, destination_playlist, template_id=0) model_dst = playlistmodel.PlaylistModel(playlist_dst.id, is_template=False) for row in range(self.ROWS_TO_CREATE): model_dst.insert_row(proposed_row_number=row, note=str(row)) model_src.move_rows_between_playlists(from_rows, to_row, model_dst.playlist_id) model_dst.refresh_data(session) # Check the rows of the destination model row_notes = [] for row_number in range(model_dst.rowCount()): index = model_dst.index( row_number, playlistmodel.Col.TITLE.value, QModelIndex() ) row_notes.append(model_dst.data(index, Qt.ItemDataRole.EditRole)) assert len(model_src.playlist_rows) == self.ROWS_TO_CREATE - len(from_rows) assert len(model_dst.playlist_rows) == self.ROWS_TO_CREATE + len(from_rows) assert [int(a) for a in row_notes] == [ 0, 1, 1, 3, 4, 2, 3, 4, 5, 6, 7, 8, 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 # # )