From b3f432a5451928e554066b18fe17db2f4d95a7a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Thomas?= Date: Tue, 22 May 2018 23:17:08 +0200 Subject: [PATCH] Issue #164: Add tests for reading MusicBrainz release tags --- .../airsonic/player/dao/MusicFolderDao.java | 10 ++++ .../player/dao/MusicFolderTestData.java | 4 ++ .../service/MediaScannerServiceTestCase.java | 53 +++++++++++++++++- .../MEDIAS/Music3/TestAlbum/01 - Aria.flac | Bin 0 -> 19328 bytes 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 airsonic-main/src/test/resources/MEDIAS/Music3/TestAlbum/01 - Aria.flac diff --git a/airsonic-main/src/main/java/org/airsonic/player/dao/MusicFolderDao.java b/airsonic-main/src/main/java/org/airsonic/player/dao/MusicFolderDao.java index 8e380bae..0135f024 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/dao/MusicFolderDao.java +++ b/airsonic-main/src/main/java/org/airsonic/player/dao/MusicFolderDao.java @@ -57,6 +57,16 @@ public class MusicFolderDao extends AbstractDao { return query(sql, rowMapper); } + /** + * Return the music folder a the given path + * + * @return Possibly null instance of MusicFolder + */ + public MusicFolder getMusicFolderForPath(String path) { + String sql = "select " + QUERY_COLUMNS + " from music_folder where path = ?"; + return queryOne(sql, rowMapper, path); + } + /** * Creates a new music folder. * diff --git a/airsonic-main/src/test/java/org/airsonic/player/dao/MusicFolderTestData.java b/airsonic-main/src/test/java/org/airsonic/player/dao/MusicFolderTestData.java index 213fb409..9a789b1f 100644 --- a/airsonic-main/src/test/java/org/airsonic/player/dao/MusicFolderTestData.java +++ b/airsonic-main/src/test/java/org/airsonic/player/dao/MusicFolderTestData.java @@ -24,6 +24,10 @@ public class MusicFolderTestData { return (MusicFolderTestData.resolveBaseMediaPath() + "Music2"); } + public static String resolveMusic3FolderPath() { + return (MusicFolderTestData.resolveBaseMediaPath() + "Music3"); + } + public static List getTestMusicFolders() { List liste = new ArrayList<>(); File musicDir = new File(MusicFolderTestData.resolveMusicFolderPath()); diff --git a/airsonic-main/src/test/java/org/airsonic/player/service/MediaScannerServiceTestCase.java b/airsonic-main/src/test/java/org/airsonic/player/service/MediaScannerServiceTestCase.java index 15c5347c..65c129d3 100644 --- a/airsonic-main/src/test/java/org/airsonic/player/service/MediaScannerServiceTestCase.java +++ b/airsonic-main/src/test/java/org/airsonic/player/service/MediaScannerServiceTestCase.java @@ -30,6 +30,7 @@ import java.io.File; import java.io.FileOutputStream; import java.util.Date; import java.util.List; +import java.util.ArrayList; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -138,7 +139,7 @@ public class MediaScannerServiceTestCase { System.out.println("--- List of all albums ---"); System.out.println("name#artist"); - List allAlbums = albumDao.getAlphabetialAlbums(0, 0, true, musicFolderDao.getAllMusicFolders()); + List allAlbums = albumDao.getAlphabeticalAlbums(0, 0, true, true, musicFolderDao.getAllMusicFolders()); allAlbums.forEach(album -> System.out.println(album.getName() + "#" + album.getArtist())); Assert.assertEquals(5, allAlbums.size()); System.out.println("--- *********************** ---"); @@ -174,4 +175,54 @@ public class MediaScannerServiceTestCase { System.out.println(mediaFile.getFile().getPath()); assertNotNull(mediaFile); } + + @Test + public void testMusicBrainzReleaseIdTag() { + + // Add the "Music3" folder to the database + File musicFolderFile = new File(MusicFolderTestData.resolveMusic3FolderPath()); + MusicFolder musicFolder = new MusicFolder(1, musicFolderFile, "Music3", true, new Date()); + musicFolderDao.createMusicFolder(musicFolder); + settingsService.clearMusicFolderCache(); + TestCaseUtils.execScan(mediaScannerService); + + // Retrieve the "Music3" folder from the database to make + // sure that we don't accidentally operate on other folders + // from previous tests. + musicFolder = musicFolderDao.getMusicFolderForPath(musicFolder.getPath().getPath()); + List folders = new ArrayList<>(); + folders.add(musicFolder); + + // Test that the artist is correctly imported + List allArtists = artistDao.getAlphabetialArtists(0, 0, folders); + Assert.assertEquals(1, allArtists.size()); + Artist artist = allArtists.get(0); + Assert.assertEquals("TestArtist", artist.getName()); + Assert.assertEquals(1, artist.getAlbumCount()); + + // Test that the album is correctly imported, along with its MusicBrainz release ID + List allAlbums = albumDao.getAlphabeticalAlbums(0, 0, true, true, folders); + Assert.assertEquals(1, allAlbums.size()); + Album album = allAlbums.get(0); + Assert.assertEquals("TestAlbum", album.getName()); + Assert.assertEquals("TestArtist", album.getArtist()); + Assert.assertEquals(1, album.getSongCount()); + Assert.assertEquals("0820752d-1043-4572-ab36-2df3b5cc15fa", album.getMusicBrainzReleaseId()); + Assert.assertEquals(musicFolderFile.toPath().resolve("TestAlbum").toString(), album.getPath()); + + // Test that the music file is correctly imported, along with its MusicBrainz release ID + List albumFiles = mediaFileDao.getChildrenOf(allAlbums.get(0).getPath()); + Assert.assertEquals(1, albumFiles.size()); + MediaFile file = albumFiles.get(0); + Assert.assertEquals("Aria", file.getTitle()); + Assert.assertEquals("flac", file.getFormat()); + Assert.assertEquals("TestAlbum", file.getAlbumName()); + Assert.assertEquals("TestArtist", file.getArtist()); + Assert.assertEquals("TestArtist", file.getAlbumArtist()); + Assert.assertEquals(1, (long)file.getTrackNumber()); + Assert.assertEquals(2001, (long)file.getYear()); + Assert.assertEquals(album.getPath(), file.getParentPath()); + Assert.assertEquals(new File(album.getPath()).toPath().resolve("01 - Aria.flac").toString(), file.getPath()); + Assert.assertEquals("0820752d-1043-4572-ab36-2df3b5cc15fa", file.getMusicBrainzReleaseId()); + } } diff --git a/airsonic-main/src/test/resources/MEDIAS/Music3/TestAlbum/01 - Aria.flac b/airsonic-main/src/test/resources/MEDIAS/Music3/TestAlbum/01 - Aria.flac new file mode 100644 index 0000000000000000000000000000000000000000..26d3346ba2d97db439e63fe8e53378efcfb0c349 GIT binary patch literal 19328 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 AmjD0& literal 0 HcmV?d00001