diff --git a/Harmonia.Core/Models/Playlist.cs b/Harmonia.Core/Models/Playlist.cs index dafe6bd..cf75da7 100644 --- a/Harmonia.Core/Models/Playlist.cs +++ b/Harmonia.Core/Models/Playlist.cs @@ -5,35 +5,37 @@ namespace Harmonia.Core.Models; public class Playlist(string name) { - public string UID { get; set; } = Guid.NewGuid().ToString(); + private readonly List _songs = []; + + public string UID { get; init; } = Guid.NewGuid().ToString(); public string Name { get; set; } = name; - public List Songs { get; set; } = []; + public IReadOnlyList Songs => _songs; public List GroupOptions { get; set; } = []; public List SortOptions { get; set; } = []; public bool IsLocked { get; set; } public event EventHandler? PlaylistUpdated; - public void AddSong(Song song, int? index) + public void AddSong(Song song, int? index = null) { AddSongs([song], index); } - public void AddSongs(Song[] songs, int? index) + public void AddSongs(Song[] songs, int? index = null) { PlaylistSong[] playlistSongs = songs.Select(song => new PlaylistSong(song)).ToArray(); AddSongs(playlistSongs, index); } - private void AddSongs(PlaylistSong[] playlistSongs, int? index) + private void AddSongs(PlaylistSong[] playlistSongs, int? index = null) { if (IsLocked) return; int insertIndex = index ?? Songs.Count; - Songs.InsertRange(insertIndex, playlistSongs); + _songs.InsertRange(insertIndex, playlistSongs); PlaylistUpdatedEventArgs eventArgs = new() { @@ -48,7 +50,7 @@ public class Playlist(string name) public void MoveSong(PlaylistSong playlistSong, int newIndex) { - int currentIndex = Songs.IndexOf(playlistSong); + int currentIndex = _songs.IndexOf(playlistSong); MoveSong(currentIndex, newIndex); } @@ -63,8 +65,8 @@ public class Playlist(string name) PlaylistSong playlistSong = Songs[oldIndex]; - Songs.Remove(playlistSong); - Songs.Insert(newIndex, playlistSong); + _songs.Remove(playlistSong); + _songs.Insert(newIndex, playlistSong); PlaylistUpdatedEventArgs eventArgs = new() { @@ -81,8 +83,8 @@ public class Playlist(string name) public void SortSongs(PlaylistSong[] playlistSongs, SortOption[] sortOptions) { Dictionary oldPlaylistSongs = playlistSongs - .OrderBy(Songs.IndexOf) - .ToDictionary(Songs.IndexOf, playlistSong => playlistSong); + .OrderBy(_songs.IndexOf) + .ToDictionary(_songs.IndexOf, playlistSong => playlistSong); Song[] songs = playlistSongs.Select(playlistSong => playlistSong.Song).ToArray(); Song[] sortedSongs = [.. songs.SortBy(sortOptions)]; @@ -98,8 +100,8 @@ public class Playlist(string name) if (newPlaylistSong == playlistSong) continue; - Songs.Remove(playlistSong); - Songs.Insert(index, newPlaylistSong); + _songs.RemoveAt(index); + _songs.Insert(index, newPlaylistSong); } PlaylistUpdatedEventArgs eventArgs = new() @@ -120,22 +122,9 @@ public class Playlist(string name) public void RemoveSongs(int index, int count) { - if (IsLocked) - return; + PlaylistSong[] playlistSongs = [.. _songs.GetRange(index, count)]; - PlaylistSong[] playlistSongs = [.. Songs.GetRange(index, count)]; - - Songs.RemoveRange(index, count); - - PlaylistUpdatedEventArgs eventArgs = new() - { - Action = PlaylistUpdateAction.Remove, - Index = index, - Count = count, - Songs = playlistSongs - }; - - PlaylistUpdated?.Invoke(this, eventArgs); + RemoveSongs(playlistSongs); } public void RemoveSong(PlaylistSong playlistSong) @@ -145,11 +134,14 @@ public class Playlist(string name) public void RemoveSongs(PlaylistSong[] playlistSongs) { + if (IsLocked) + return; + List removedSongs = []; foreach (PlaylistSong playlistSong in playlistSongs) { - if (Songs.Remove(playlistSong)) + if (_songs.Remove(playlistSong)) { removedSongs.Add(playlistSong); } diff --git a/Harmonia.Tests/Harmonia.Tests.csproj b/Harmonia.Tests/Harmonia.Tests.csproj new file mode 100644 index 0000000..f7a0fbe --- /dev/null +++ b/Harmonia.Tests/Harmonia.Tests.csproj @@ -0,0 +1,33 @@ + + + + net9.0 + enable + enable + false + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/Harmonia.Tests/PlaylistTests.cs b/Harmonia.Tests/PlaylistTests.cs new file mode 100644 index 0000000..e88e574 --- /dev/null +++ b/Harmonia.Tests/PlaylistTests.cs @@ -0,0 +1,126 @@ +using Harmonia.Core.Models; +using Shouldly; +using System.ComponentModel; + +namespace Harmonia.Tests; + +public class PlaylistTests +{ + [Fact] + public void Add_Songs() + { + Playlist playlist = new("Test"); + + Song[] songs = + [ + new Song() { FileName = "Song1.mp3" }, + new Song() { FileName = "Song2.mp3" }, + new Song() { FileName = "Song3.mp3" } + ]; + + playlist.AddSongs(songs); + + playlist.Songs.Count.ShouldBe(3); + + Song song = new() { FileName = "Song4.mp3" }; + playlist.AddSong(song); + + playlist.Songs.Count.ShouldBe(4); + playlist.Songs[3].Song.FileName.ShouldBe("Song4.mp3"); + + Song song2 = new() { FileName = "Song5.mp3" }; + playlist.AddSong(song2, 1); + + playlist.Songs.Count.ShouldBe(5); + playlist.Songs[1].Song.FileName.ShouldBe("Song5.mp3"); + } + + [Fact] + public void Sort_Songs() + { + Playlist playlist = new("Test"); + + Song[] songs = + [ + new Song() { FileName = "Song1.mp3", Album = "Album 2", TrackNumber = 2 }, + new Song() { FileName = "Song2.mp3", Album = "Album 1", TrackNumber = 2 }, + new Song() { FileName = "Song3.mp3", Album = "Album 2", TrackNumber = 1 }, + new Song() { FileName = "Song4.mp3", Album = "Album 1", TrackNumber = 1 }, + new Song() { FileName = "Song5.mp3", Album = "Album 2", TrackNumber = 3 }, + ]; + + playlist.AddSongs(songs); + + SortOption[] sortOptions = + [ + new("Album", ListSortDirection.Ascending), + new("TrackNumber", ListSortDirection.Ascending) + ]; + + playlist.SortSongs([.. playlist.Songs], sortOptions); + + string[] expectedSortedFileNames = + [ + "Song4.mp3", + "Song2.mp3", + "Song3.mp3", + "Song1.mp3", + "Song5.mp3" + ]; + + playlist.Songs.Select(x => x.Song.FileName).ToArray() + .ShouldBeEquivalentTo(expectedSortedFileNames); + } + + [Fact] + public void Remove_Songs() + { + Playlist playlist = new("Test"); + + Song[] songs = + [ + new Song() { FileName = "Song1.mp3" }, + new Song() { FileName = "Song2.mp3" }, + new Song() { FileName = "Song3.mp3" } + ]; + + playlist.AddSongs(songs); + + playlist.RemoveSong(1); + + string[] expectedFileNames = + [ + "Song1.mp3", + "Song3.mp3" + ]; + + playlist.Songs.Select(x => x.Song.FileName).ToArray() + .ShouldBeEquivalentTo(expectedFileNames); + } + + [Fact] + public void Lock_Playlist() + { + Playlist playlist = new("Test"); + + Song[] songs = + [ + new Song() { FileName = "Song1.mp3" }, + new Song() { FileName = "Song2.mp3" }, + new Song() { FileName = "Song3.mp3" } + ]; + + playlist.AddSongs(songs); + + playlist.IsLocked = true; + + Song song = new() { FileName = "Song4.mp3" }; + playlist.AddSong(song); + + playlist.Songs.Count.ShouldBe(3); + + playlist.RemoveSong(0); + + playlist.Songs.Count.ShouldBe(3); + } +} \ No newline at end of file diff --git a/Harmonia.sln b/Harmonia.sln index 78ed5d4..fcec17b 100644 --- a/Harmonia.sln +++ b/Harmonia.sln @@ -1,10 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.12.35728.132 d17.12 +VisualStudioVersion = 17.12.35728.132 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Harmonia.Core", "Harmonia.Core\Harmonia.Core.csproj", "{BC40B066-CD33-4A9E-A470-A0F9216D1822}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Harmonia.Tests", "Harmonia.Tests\Harmonia.Tests.csproj", "{F17DDB67-F609-4316-B0AB-2235BE69135B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {BC40B066-CD33-4A9E-A470-A0F9216D1822}.Debug|Any CPU.Build.0 = Debug|Any CPU {BC40B066-CD33-4A9E-A470-A0F9216D1822}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC40B066-CD33-4A9E-A470-A0F9216D1822}.Release|Any CPU.Build.0 = Release|Any CPU + {F17DDB67-F609-4316-B0AB-2235BE69135B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F17DDB67-F609-4316-B0AB-2235BE69135B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F17DDB67-F609-4316-B0AB-2235BE69135B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F17DDB67-F609-4316-B0AB-2235BE69135B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE