From 9c392d5f01ad7e8ebe8cda13de6fc9eb32962f40 Mon Sep 17 00:00:00 2001 From: Abestanis Date: Mon, 14 Oct 2024 14:54:22 +0200 Subject: [PATCH] Add tests for sorting behaviour and make album year optional (#152) * Add tests for sorting behaviour and make album year optional * Mark SortFeature as an interface * Use SortOrder in Sort * Use Dart 3 pattern destructuring in sort tests * Make the sort expectation clearer to see in the tests * Fix Sort.toMap --- lib/localization/localization.dart | 8 - lib/logic/models/album.dart | 9 +- lib/logic/models/sort.dart | 276 ++++---- lib/logic/player/content.dart | 19 +- .../content_list_view/list_header.dart | 6 +- .../persistent_queue_tile.dart | 2 +- test/logic/models/sort_test.dart | 633 ++++++++++++++++++ 7 files changed, 759 insertions(+), 194 deletions(-) create mode 100644 test/logic/models/sort_test.dart diff --git a/lib/localization/localization.dart b/lib/localization/localization.dart index 811fb145a..09537c1de 100644 --- a/lib/localization/localization.dart +++ b/lib/localization/localization.dart @@ -65,8 +65,6 @@ extension AppLocalizationsExtension on AppLocalizations { return artist; case SongSortFeature.album: return album; - default: - throw UnimplementedError(); } case ContentType.album: switch (feature as AlbumSortFeature) { @@ -78,8 +76,6 @@ extension AppLocalizationsExtension on AppLocalizations { return year; case AlbumSortFeature.numberOfSongs: return numberOfTracks; - default: - throw UnimplementedError(); } case ContentType.playlist: switch (feature as PlaylistSortFeature) { @@ -89,8 +85,6 @@ extension AppLocalizationsExtension on AppLocalizations { return dateAdded; case PlaylistSortFeature.name: return title; - default: - throw UnimplementedError(); } case ContentType.artist: switch (feature as ArtistSortFeature) { @@ -100,8 +94,6 @@ extension AppLocalizationsExtension on AppLocalizations { return numberOfAlbums; case ArtistSortFeature.numberOfTracks: return numberOfTracks; - default: - throw UnimplementedError(); } } } diff --git a/lib/logic/models/album.dart b/lib/logic/models/album.dart index d91b58787..3393e1cc2 100644 --- a/lib/logic/models/album.dart +++ b/lib/logic/models/album.dart @@ -37,8 +37,8 @@ class Album extends PersistentQueue implements PlatformAlbum { bool get playable => true; /// Gets album normalized year. - int get year { - return lastYear == null || lastYear! < 1000 ? clock.now().year : lastYear!; + int? get year { + return lastYear ?? firstYear; } /// Returns string in format `album name • year`. @@ -46,11 +46,6 @@ class Album extends PersistentQueue implements PlatformAlbum { return ContentUtils.appendYearWithDot(album, year); } - /// Returns string in format `Album • year`. - String albumDotName(AppLocalizations l10n) { - return ContentUtils.appendYearWithDot(l10n.album, year); - } - /// Returns the album artist. Artist? getArtist() => artistId == null ? null : ContentControl.instance.state.artists.firstWhereOrNull((el) => el.id == artistId); diff --git a/lib/logic/models/sort.dart b/lib/logic/models/sort.dart index 99b2c4558..f5e827a49 100644 --- a/lib/logic/models/sort.dart +++ b/lib/logic/models/sort.dart @@ -1,15 +1,25 @@ -import 'package:enum_to_string/enum_to_string.dart'; import 'package:equatable/equatable.dart'; import 'package:sweyer/sweyer.dart'; -/// Interface for other sort feature enums. -abstract class SortFeature extends Enum { - const SortFeature._(String value) : super(value); +/// The order of a sort operation. +enum SortOrder { + /// Ascending sort order ("lower" values first, "higher" values last). + ascending, + + /// Descending sort order ("higher" values first, "lower" values last). + descending; + + /// The inversion of the sort order. + SortOrder get inverted => switch (this) { + SortOrder.ascending => SortOrder.descending, + SortOrder.descending => SortOrder.ascending, + }; +} +/// Interface for other sort feature enums. +abstract interface class SortFeature { /// Returns sort feature values for a given content. static List> getValuesForContent(ContentType contentType) { - // TODO: Remove ContentType cast, see https://github.com/dart-lang/language/issues/2315 - // ignore: unnecessary_cast switch (contentType as ContentType) { case ContentType.song: return SongSortFeature.values as List>; @@ -22,177 +32,161 @@ abstract class SortFeature extends Enum { } } - /// Whether the default order is ASC. - bool get defaultOrderAscending; + /// An identifier for this feature. + String get id; + + /// The default sorting order of the feature. + SortOrder get defaultSortingOrder; } /// Features to sort by a [Song] list. /// /// Each feature also has the default sort order - ASC or DESC. /// When user changes the [SongSortFeature] the new default sort order is applied. -class SongSortFeature extends SortFeature { - const SongSortFeature._(String value) : super._(value); - - @override - bool get defaultOrderAscending => this != dateModified && this != dateAdded; - - static List get values => const [dateModified, dateAdded, title, artist, album]; - +enum SongSortFeature implements SortFeature { /// Sort by the [Song.dateModified]. - /// Default sort order is DESC. - static const dateModified = SongSortFeature._('dateModified'); + dateModified(SortOrder.descending), /// Sort by the [Song.dateAdded]. - /// Default sort order is DESC. - static const dateAdded = SongSortFeature._('dateAdded'); + dateAdded(SortOrder.descending), /// Sort by the [Song.title]. - /// Default sort order is ASC. - static const title = SongSortFeature._('title'); + title(SortOrder.ascending), /// Sort by the [Song.artist]. - /// Default sort order is ASC. - static const artist = SongSortFeature._('artist'); + artist(SortOrder.ascending), /// Sort by the [Song.album]. - /// Default sort order is ASC. - static const album = SongSortFeature._('album'); + album(SortOrder.ascending); + + const SongSortFeature(this.defaultSortingOrder); + + @override + String get id => name; + + @override + final SortOrder defaultSortingOrder; } /// Features to sort by a [Album] list. /// /// Each feature also has the default sort order - ASC or DESC. /// When user changes the [AlbumSortFeature] the new default sort order is applied. -class AlbumSortFeature extends SortFeature { - const AlbumSortFeature._(String value) : super._(value); - - @override - bool get defaultOrderAscending => this != year; - - static List get values => const [title, artist, year, numberOfSongs]; - +enum AlbumSortFeature implements SortFeature { /// Sort by the [Album.album]. - /// Default sort order is ASC. - static const title = AlbumSortFeature._('title'); + title(SortOrder.ascending), /// Sort by the [Album.artist]. - /// Default sort order is ASC. - static const artist = AlbumSortFeature._('artist'); + artist(SortOrder.ascending), /// Sort by the [Album.lastYear]. - /// Default sort order is DESC. - static const year = AlbumSortFeature._('year'); + year(SortOrder.descending), /// Sort by the [Album.numberOfSongs]. - /// Default sort order is ASC. - static const numberOfSongs = AlbumSortFeature._('numberOfSongs'); + numberOfSongs(SortOrder.ascending); + + const AlbumSortFeature(this.defaultSortingOrder); + + @override + String get id => name; + + @override + final SortOrder defaultSortingOrder; } /// Features to sort by a [Playlist] list. /// /// Each feature also has the default sort order - ASC or DESC. /// When user changes the [PlaylistSortFeature] the new default sort order is applied. -class PlaylistSortFeature extends SortFeature { - const PlaylistSortFeature._(String value) : super._(value); - - @override - bool get defaultOrderAscending => this != dateModified && this != dateAdded; - - static List get values => const [dateAdded, dateModified, name]; - +enum PlaylistSortFeature implements SortFeature { /// Sort by the [Playlist.dateModified]. - /// Default sort order is DESC. - static const dateModified = PlaylistSortFeature._('dateModified'); + dateModified(SortOrder.descending), /// Sort by the [Playlist.dateAdded]. - /// Default sort order is DESC. - static const dateAdded = PlaylistSortFeature._('dateAdded'); + dateAdded(SortOrder.descending), /// Sort by the [Playlist.name]. - /// Default sort order is ASC. - static const name = PlaylistSortFeature._('name'); + name(SortOrder.ascending); + + const PlaylistSortFeature(this.defaultSortingOrder); + + @override + String get id => this.name; + + @override + final SortOrder defaultSortingOrder; } /// Features to sort by a [Artist] list. /// /// Each feature also has the default sort order - ASC or DESC. /// When user changes the [ArtistSortFeature] the new default sort order is applied. -class ArtistSortFeature extends SortFeature { - const ArtistSortFeature._(String value) : super._(value); - - @override - bool get defaultOrderAscending => true; - - static List get values => const [name, numberOfAlbums, numberOfTracks]; - +enum ArtistSortFeature implements SortFeature { /// Sort by the [Artist.artist]. - /// Default sort order is ASC. - static const name = ArtistSortFeature._('name'); + name(SortOrder.ascending), /// Sort by the [Artist.numberOfAlbums]. - /// Default sort order is ASC. - static const numberOfAlbums = ArtistSortFeature._('numberOfAlbums'); + numberOfAlbums(SortOrder.ascending), /// Sort by the [Artist.numberOfTracks]. - /// Default sort order is ASC. - static const numberOfTracks = ArtistSortFeature._('numberOfTracks'); + numberOfTracks(SortOrder.ascending); + + const ArtistSortFeature(this.defaultSortingOrder); + + @override + String get id => this.name; + + @override + final SortOrder defaultSortingOrder; } abstract class Sort extends Equatable { const Sort({ required this.feature, - required this.orderAscending, + required this.order, }); - Sort.defaultOrder(this.feature) : orderAscending = feature.defaultOrderAscending; + Sort.defaultOrder(this.feature) : order = feature.defaultSortingOrder; final SortFeature feature; - final bool orderAscending; + final SortOrder order; @override - List get props => [feature, orderAscending]; + List get props => [feature, order]; - Sort copyWith({SortFeature? feature, bool? orderAscending}); - Sort get withDefaultOrder; + Sort copyWith({SortFeature? feature, SortOrder? order}); + Sort get withDefaultOrder => copyWith(order: feature.defaultSortingOrder); Comparator get comparator; Map toMap() => { - 'feature': feature.value, - 'orderAscending': orderAscending, + 'feature': feature.id, + 'order': order.name, }; } class SongSort extends Sort { const SongSort({ - required SongSortFeature feature, - bool orderAscending = true, - }) : super(feature: feature, orderAscending: orderAscending); + required super.feature, + super.order = SortOrder.ascending, + }); SongSort.defaultOrder(feature) : super.defaultOrder(feature); factory SongSort.fromMap(Map map) => SongSort( - feature: EnumToString.fromString( - SongSortFeature.values, - map['feature'], - )!, - orderAscending: map['orderAscending'], + feature: SongSortFeature.values.byName(map['feature']), + order: SortOrder.values.byName(map['order'] ?? 'ascending'), ); @override SongSort copyWith({ covariant SongSortFeature? feature, - bool? orderAscending, + SortOrder? order, }) { return SongSort( feature: feature ?? this.feature as SongSortFeature, - orderAscending: orderAscending ?? this.orderAscending, + order: order ?? this.order, ); } - @override - SongSort get withDefaultOrder { - return copyWith(orderAscending: feature.defaultOrderAscending); - } - int _fallbackTitle(Song a, Song b) { return a.title.toLowerCase().compareTo(b.title.toLowerCase()); } @@ -253,7 +247,7 @@ class SongSort extends Sort { default: throw UnimplementedError(); } - if (!orderAscending) { + if (order == SortOrder.descending) { return (a, b) => c(b, a); } return c; @@ -262,43 +256,29 @@ class SongSort extends Sort { class AlbumSort extends Sort { const AlbumSort({ - required AlbumSortFeature feature, - bool orderAscending = true, - }) : super(feature: feature, orderAscending: orderAscending); + required super.feature, + super.order = SortOrder.ascending, + }); AlbumSort.defaultOrder(feature) : super.defaultOrder(feature); factory AlbumSort.fromMap(Map map) => AlbumSort( - feature: EnumToString.fromString( - AlbumSortFeature.values, - map['feature'], - )!, - orderAscending: map['orderAscending'], + feature: AlbumSortFeature.values.byName(map['feature']), + order: SortOrder.values.byName(map['order'] ?? 'ascending'), ); - @override - Map toMap() => { - 'feature': feature.value, - 'orderAscending': orderAscending, - }; - @override AlbumSort copyWith({ covariant AlbumSortFeature? feature, - bool? orderAscending, + SortOrder? order, }) { return AlbumSort( feature: feature ?? this.feature as AlbumSortFeature, - orderAscending: orderAscending ?? this.orderAscending, + order: order ?? this.order, ); } - @override - AlbumSort get withDefaultOrder { - return copyWith(orderAscending: feature.defaultOrderAscending); - } - int _fallbackYear(Album a, Album b) { - return a.year.compareTo(b.year); + return (a.year ?? 0).compareTo((b.year ?? 0)); // TODO: Decide how to sort null values } int _fallbackTitle(Album a, Album b) { @@ -329,7 +309,7 @@ class AlbumSort extends Sort { break; case AlbumSortFeature.year: c = (a, b) { - final compare = a.year.compareTo(b.year); + final compare = (a.year ?? 0).compareTo(b.year ?? 0); // TODO: Decide how to sort null values if (compare == 0) { return _fallbackTitle(a, b); } @@ -348,7 +328,7 @@ class AlbumSort extends Sort { default: throw UnimplementedError(); } - if (!orderAscending) { + if (order == SortOrder.descending) { return (a, b) => c(b, a); } return c; @@ -357,41 +337,27 @@ class AlbumSort extends Sort { class PlaylistSort extends Sort { const PlaylistSort({ - required PlaylistSortFeature feature, - bool orderAscending = true, - }) : super(feature: feature, orderAscending: orderAscending); + required super.feature, + super.order = SortOrder.ascending, + }); PlaylistSort.defaultOrder(feature) : super.defaultOrder(feature); factory PlaylistSort.fromMap(Map map) => PlaylistSort( - feature: EnumToString.fromString( - PlaylistSortFeature.values, - map['feature'], - )!, - orderAscending: map['orderAscending'], + feature: PlaylistSortFeature.values.byName(map['feature']), + order: SortOrder.values.byName(map['order'] ?? 'ascending'), ); - @override - Map toMap() => { - 'feature': feature.value, - 'orderAscending': orderAscending, - }; - @override PlaylistSort copyWith({ covariant PlaylistSortFeature? feature, - bool? orderAscending, + SortOrder? order, }) { return PlaylistSort( feature: feature ?? this.feature as PlaylistSortFeature, - orderAscending: orderAscending ?? this.orderAscending, + order: order ?? this.order, ); } - @override - PlaylistSort get withDefaultOrder { - return copyWith(orderAscending: feature.defaultOrderAscending); - } - int _fallbackName(Playlist a, Playlist b) { return a.name.toLowerCase().compareTo(b.name.toLowerCase()); } @@ -434,7 +400,7 @@ class PlaylistSort extends Sort { default: throw UnimplementedError(); } - if (!orderAscending) { + if (order == SortOrder.descending) { return (a, b) => c(b, a); } return c; @@ -443,41 +409,27 @@ class PlaylistSort extends Sort { class ArtistSort extends Sort { const ArtistSort({ - required ArtistSortFeature feature, - bool orderAscending = true, - }) : super(feature: feature, orderAscending: orderAscending); + required super.feature, + super.order = SortOrder.ascending, + }); ArtistSort.defaultOrder(feature) : super.defaultOrder(feature); factory ArtistSort.fromMap(Map map) => ArtistSort( - feature: EnumToString.fromString( - ArtistSortFeature.values, - map['feature'], - )!, - orderAscending: map['orderAscending'], + feature: ArtistSortFeature.values.byName(map['feature']), + order: SortOrder.values.byName(map['order'] ?? 'ascending'), ); - @override - Map toMap() => { - 'feature': feature.value, - 'orderAscending': orderAscending, - }; - @override ArtistSort copyWith({ covariant ArtistSortFeature? feature, - bool? orderAscending, + SortOrder? order, }) { return ArtistSort( feature: feature ?? this.feature as ArtistSortFeature, - orderAscending: orderAscending ?? this.orderAscending, + order: order ?? this.order, ); } - @override - ArtistSort get withDefaultOrder { - return copyWith(orderAscending: feature.defaultOrderAscending); - } - int _fallbackName(Artist a, Artist b) { return a.artist.toLowerCase().compareTo(b.artist.toLowerCase()); } @@ -520,7 +472,7 @@ class ArtistSort extends Sort { default: throw UnimplementedError(); } - if (!orderAscending) { + if (order == SortOrder.descending) { return (a, b) => c(b, a); } return c; diff --git a/lib/logic/player/content.dart b/lib/logic/player/content.dart index ebbfbddf2..6fca5538d 100644 --- a/lib/logic/player/content.dart +++ b/lib/logic/player/content.dart @@ -822,18 +822,8 @@ class ContentUtils { static const String dot = '•'; /// Joins list with the [dot]. - static String joinDot(List list) { - if (list.isEmpty) { - return ''; - } - var result = list.first; - for (int i = 1; i < list.length; i++) { - final string = list[i].toString(); - if (string.isNotEmpty) { - result += ' $dot $string'; - } - } - return result; + static String joinDot(List list) { + return list.where((item) => item != null && item.toString().isNotEmpty).join(' $dot '); } /// Returns a default icon for the playlist art. @@ -855,7 +845,10 @@ class ContentUtils { } /// Appends dot and year to [string]. - static String appendYearWithDot(String string, int year) { + static String appendYearWithDot(String string, int? year) { + if (year == null) { + return string; + } return '$string $dot $year'; } diff --git a/lib/widgets/content_list_view/list_header.dart b/lib/widgets/content_list_view/list_header.dart index a2a7a4b47..d6156424f 100644 --- a/lib/widgets/content_list_view/list_header.dart +++ b/lib/widgets/content_list_view/list_header.dart @@ -108,7 +108,7 @@ class ContentListHeader extends StatelessWidget { void _handleTap(BuildContext context) { final l10n = getl10n(context); final sort = getSort(); - ShowFunctions.instance.showRadio( + ShowFunctions.instance.showRadio>( context: context, title: l10n.sort, items: SortFeature.getValuesForContent(contentType), @@ -174,11 +174,11 @@ class ContentListHeader extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ ContentListHeaderAction( - icon: Icon(sort.orderAscending ? Icons.north_rounded : Icons.south_rounded), + icon: Icon(sort.order == SortOrder.ascending ? Icons.north_rounded : Icons.south_rounded), onPressed: () { ContentControl.instance.sort( contentType: contentType, - sort: sort.copyWith(orderAscending: !sort.orderAscending), + sort: sort.copyWith(order: sort.order.inverted), ); }, ), diff --git a/lib/widgets/content_list_view/persistent_queue_tile.dart b/lib/widgets/content_list_view/persistent_queue_tile.dart index b9435060a..331fed3d2 100644 --- a/lib/widgets/content_list_view/persistent_queue_tile.dart +++ b/lib/widgets/content_list_view/persistent_queue_tile.dart @@ -230,7 +230,7 @@ class _PersistentQueueTileState if (queue is Album) { children.add(ArtistWidget( artist: queue.artist, - trailingText: queue.year.toString(), + trailingText: queue.year?.toString(), textStyle: _subtitleTheme(widget.queue.type, theme), )); } else if (queue is Playlist) { diff --git a/test/logic/models/sort_test.dart b/test/logic/models/sort_test.dart new file mode 100644 index 000000000..258384f76 --- /dev/null +++ b/test/logic/models/sort_test.dart @@ -0,0 +1,633 @@ +import '../../test.dart'; + +void main() { + group('Song sorting', () { + late List songs; + setUp(() { + songs = [ + songWith(id: 0, dateModified: 0, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 1, dateModified: 1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 2, dateModified: -1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 3, dateModified: 0, dateAdded: 1, title: '', artist: '', album: ''), + songWith(id: 4, dateModified: 0, dateAdded: -1, title: '', artist: '', album: ''), + songWith(id: 5, dateModified: 0, dateAdded: 0, title: 'A', artist: '', album: ''), + songWith(id: 6, dateModified: 0, dateAdded: 0, title: 'AA', artist: '', album: ''), + songWith(id: 7, dateModified: 0, dateAdded: 0, title: 'a', artist: '', album: ''), + songWith(id: 8, dateModified: 0, dateAdded: 0, title: 'z', artist: '', album: ''), + songWith(id: 9, dateModified: 0, dateAdded: 0, title: '', artist: 'A', album: ''), + songWith(id: 10, dateModified: 0, dateAdded: 0, title: '', artist: 'AA', album: ''), + songWith(id: 11, dateModified: 0, dateAdded: 0, title: '', artist: 'a', album: ''), + songWith(id: 12, dateModified: 0, dateAdded: 0, title: '', artist: 'z', album: ''), + songWith(id: 13, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'A'), + songWith(id: 14, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'AA'), + songWith(id: 15, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'a'), + songWith(id: 16, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'z'), + ]; + }); + final Map>> testCases = { + SortOrder.ascending: { + SongSortFeature.dateModified: [ + songWith(id: 2, dateModified: -1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 0, dateModified: 0, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 3, dateModified: 0, dateAdded: 1, title: '', artist: '', album: ''), + songWith(id: 4, dateModified: 0, dateAdded: -1, title: '', artist: '', album: ''), + songWith(id: 9, dateModified: 0, dateAdded: 0, title: '', artist: 'A', album: ''), + songWith(id: 10, dateModified: 0, dateAdded: 0, title: '', artist: 'AA', album: ''), + songWith(id: 11, dateModified: 0, dateAdded: 0, title: '', artist: 'a', album: ''), + songWith(id: 12, dateModified: 0, dateAdded: 0, title: '', artist: 'z', album: ''), + songWith(id: 13, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'A'), + songWith(id: 14, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'AA'), + songWith(id: 15, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'a'), + songWith(id: 16, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'z'), + songWith(id: 5, dateModified: 0, dateAdded: 0, title: 'A', artist: '', album: ''), + songWith(id: 7, dateModified: 0, dateAdded: 0, title: 'a', artist: '', album: ''), + songWith(id: 6, dateModified: 0, dateAdded: 0, title: 'AA', artist: '', album: ''), + songWith(id: 8, dateModified: 0, dateAdded: 0, title: 'z', artist: '', album: ''), + songWith(id: 1, dateModified: 1, dateAdded: 0, title: '', artist: '', album: ''), + ], + SongSortFeature.dateAdded: [ + songWith(id: 4, dateModified: 0, dateAdded: -1, title: '', artist: '', album: ''), + songWith(id: 0, dateModified: 0, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 1, dateModified: 1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 2, dateModified: -1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 9, dateModified: 0, dateAdded: 0, title: '', artist: 'A', album: ''), + songWith(id: 10, dateModified: 0, dateAdded: 0, title: '', artist: 'AA', album: ''), + songWith(id: 11, dateModified: 0, dateAdded: 0, title: '', artist: 'a', album: ''), + songWith(id: 12, dateModified: 0, dateAdded: 0, title: '', artist: 'z', album: ''), + songWith(id: 13, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'A'), + songWith(id: 14, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'AA'), + songWith(id: 15, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'a'), + songWith(id: 16, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'z'), + songWith(id: 5, dateModified: 0, dateAdded: 0, title: 'A', artist: '', album: ''), + songWith(id: 7, dateModified: 0, dateAdded: 0, title: 'a', artist: '', album: ''), + songWith(id: 6, dateModified: 0, dateAdded: 0, title: 'AA', artist: '', album: ''), + songWith(id: 8, dateModified: 0, dateAdded: 0, title: 'z', artist: '', album: ''), + songWith(id: 3, dateModified: 0, dateAdded: 1, title: '', artist: '', album: ''), + ], + SongSortFeature.title: [ + songWith(id: 2, dateModified: -1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 0, dateModified: 0, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 3, dateModified: 0, dateAdded: 1, title: '', artist: '', album: ''), + songWith(id: 4, dateModified: 0, dateAdded: -1, title: '', artist: '', album: ''), + songWith(id: 9, dateModified: 0, dateAdded: 0, title: '', artist: 'A', album: ''), + songWith(id: 10, dateModified: 0, dateAdded: 0, title: '', artist: 'AA', album: ''), + songWith(id: 11, dateModified: 0, dateAdded: 0, title: '', artist: 'a', album: ''), + songWith(id: 12, dateModified: 0, dateAdded: 0, title: '', artist: 'z', album: ''), + songWith(id: 13, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'A'), + songWith(id: 14, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'AA'), + songWith(id: 15, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'a'), + songWith(id: 16, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'z'), + songWith(id: 1, dateModified: 1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 5, dateModified: 0, dateAdded: 0, title: 'A', artist: '', album: ''), + songWith(id: 7, dateModified: 0, dateAdded: 0, title: 'a', artist: '', album: ''), + songWith(id: 6, dateModified: 0, dateAdded: 0, title: 'AA', artist: '', album: ''), + songWith(id: 8, dateModified: 0, dateAdded: 0, title: 'z', artist: '', album: ''), + ], + SongSortFeature.artist: [ + songWith(id: 0, dateModified: 0, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 1, dateModified: 1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 2, dateModified: -1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 3, dateModified: 0, dateAdded: 1, title: '', artist: '', album: ''), + songWith(id: 4, dateModified: 0, dateAdded: -1, title: '', artist: '', album: ''), + songWith(id: 13, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'A'), + songWith(id: 14, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'AA'), + songWith(id: 15, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'a'), + songWith(id: 16, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'z'), + songWith(id: 5, dateModified: 0, dateAdded: 0, title: 'A', artist: '', album: ''), + songWith(id: 7, dateModified: 0, dateAdded: 0, title: 'a', artist: '', album: ''), + songWith(id: 6, dateModified: 0, dateAdded: 0, title: 'AA', artist: '', album: ''), + songWith(id: 8, dateModified: 0, dateAdded: 0, title: 'z', artist: '', album: ''), + songWith(id: 9, dateModified: 0, dateAdded: 0, title: '', artist: 'A', album: ''), + songWith(id: 11, dateModified: 0, dateAdded: 0, title: '', artist: 'a', album: ''), + songWith(id: 10, dateModified: 0, dateAdded: 0, title: '', artist: 'AA', album: ''), + songWith(id: 12, dateModified: 0, dateAdded: 0, title: '', artist: 'z', album: ''), + ], + SongSortFeature.album: [ + songWith(id: 0, dateModified: 0, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 1, dateModified: 1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 2, dateModified: -1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 3, dateModified: 0, dateAdded: 1, title: '', artist: '', album: ''), + songWith(id: 4, dateModified: 0, dateAdded: -1, title: '', artist: '', album: ''), + songWith(id: 9, dateModified: 0, dateAdded: 0, title: '', artist: 'A', album: ''), + songWith(id: 10, dateModified: 0, dateAdded: 0, title: '', artist: 'AA', album: ''), + songWith(id: 11, dateModified: 0, dateAdded: 0, title: '', artist: 'a', album: ''), + songWith(id: 12, dateModified: 0, dateAdded: 0, title: '', artist: 'z', album: ''), + songWith(id: 5, dateModified: 0, dateAdded: 0, title: 'A', artist: '', album: ''), + songWith(id: 7, dateModified: 0, dateAdded: 0, title: 'a', artist: '', album: ''), + songWith(id: 6, dateModified: 0, dateAdded: 0, title: 'AA', artist: '', album: ''), + songWith(id: 8, dateModified: 0, dateAdded: 0, title: 'z', artist: '', album: ''), + songWith(id: 13, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'A'), + songWith(id: 15, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'a'), + songWith(id: 14, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'AA'), + songWith(id: 16, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'z'), + ], + }, + SortOrder.descending: { + SongSortFeature.dateModified: [ + songWith(id: 1, dateModified: 1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 8, dateModified: 0, dateAdded: 0, title: 'z', artist: '', album: ''), + songWith(id: 6, dateModified: 0, dateAdded: 0, title: 'AA', artist: '', album: ''), + songWith(id: 5, dateModified: 0, dateAdded: 0, title: 'A', artist: '', album: ''), + songWith(id: 7, dateModified: 0, dateAdded: 0, title: 'a', artist: '', album: ''), + songWith(id: 0, dateModified: 0, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 3, dateModified: 0, dateAdded: 1, title: '', artist: '', album: ''), + songWith(id: 4, dateModified: 0, dateAdded: -1, title: '', artist: '', album: ''), + songWith(id: 9, dateModified: 0, dateAdded: 0, title: '', artist: 'A', album: ''), + songWith(id: 10, dateModified: 0, dateAdded: 0, title: '', artist: 'AA', album: ''), + songWith(id: 11, dateModified: 0, dateAdded: 0, title: '', artist: 'a', album: ''), + songWith(id: 12, dateModified: 0, dateAdded: 0, title: '', artist: 'z', album: ''), + songWith(id: 13, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'A'), + songWith(id: 14, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'AA'), + songWith(id: 15, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'a'), + songWith(id: 16, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'z'), + songWith(id: 2, dateModified: -1, dateAdded: 0, title: '', artist: '', album: ''), + ], + SongSortFeature.dateAdded: [ + songWith(id: 3, dateModified: 0, dateAdded: 1, title: '', artist: '', album: ''), + songWith(id: 8, dateModified: 0, dateAdded: 0, title: 'z', artist: '', album: ''), + songWith(id: 6, dateModified: 0, dateAdded: 0, title: 'AA', artist: '', album: ''), + songWith(id: 5, dateModified: 0, dateAdded: 0, title: 'A', artist: '', album: ''), + songWith(id: 7, dateModified: 0, dateAdded: 0, title: 'a', artist: '', album: ''), + songWith(id: 0, dateModified: 0, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 1, dateModified: 1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 2, dateModified: -1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 9, dateModified: 0, dateAdded: 0, title: '', artist: 'A', album: ''), + songWith(id: 10, dateModified: 0, dateAdded: 0, title: '', artist: 'AA', album: ''), + songWith(id: 11, dateModified: 0, dateAdded: 0, title: '', artist: 'a', album: ''), + songWith(id: 12, dateModified: 0, dateAdded: 0, title: '', artist: 'z', album: ''), + songWith(id: 13, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'A'), + songWith(id: 14, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'AA'), + songWith(id: 15, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'a'), + songWith(id: 16, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'z'), + songWith(id: 4, dateModified: 0, dateAdded: -1, title: '', artist: '', album: ''), + ], + SongSortFeature.title: [ + songWith(id: 8, dateModified: 0, dateAdded: 0, title: 'z', artist: '', album: ''), + songWith(id: 6, dateModified: 0, dateAdded: 0, title: 'AA', artist: '', album: ''), + songWith(id: 5, dateModified: 0, dateAdded: 0, title: 'A', artist: '', album: ''), + songWith(id: 7, dateModified: 0, dateAdded: 0, title: 'a', artist: '', album: ''), + songWith(id: 1, dateModified: 1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 0, dateModified: 0, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 3, dateModified: 0, dateAdded: 1, title: '', artist: '', album: ''), + songWith(id: 4, dateModified: 0, dateAdded: -1, title: '', artist: '', album: ''), + songWith(id: 9, dateModified: 0, dateAdded: 0, title: '', artist: 'A', album: ''), + songWith(id: 10, dateModified: 0, dateAdded: 0, title: '', artist: 'AA', album: ''), + songWith(id: 11, dateModified: 0, dateAdded: 0, title: '', artist: 'a', album: ''), + songWith(id: 12, dateModified: 0, dateAdded: 0, title: '', artist: 'z', album: ''), + songWith(id: 13, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'A'), + songWith(id: 14, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'AA'), + songWith(id: 15, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'a'), + songWith(id: 16, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'z'), + songWith(id: 2, dateModified: -1, dateAdded: 0, title: '', artist: '', album: ''), + ], + SongSortFeature.artist: [ + songWith(id: 12, dateModified: 0, dateAdded: 0, title: '', artist: 'z', album: ''), + songWith(id: 10, dateModified: 0, dateAdded: 0, title: '', artist: 'AA', album: ''), + songWith(id: 9, dateModified: 0, dateAdded: 0, title: '', artist: 'A', album: ''), + songWith(id: 11, dateModified: 0, dateAdded: 0, title: '', artist: 'a', album: ''), + songWith(id: 8, dateModified: 0, dateAdded: 0, title: 'z', artist: '', album: ''), + songWith(id: 6, dateModified: 0, dateAdded: 0, title: 'AA', artist: '', album: ''), + songWith(id: 5, dateModified: 0, dateAdded: 0, title: 'A', artist: '', album: ''), + songWith(id: 7, dateModified: 0, dateAdded: 0, title: 'a', artist: '', album: ''), + songWith(id: 0, dateModified: 0, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 1, dateModified: 1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 2, dateModified: -1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 3, dateModified: 0, dateAdded: 1, title: '', artist: '', album: ''), + songWith(id: 4, dateModified: 0, dateAdded: -1, title: '', artist: '', album: ''), + songWith(id: 13, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'A'), + songWith(id: 14, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'AA'), + songWith(id: 15, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'a'), + songWith(id: 16, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'z'), + ], + SongSortFeature.album: [ + songWith(id: 16, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'z'), + songWith(id: 14, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'AA'), + songWith(id: 13, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'A'), + songWith(id: 15, dateModified: 0, dateAdded: 0, title: '', artist: '', album: 'a'), + songWith(id: 8, dateModified: 0, dateAdded: 0, title: 'z', artist: '', album: ''), + songWith(id: 6, dateModified: 0, dateAdded: 0, title: 'AA', artist: '', album: ''), + songWith(id: 5, dateModified: 0, dateAdded: 0, title: 'A', artist: '', album: ''), + songWith(id: 7, dateModified: 0, dateAdded: 0, title: 'a', artist: '', album: ''), + songWith(id: 0, dateModified: 0, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 1, dateModified: 1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 2, dateModified: -1, dateAdded: 0, title: '', artist: '', album: ''), + songWith(id: 3, dateModified: 0, dateAdded: 1, title: '', artist: '', album: ''), + songWith(id: 4, dateModified: 0, dateAdded: -1, title: '', artist: '', album: ''), + songWith(id: 9, dateModified: 0, dateAdded: 0, title: '', artist: 'A', album: ''), + songWith(id: 10, dateModified: 0, dateAdded: 0, title: '', artist: 'AA', album: ''), + songWith(id: 11, dateModified: 0, dateAdded: 0, title: '', artist: 'a', album: ''), + songWith(id: 12, dateModified: 0, dateAdded: 0, title: '', artist: 'z', album: ''), + ], + }, + }; + for (final MapEntry(key: sortOrder, value: featureTestCases) in testCases.entries) { + for (final MapEntry(key: feature, value: expectedContentList) in featureTestCases.entries) { + test('Sorts songs by ${feature.name} ${sortOrder.name}', () async { + expect( + (songs.toList()..sort(SongSort(feature: feature, order: sortOrder).comparator)) + .map((song) => song.toMap()) + .toList(), + expectedContentList.map((song) => song.toMap()).toList(), + ); + }); + } + } + }); + + group('Album sorting', () { + late List albums; + setUp(() { + albums = [ + albumWith(id: 0, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 1, album: 'A', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 2, album: 'AA', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 3, album: 'a', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 4, album: 'z', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 5, album: '', artist: 'A', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 6, album: '', artist: 'AA', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 7, album: '', artist: 'a', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 8, album: '', artist: 'z', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 9, album: '', artist: '', lastYear: 1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 10, album: '', artist: '', lastYear: -1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 11, album: '', artist: '', lastYear: null, firstYear: 0, numberOfSongs: 0), + albumWith(id: 12, album: '', artist: '', lastYear: null, firstYear: 1, numberOfSongs: 0), + albumWith(id: 13, album: '', artist: '', lastYear: null, firstYear: -1, numberOfSongs: 0), + albumWith(id: 14, album: '', artist: '', lastYear: null, firstYear: null, numberOfSongs: 0), + albumWith(id: 15, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 1), + albumWith(id: 16, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: -1), + ]; + }); + final Map>> testCases = { + SortOrder.ascending: { + AlbumSortFeature.title: [ + albumWith(id: 10, album: '', artist: '', lastYear: -1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 13, album: '', artist: '', lastYear: null, firstYear: -1, numberOfSongs: 0), + albumWith(id: 0, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 5, album: '', artist: 'A', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 6, album: '', artist: 'AA', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 7, album: '', artist: 'a', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 8, album: '', artist: 'z', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 11, album: '', artist: '', lastYear: null, firstYear: 0, numberOfSongs: 0), + albumWith(id: 14, album: '', artist: '', lastYear: null, firstYear: null, numberOfSongs: 0), + albumWith(id: 15, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 1), + albumWith(id: 16, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: -1), + albumWith(id: 9, album: '', artist: '', lastYear: 1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 12, album: '', artist: '', lastYear: null, firstYear: 1, numberOfSongs: 0), + albumWith(id: 1, album: 'A', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 3, album: 'a', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 2, album: 'AA', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 4, album: 'z', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + ], + AlbumSortFeature.artist: [ + albumWith(id: 10, album: '', artist: '', lastYear: -1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 13, album: '', artist: '', lastYear: null, firstYear: -1, numberOfSongs: 0), + albumWith(id: 0, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 1, album: 'A', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 2, album: 'AA', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 3, album: 'a', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 4, album: 'z', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 11, album: '', artist: '', lastYear: null, firstYear: 0, numberOfSongs: 0), + albumWith(id: 14, album: '', artist: '', lastYear: null, firstYear: null, numberOfSongs: 0), + albumWith(id: 15, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 1), + albumWith(id: 16, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: -1), + albumWith(id: 9, album: '', artist: '', lastYear: 1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 12, album: '', artist: '', lastYear: null, firstYear: 1, numberOfSongs: 0), + albumWith(id: 5, album: '', artist: 'A', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 7, album: '', artist: 'a', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 6, album: '', artist: 'AA', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 8, album: '', artist: 'z', lastYear: 0, firstYear: 0, numberOfSongs: 0), + ], + AlbumSortFeature.year: [ + albumWith(id: 10, album: '', artist: '', lastYear: -1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 13, album: '', artist: '', lastYear: null, firstYear: -1, numberOfSongs: 0), + albumWith(id: 0, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 5, album: '', artist: 'A', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 6, album: '', artist: 'AA', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 7, album: '', artist: 'a', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 8, album: '', artist: 'z', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 11, album: '', artist: '', lastYear: null, firstYear: 0, numberOfSongs: 0), + albumWith(id: 14, album: '', artist: '', lastYear: null, firstYear: null, numberOfSongs: 0), + albumWith(id: 15, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 1), + albumWith(id: 16, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: -1), + albumWith(id: 1, album: 'A', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 3, album: 'a', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 2, album: 'AA', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 4, album: 'z', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 9, album: '', artist: '', lastYear: 1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 12, album: '', artist: '', lastYear: null, firstYear: 1, numberOfSongs: 0), + ], + AlbumSortFeature.numberOfSongs: [ + albumWith(id: 16, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: -1), + albumWith(id: 10, album: '', artist: '', lastYear: -1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 13, album: '', artist: '', lastYear: null, firstYear: -1, numberOfSongs: 0), + albumWith(id: 0, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 1, album: 'A', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 2, album: 'AA', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 3, album: 'a', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 4, album: 'z', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 5, album: '', artist: 'A', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 6, album: '', artist: 'AA', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 7, album: '', artist: 'a', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 8, album: '', artist: 'z', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 11, album: '', artist: '', lastYear: null, firstYear: 0, numberOfSongs: 0), + albumWith(id: 14, album: '', artist: '', lastYear: null, firstYear: null, numberOfSongs: 0), + albumWith(id: 9, album: '', artist: '', lastYear: 1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 12, album: '', artist: '', lastYear: null, firstYear: 1, numberOfSongs: 0), + albumWith(id: 15, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 1), + ], + }, + SortOrder.descending: { + AlbumSortFeature.title: [ + albumWith(id: 4, album: 'z', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 2, album: 'AA', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 1, album: 'A', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 3, album: 'a', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 9, album: '', artist: '', lastYear: 1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 12, album: '', artist: '', lastYear: null, firstYear: 1, numberOfSongs: 0), + albumWith(id: 0, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 5, album: '', artist: 'A', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 6, album: '', artist: 'AA', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 7, album: '', artist: 'a', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 8, album: '', artist: 'z', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 11, album: '', artist: '', lastYear: null, firstYear: 0, numberOfSongs: 0), + albumWith(id: 14, album: '', artist: '', lastYear: null, firstYear: null, numberOfSongs: 0), + albumWith(id: 15, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 1), + albumWith(id: 16, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: -1), + albumWith(id: 10, album: '', artist: '', lastYear: -1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 13, album: '', artist: '', lastYear: null, firstYear: -1, numberOfSongs: 0), + ], + AlbumSortFeature.artist: [ + albumWith(id: 8, album: '', artist: 'z', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 6, album: '', artist: 'AA', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 5, album: '', artist: 'A', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 7, album: '', artist: 'a', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 9, album: '', artist: '', lastYear: 1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 12, album: '', artist: '', lastYear: null, firstYear: 1, numberOfSongs: 0), + albumWith(id: 0, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 1, album: 'A', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 2, album: 'AA', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 3, album: 'a', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 4, album: 'z', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 11, album: '', artist: '', lastYear: null, firstYear: 0, numberOfSongs: 0), + albumWith(id: 14, album: '', artist: '', lastYear: null, firstYear: null, numberOfSongs: 0), + albumWith(id: 15, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 1), + albumWith(id: 16, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: -1), + albumWith(id: 10, album: '', artist: '', lastYear: -1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 13, album: '', artist: '', lastYear: null, firstYear: -1, numberOfSongs: 0), + ], + AlbumSortFeature.year: [ + albumWith(id: 9, album: '', artist: '', lastYear: 1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 12, album: '', artist: '', lastYear: null, firstYear: 1, numberOfSongs: 0), + albumWith(id: 4, album: 'z', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 2, album: 'AA', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 1, album: 'A', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 3, album: 'a', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 0, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 5, album: '', artist: 'A', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 6, album: '', artist: 'AA', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 7, album: '', artist: 'a', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 8, album: '', artist: 'z', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 11, album: '', artist: '', lastYear: null, firstYear: 0, numberOfSongs: 0), + albumWith(id: 14, album: '', artist: '', lastYear: null, firstYear: null, numberOfSongs: 0), + albumWith(id: 15, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 1), + albumWith(id: 16, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: -1), + albumWith(id: 10, album: '', artist: '', lastYear: -1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 13, album: '', artist: '', lastYear: null, firstYear: -1, numberOfSongs: 0), + ], + AlbumSortFeature.numberOfSongs: [ + albumWith(id: 15, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 1), + albumWith(id: 9, album: '', artist: '', lastYear: 1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 12, album: '', artist: '', lastYear: null, firstYear: 1, numberOfSongs: 0), + albumWith(id: 0, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 1, album: 'A', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 2, album: 'AA', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 3, album: 'a', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 4, album: 'z', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 5, album: '', artist: 'A', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 6, album: '', artist: 'AA', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 7, album: '', artist: 'a', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 8, album: '', artist: 'z', lastYear: 0, firstYear: 0, numberOfSongs: 0), + albumWith(id: 11, album: '', artist: '', lastYear: null, firstYear: 0, numberOfSongs: 0), + albumWith(id: 14, album: '', artist: '', lastYear: null, firstYear: null, numberOfSongs: 0), + albumWith(id: 10, album: '', artist: '', lastYear: -1, firstYear: 0, numberOfSongs: 0), + albumWith(id: 13, album: '', artist: '', lastYear: null, firstYear: -1, numberOfSongs: 0), + albumWith(id: 16, album: '', artist: '', lastYear: 0, firstYear: 0, numberOfSongs: -1), + ], + }, + }; + for (final MapEntry(key: sortOrder, value: featureTestCases) in testCases.entries) { + for (final MapEntry(key: feature, value: expectedContentList) in featureTestCases.entries) { + test('Sorts albums by ${feature.name} ${sortOrder.name}', () async { + expect( + (albums.toList()..sort(AlbumSort(feature: feature, order: sortOrder).comparator)) + .map((album) => album.toMap()) + .toList(), + expectedContentList.map((album) => album.toMap()).toList(), + ); + }); + } + } + }); + + group('Playlist sorting', () { + late List playlists; + setUp(() { + playlists = [ + playlistWith(id: 0, name: '', dateModified: 0, dateAdded: 0), + playlistWith(id: 1, name: 'A', dateModified: 0, dateAdded: 0), + playlistWith(id: 2, name: 'AA', dateModified: 0, dateAdded: 0), + playlistWith(id: 3, name: 'a', dateModified: 0, dateAdded: 0), + playlistWith(id: 4, name: 'z', dateModified: 0, dateAdded: 0), + playlistWith(id: 5, name: '', dateModified: 1, dateAdded: 0), + playlistWith(id: 6, name: '', dateModified: -1, dateAdded: 0), + playlistWith(id: 7, name: '', dateModified: 0, dateAdded: 1), + playlistWith(id: 8, name: '', dateModified: 0, dateAdded: -1), + ]; + }); + final Map>> testCases = { + SortOrder.ascending: { + PlaylistSortFeature.dateModified: [ + playlistWith(id: 6, name: '', dateModified: -1, dateAdded: 0), + playlistWith(id: 0, name: '', dateModified: 0, dateAdded: 0), + playlistWith(id: 7, name: '', dateModified: 0, dateAdded: 1), + playlistWith(id: 8, name: '', dateModified: 0, dateAdded: -1), + playlistWith(id: 1, name: 'A', dateModified: 0, dateAdded: 0), + playlistWith(id: 3, name: 'a', dateModified: 0, dateAdded: 0), + playlistWith(id: 2, name: 'AA', dateModified: 0, dateAdded: 0), + playlistWith(id: 4, name: 'z', dateModified: 0, dateAdded: 0), + playlistWith(id: 5, name: '', dateModified: 1, dateAdded: 0), + ], + PlaylistSortFeature.dateAdded: [ + playlistWith(id: 8, name: '', dateModified: 0, dateAdded: -1), + playlistWith(id: 0, name: '', dateModified: 0, dateAdded: 0), + playlistWith(id: 5, name: '', dateModified: 1, dateAdded: 0), + playlistWith(id: 6, name: '', dateModified: -1, dateAdded: 0), + playlistWith(id: 1, name: 'A', dateModified: 0, dateAdded: 0), + playlistWith(id: 3, name: 'a', dateModified: 0, dateAdded: 0), + playlistWith(id: 2, name: 'AA', dateModified: 0, dateAdded: 0), + playlistWith(id: 4, name: 'z', dateModified: 0, dateAdded: 0), + playlistWith(id: 7, name: '', dateModified: 0, dateAdded: 1), + ], + PlaylistSortFeature.name: [ + playlistWith(id: 6, name: '', dateModified: -1, dateAdded: 0), + playlistWith(id: 0, name: '', dateModified: 0, dateAdded: 0), + playlistWith(id: 7, name: '', dateModified: 0, dateAdded: 1), + playlistWith(id: 8, name: '', dateModified: 0, dateAdded: -1), + playlistWith(id: 5, name: '', dateModified: 1, dateAdded: 0), + playlistWith(id: 1, name: 'A', dateModified: 0, dateAdded: 0), + playlistWith(id: 3, name: 'a', dateModified: 0, dateAdded: 0), + playlistWith(id: 2, name: 'AA', dateModified: 0, dateAdded: 0), + playlistWith(id: 4, name: 'z', dateModified: 0, dateAdded: 0), + ], + }, + SortOrder.descending: { + PlaylistSortFeature.dateModified: [ + playlistWith(id: 5, name: '', dateModified: 1, dateAdded: 0), + playlistWith(id: 4, name: 'z', dateModified: 0, dateAdded: 0), + playlistWith(id: 2, name: 'AA', dateModified: 0, dateAdded: 0), + playlistWith(id: 1, name: 'A', dateModified: 0, dateAdded: 0), + playlistWith(id: 3, name: 'a', dateModified: 0, dateAdded: 0), + playlistWith(id: 0, name: '', dateModified: 0, dateAdded: 0), + playlistWith(id: 7, name: '', dateModified: 0, dateAdded: 1), + playlistWith(id: 8, name: '', dateModified: 0, dateAdded: -1), + playlistWith(id: 6, name: '', dateModified: -1, dateAdded: 0), + ], + PlaylistSortFeature.dateAdded: [ + playlistWith(id: 7, name: '', dateModified: 0, dateAdded: 1), + playlistWith(id: 4, name: 'z', dateModified: 0, dateAdded: 0), + playlistWith(id: 2, name: 'AA', dateModified: 0, dateAdded: 0), + playlistWith(id: 1, name: 'A', dateModified: 0, dateAdded: 0), + playlistWith(id: 3, name: 'a', dateModified: 0, dateAdded: 0), + playlistWith(id: 0, name: '', dateModified: 0, dateAdded: 0), + playlistWith(id: 5, name: '', dateModified: 1, dateAdded: 0), + playlistWith(id: 6, name: '', dateModified: -1, dateAdded: 0), + playlistWith(id: 8, name: '', dateModified: 0, dateAdded: -1), + ], + PlaylistSortFeature.name: [ + playlistWith(id: 4, name: 'z', dateModified: 0, dateAdded: 0), + playlistWith(id: 2, name: 'AA', dateModified: 0, dateAdded: 0), + playlistWith(id: 1, name: 'A', dateModified: 0, dateAdded: 0), + playlistWith(id: 3, name: 'a', dateModified: 0, dateAdded: 0), + playlistWith(id: 5, name: '', dateModified: 1, dateAdded: 0), + playlistWith(id: 0, name: '', dateModified: 0, dateAdded: 0), + playlistWith(id: 7, name: '', dateModified: 0, dateAdded: 1), + playlistWith(id: 8, name: '', dateModified: 0, dateAdded: -1), + playlistWith(id: 6, name: '', dateModified: -1, dateAdded: 0), + ], + }, + }; + for (final MapEntry(key: sortOrder, value: featureTestCases) in testCases.entries) { + for (final MapEntry(key: feature, value: expectedContentList) in featureTestCases.entries) { + test('Sorts playlists by ${feature.name} ${sortOrder.name}', () async { + expect( + (playlists.toList()..sort(PlaylistSort(feature: feature, order: sortOrder).comparator)) + .map((playlist) => playlist.toMap()) + .toList(), + expectedContentList.map((playlist) => playlist.toMap()).toList(), + ); + }); + } + } + }); + + group('Artist sorting', () { + late List artists; + setUp(() { + artists = [ + artistWith(id: 0, artist: '', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 1, artist: 'A', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 2, artist: 'AA', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 3, artist: 'a', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 4, artist: 'z', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 5, artist: '', numberOfAlbums: 1, numberOfTracks: 0), + artistWith(id: 6, artist: '', numberOfAlbums: -1, numberOfTracks: 0), + artistWith(id: 7, artist: '', numberOfAlbums: 0, numberOfTracks: 1), + artistWith(id: 8, artist: '', numberOfAlbums: 0, numberOfTracks: -1), + ]; + }); + final Map>> testCases = { + SortOrder.ascending: { + ArtistSortFeature.name: [ + artistWith(id: 8, artist: '', numberOfAlbums: 0, numberOfTracks: -1), + artistWith(id: 0, artist: '', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 5, artist: '', numberOfAlbums: 1, numberOfTracks: 0), + artistWith(id: 6, artist: '', numberOfAlbums: -1, numberOfTracks: 0), + artistWith(id: 7, artist: '', numberOfAlbums: 0, numberOfTracks: 1), + artistWith(id: 1, artist: 'A', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 3, artist: 'a', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 2, artist: 'AA', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 4, artist: 'z', numberOfAlbums: 0, numberOfTracks: 0), + ], + ArtistSortFeature.numberOfAlbums: [ + artistWith(id: 6, artist: '', numberOfAlbums: -1, numberOfTracks: 0), + artistWith(id: 0, artist: '', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 7, artist: '', numberOfAlbums: 0, numberOfTracks: 1), + artistWith(id: 8, artist: '', numberOfAlbums: 0, numberOfTracks: -1), + artistWith(id: 1, artist: 'A', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 3, artist: 'a', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 2, artist: 'AA', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 4, artist: 'z', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 5, artist: '', numberOfAlbums: 1, numberOfTracks: 0), + ], + ArtistSortFeature.numberOfTracks: [ + artistWith(id: 8, artist: '', numberOfAlbums: 0, numberOfTracks: -1), + artistWith(id: 0, artist: '', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 5, artist: '', numberOfAlbums: 1, numberOfTracks: 0), + artistWith(id: 6, artist: '', numberOfAlbums: -1, numberOfTracks: 0), + artistWith(id: 1, artist: 'A', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 3, artist: 'a', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 2, artist: 'AA', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 4, artist: 'z', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 7, artist: '', numberOfAlbums: 0, numberOfTracks: 1), + ], + }, + SortOrder.descending: { + ArtistSortFeature.name: [ + artistWith(id: 4, artist: 'z', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 2, artist: 'AA', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 1, artist: 'A', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 3, artist: 'a', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 7, artist: '', numberOfAlbums: 0, numberOfTracks: 1), + artistWith(id: 0, artist: '', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 5, artist: '', numberOfAlbums: 1, numberOfTracks: 0), + artistWith(id: 6, artist: '', numberOfAlbums: -1, numberOfTracks: 0), + artistWith(id: 8, artist: '', numberOfAlbums: 0, numberOfTracks: -1), + ], + ArtistSortFeature.numberOfAlbums: [ + artistWith(id: 5, artist: '', numberOfAlbums: 1, numberOfTracks: 0), + artistWith(id: 4, artist: 'z', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 2, artist: 'AA', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 1, artist: 'A', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 3, artist: 'a', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 0, artist: '', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 7, artist: '', numberOfAlbums: 0, numberOfTracks: 1), + artistWith(id: 8, artist: '', numberOfAlbums: 0, numberOfTracks: -1), + artistWith(id: 6, artist: '', numberOfAlbums: -1, numberOfTracks: 0), + ], + ArtistSortFeature.numberOfTracks: [ + artistWith(id: 7, artist: '', numberOfAlbums: 0, numberOfTracks: 1), + artistWith(id: 4, artist: 'z', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 2, artist: 'AA', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 1, artist: 'A', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 3, artist: 'a', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 0, artist: '', numberOfAlbums: 0, numberOfTracks: 0), + artistWith(id: 5, artist: '', numberOfAlbums: 1, numberOfTracks: 0), + artistWith(id: 6, artist: '', numberOfAlbums: -1, numberOfTracks: 0), + artistWith(id: 8, artist: '', numberOfAlbums: 0, numberOfTracks: -1), + ], + }, + }; + for (final MapEntry(key: sortOrder, value: featureTestCases) in testCases.entries) { + for (final MapEntry(key: feature, value: expectedContentList) in featureTestCases.entries) { + test('Sorts artists by ${feature.name} ${sortOrder.name}', () async { + expect( + (artists.toList()..sort(ArtistSort(feature: feature, order: sortOrder).comparator)) + .map((artist) => artist.toMap()) + .toList(), + expectedContentList.map((artist) => artist.toMap()).toList(), + ); + }); + } + } + }); +}