Refactored database implementation to further hide implementation details from other classes and allow for drop-in replacement.
This commit is contained in:
parent
5e80829db0
commit
e8633aa0e1
@ -5,8 +5,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import musicplayer.callbacks.LibraryCallbackInterface;
|
||||
import musicplayer.callbacks.PlayerCallbackInterface;
|
||||
import musicplayer.db.DatabaseManager;
|
||||
import musicplayer.db.Gateway;
|
||||
import musicplayer.db.IDatabase;
|
||||
import musicplayer.model.Album;
|
||||
import musicplayer.model.Artist;
|
||||
import musicplayer.model.HasSongs;
|
||||
@ -52,6 +51,7 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
|
||||
private JSlider seekBar;
|
||||
private final JComboBox<String> libraryDisplayType = new JComboBox<>();
|
||||
private final IPlayer player;
|
||||
private final IDatabase database;
|
||||
private final PlaylistTableModel playlistTableModel = new PlaylistTableModel(new ArrayList<>());
|
||||
private static final DefaultMutableTreeNode updatingNode = new DefaultMutableTreeNode();
|
||||
private boolean libraryUpdating = false;
|
||||
@ -60,8 +60,9 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
|
||||
private final Map<String, Runnable> libraryDisplayVariants = createDisplayVariantMap();
|
||||
|
||||
@Inject
|
||||
public PlayerGUI(IPlayer player) {
|
||||
public PlayerGUI(IPlayer player, IDatabase database) {
|
||||
this.player = player;
|
||||
this.database = database;
|
||||
createUI();
|
||||
resetTree();
|
||||
Thread seekBarUpdater = new Thread(() -> {
|
||||
@ -102,7 +103,6 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
DatabaseManager.init();
|
||||
Injector injector = Guice.createInjector();
|
||||
PlayerGUI playerGUI = injector.getInstance(PlayerGUI.class);
|
||||
JFrame frame = new JFrame();
|
||||
@ -269,7 +269,7 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
|
||||
addNodeToTreeModel(model, parentNode, updatingNode);
|
||||
libraryView.setModel(model);
|
||||
List<Path> dirs = ConfigManager.getLibraryDirectories().stream().map(File::toPath).collect(Collectors.toList());
|
||||
LibraryUtils.processSongsWithCallback(dirs, this);
|
||||
LibraryUtils.processSongsWithCallback(database, dirs, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -290,9 +290,9 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
|
||||
*/
|
||||
private Map<String, Runnable> createDisplayVariantMap() {
|
||||
Map<String, Runnable> value = new HashMap<>();
|
||||
value.put("Song", () -> Gateway.listAllT(Song.class).ifPresent(this::populateLibraryWithSongsOnly));
|
||||
value.put("Album/Song", () -> Gateway.listAllT(Album.class).ifPresent(this::populateLibraryWithGroupedSongs));
|
||||
value.put("Artist/Song", () -> Gateway.listAllT(Artist.class).ifPresent(this::populateLibraryWithGroupedSongs));
|
||||
value.put("Song", () -> database.listAllT(Song.class).ifPresent(this::populateLibraryWithSongsOnly));
|
||||
value.put("Album/Song", () -> database.listAllT(Album.class).ifPresent(this::populateLibraryWithGroupedSongs));
|
||||
value.put("Artist/Song", () -> database.listAllT(Artist.class).ifPresent(this::populateLibraryWithGroupedSongs));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
@ -1,82 +0,0 @@
|
||||
package musicplayer.db;
|
||||
|
||||
import musicplayer.ConfigManager;
|
||||
import musicplayer.model.Album;
|
||||
import musicplayer.model.Artist;
|
||||
import musicplayer.model.Song;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
/**
|
||||
* Singleton for managing connection to Hibernate.
|
||||
*/
|
||||
public class DatabaseManager {
|
||||
private static final DatabaseManager ourInstance = new DatabaseManager();
|
||||
|
||||
private SessionFactory sessionFactory;
|
||||
|
||||
public static DatabaseManager getInstance() {
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
private DatabaseManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a SessionFactory to the database as defined in hibernate.cfg.xml
|
||||
*/
|
||||
public static void init() {
|
||||
if (getInstance().sessionFactory != null)
|
||||
getInstance().sessionFactory.close();
|
||||
Properties properties = new Properties();
|
||||
properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
|
||||
properties.put("hibernate.connection.driver_class", "org.hsqldb.jdbc.JDBCDriver");
|
||||
properties.put("hibernate.connection.url", ConfigManager.getDatabaseConnectionString());
|
||||
properties.put("hibernate.hbm2ddl.auto", "update");
|
||||
properties.put("hibernate.enable_lazy_load_no_trans", "true");
|
||||
getInstance().sessionFactory = new Configuration()
|
||||
.addProperties(properties)
|
||||
.addAnnotatedClass(Album.class)
|
||||
.addAnnotatedClass(Artist.class)
|
||||
.addAnnotatedClass(Song.class)
|
||||
.buildSessionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a SessionFactory to an in-memory database for testing
|
||||
*/
|
||||
public static void testMode() {
|
||||
if (getInstance().sessionFactory != null)
|
||||
getInstance().sessionFactory.close();
|
||||
Properties properties = new Properties();
|
||||
properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
|
||||
properties.put("hibernate.connection.driver_class", "org.hsqldb.jdbc.JDBCDriver");
|
||||
properties.put("hibernate.connection.url", "jdbc:hsqldb:mem:.");
|
||||
properties.put("hibernate.hbm2ddl.auto", "create-drop");
|
||||
properties.put("hibernate.enable_lazy_load_no_trans", "true");
|
||||
getInstance().sessionFactory = new Configuration()
|
||||
.addProperties(properties)
|
||||
.addAnnotatedClass(Album.class)
|
||||
.addAnnotatedClass(Artist.class)
|
||||
.addAnnotatedClass(Song.class)
|
||||
.buildSessionFactory();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Session for database interaction.
|
||||
*/
|
||||
public Session getSession() {
|
||||
return sessionFactory.openSession();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CloneDoesntCallSuperClone")
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
throw new CloneNotSupportedException();
|
||||
}
|
||||
}
|
@ -1,26 +1,77 @@
|
||||
package musicplayer.db;
|
||||
|
||||
import musicplayer.ConfigManager;
|
||||
import musicplayer.model.*;
|
||||
import musicplayer.util.AlbumArtExtractor;
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
/**
|
||||
* Contains database queries.
|
||||
* Class for managing connection to Hibernate.
|
||||
*/
|
||||
public class Gateway {
|
||||
class HibernateDatabase implements IDatabase{
|
||||
|
||||
private static SessionFactory sessionFactory;
|
||||
|
||||
/**
|
||||
* Create a database connection.
|
||||
*/
|
||||
public HibernateDatabase() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a database connection.
|
||||
* @param testMode Enable test mode.
|
||||
*/
|
||||
public HibernateDatabase(boolean testMode){
|
||||
Properties properties = new Properties();
|
||||
if (sessionFactory != null)
|
||||
sessionFactory.close();
|
||||
if(testMode){
|
||||
properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
|
||||
properties.put("hibernate.connection.driver_class", "org.hsqldb.jdbc.JDBCDriver");
|
||||
properties.put("hibernate.connection.url", "jdbc:hsqldb:mem:.");
|
||||
properties.put("hibernate.hbm2ddl.auto", "create-drop");
|
||||
properties.put("hibernate.enable_lazy_load_no_trans", "true");
|
||||
}
|
||||
else{
|
||||
properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
|
||||
properties.put("hibernate.connection.driver_class", "org.hsqldb.jdbc.JDBCDriver");
|
||||
properties.put("hibernate.connection.url", ConfigManager.getDatabaseConnectionString());
|
||||
properties.put("hibernate.hbm2ddl.auto", "update");
|
||||
properties.put("hibernate.enable_lazy_load_no_trans", "true");
|
||||
}
|
||||
sessionFactory = new Configuration()
|
||||
.addProperties(properties)
|
||||
.addAnnotatedClass(Album.class)
|
||||
.addAnnotatedClass(Artist.class)
|
||||
.addAnnotatedClass(Song.class)
|
||||
.buildSessionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Session for database interaction.
|
||||
*/
|
||||
private Session getSession() {
|
||||
return sessionFactory.openSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name Name of the album to find.
|
||||
* @return Album found with name or Optional.empty()
|
||||
*/
|
||||
public static Optional<Album> getOneAlbum(String name) {
|
||||
try (Session session = DatabaseManager.getInstance().getSession()) {
|
||||
public Optional<Album> getOneAlbum(String name) {
|
||||
try (Session session = getSession()) {
|
||||
Criteria criteria = session.createCriteria(Album.class);
|
||||
Album album = (Album) criteria.add(Restrictions.eq("name", name)).uniqueResult();
|
||||
return (album == null) ? Optional.empty() : Optional.of(album);
|
||||
@ -31,8 +82,8 @@ public class Gateway {
|
||||
* @param name Name of the artist to find.
|
||||
* @return Artist found with name or Optional.empty()
|
||||
*/
|
||||
public static Optional<Artist> getOneArtist(String name) {
|
||||
try (Session session = DatabaseManager.getInstance().getSession()) {
|
||||
public Optional<Artist> getOneArtist(String name) {
|
||||
try (Session session = getSession()) {
|
||||
Criteria criteria = session.createCriteria(Artist.class);
|
||||
Artist artist = (Artist) criteria.add(Restrictions.eq("name", name)).uniqueResult();
|
||||
return (artist == null) ? Optional.empty() : Optional.of(artist);
|
||||
@ -43,8 +94,8 @@ public class Gateway {
|
||||
* @param metadata Metadata of song to find.
|
||||
* @return Song found with metadata or Optional.empty()
|
||||
*/
|
||||
public static Optional<Song> getOneSong(ExtractedMetadata metadata){
|
||||
try (Session session = DatabaseManager.getInstance().getSession()) {
|
||||
public Optional<Song> getOneSong(ExtractedMetadata metadata){
|
||||
try (Session session = getSession()) {
|
||||
Song song = (Song)session.createCriteria(Song.class)
|
||||
.add(Restrictions.eq("songFile", metadata.getSongFile())).uniqueResult();
|
||||
return (song == null) ? Optional.empty() : Optional.of(song);
|
||||
@ -56,8 +107,8 @@ public class Gateway {
|
||||
* @param typeClass Class representing the type of items to find.
|
||||
* @return List of all items in the database of class typeClass.
|
||||
*/
|
||||
public static <T extends IDBType> Optional<List<T>> listAllT(Class<T> typeClass){
|
||||
try (Session session = DatabaseManager.getInstance().getSession()) {
|
||||
public <T extends IDBType> Optional<List<T>> listAllT(Class<T> typeClass){
|
||||
try (Session session = getSession()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<T> output = session.createCriteria(typeClass).list();
|
||||
return (output == null || output.isEmpty()) ? Optional.empty() : Optional.of(output);
|
||||
@ -69,8 +120,8 @@ public class Gateway {
|
||||
* If the song already exists it will be updated instead.
|
||||
* @param metadata New song information.
|
||||
*/
|
||||
public static void addSong(ExtractedMetadata metadata) {
|
||||
try (Session session = DatabaseManager.getInstance().getSession()) {
|
||||
public void addSong(ExtractedMetadata metadata) {
|
||||
try (Session session = getSession()) {
|
||||
session.beginTransaction();
|
||||
Optional<Album> albumObj = getOneAlbum(metadata.getAlbum());
|
||||
if (!albumObj.isPresent()) {
|
||||
@ -92,4 +143,12 @@ public class Gateway {
|
||||
session.getTransaction().commit();
|
||||
}
|
||||
}
|
||||
|
||||
public void addSong(Song song){
|
||||
try (Session session = getSession()) {
|
||||
session.beginTransaction();
|
||||
session.save(song);
|
||||
session.getTransaction().commit();
|
||||
}
|
||||
}
|
||||
}
|
17
src/main/java/musicplayer/db/IDatabase.java
Normal file
17
src/main/java/musicplayer/db/IDatabase.java
Normal file
@ -0,0 +1,17 @@
|
||||
package musicplayer.db;
|
||||
|
||||
import com.google.inject.ImplementedBy;
|
||||
import musicplayer.model.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@ImplementedBy(HibernateDatabase.class)
|
||||
public interface IDatabase {
|
||||
Optional<Album> getOneAlbum(String name);
|
||||
Optional<Artist> getOneArtist(String name);
|
||||
Optional<Song> getOneSong(ExtractedMetadata metadata);
|
||||
<T extends IDBType> Optional<List<T>> listAllT(Class<T> typeClass);
|
||||
void addSong(ExtractedMetadata metadata);
|
||||
void addSong(Song song);
|
||||
}
|
@ -13,7 +13,7 @@ import org.gstreamer.elements.PlayBin2;
|
||||
import java.io.File;
|
||||
import java.util.Optional;
|
||||
|
||||
public class GStreamerPlayer implements IPlayer{
|
||||
class GStreamerPlayer implements IPlayer{
|
||||
|
||||
private final PlayBin2 playBin;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package musicplayer.util;
|
||||
|
||||
import musicplayer.callbacks.LibraryCallbackInterface;
|
||||
import musicplayer.db.Gateway;
|
||||
import musicplayer.db.IDatabase;
|
||||
import musicplayer.model.ExtractedMetadata;
|
||||
import org.jaudiotagger.audio.AudioFileIO;
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||
@ -26,7 +26,7 @@ public final class LibraryUtils {
|
||||
* @param rootDirectory Folder(s) containing music files to index.
|
||||
* @param callbackInterface Object to send callback data to, set to null if the application doesn't require update data.
|
||||
*/
|
||||
public static void processSongsWithCallback(List<Path> rootDirectory, LibraryCallbackInterface callbackInterface){
|
||||
public static void processSongsWithCallback(IDatabase database, List<Path> rootDirectory, LibraryCallbackInterface callbackInterface){
|
||||
boolean callbacksEnabled = callbackInterface != null;
|
||||
updaterThread = new Thread(() -> {
|
||||
rootDirectory.forEach(dir -> {
|
||||
@ -34,7 +34,7 @@ public final class LibraryUtils {
|
||||
Files.walk(dir)
|
||||
.filter(f -> f.toString().matches(musicFileExtensionRegex)).map(LibraryUtils::autoParse)
|
||||
.forEach(x -> x.ifPresent(i -> {
|
||||
Gateway.addSong(i);
|
||||
database.addSong(i);
|
||||
if (callbacksEnabled)
|
||||
callbackInterface.currentlyUpdating(i.toString());
|
||||
}));
|
||||
|
@ -4,11 +4,9 @@ import musicplayer.model.Album;
|
||||
import musicplayer.model.Artist;
|
||||
import musicplayer.model.ExtractedMetadata;
|
||||
import musicplayer.model.Song;
|
||||
import org.hibernate.Session;
|
||||
import org.jaudiotagger.tag.FieldKey;
|
||||
import org.jaudiotagger.tag.Tag;
|
||||
import org.jaudiotagger.tag.id3.ID3v23Tag;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -20,46 +18,37 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class GatewayTest {
|
||||
|
||||
private Session session;
|
||||
private IDatabase database;
|
||||
|
||||
@Before
|
||||
public void setUp(){
|
||||
DatabaseManager.testMode();
|
||||
session = DatabaseManager.getInstance().getSession();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown(){
|
||||
session.close();
|
||||
session = null;
|
||||
database = new HibernateDatabase(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOneAlbum() throws Exception {
|
||||
String albumName = "Test Album";
|
||||
Album testAlbum = new Album(albumName);
|
||||
session.beginTransaction();
|
||||
session.save(testAlbum);
|
||||
session.getTransaction().commit();
|
||||
assertTrue(Gateway.getOneAlbum(albumName).isPresent());
|
||||
Album retrievedAlbum = Gateway.getOneAlbum(albumName).get();
|
||||
Song song = new Song("1", "1", "s1", new Artist("art"), testAlbum, "", "");
|
||||
database.addSong(song);
|
||||
assertTrue(database.getOneAlbum(albumName).isPresent());
|
||||
Album retrievedAlbum = database.getOneAlbum(albumName).get();
|
||||
assertEquals(testAlbum, retrievedAlbum);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOneAlbumEmptyDB() throws Exception {
|
||||
assertTrue(!Gateway.getOneAlbum("").isPresent());
|
||||
assertTrue(!database.getOneAlbum("").isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOneArtist() throws Exception {
|
||||
String artistName = "Test Artist";
|
||||
Artist testArtist = new Artist(artistName);
|
||||
session.beginTransaction();
|
||||
session.save(testArtist);
|
||||
session.getTransaction().commit();
|
||||
assertTrue(Gateway.getOneArtist(artistName).isPresent());
|
||||
Artist retrievedArtist = Gateway.getOneArtist(artistName).get();
|
||||
Song song = new Song("1", "1", "s1", testArtist, new Album("alb"), "", "");
|
||||
database.addSong(song);
|
||||
assertTrue(database.getOneArtist(artistName).isPresent());
|
||||
Artist retrievedArtist = database.getOneArtist(artistName).get();
|
||||
assertEquals(testArtist, retrievedArtist);
|
||||
}
|
||||
|
||||
@ -70,12 +59,10 @@ public class GatewayTest {
|
||||
Song song1 = new Song("1", "1", "s1", artist1, new Album("a"), "", "");
|
||||
Song song2 = new Song("2", "1", "s2", artist1, new Album("b"), "", "");
|
||||
Song song3 = new Song("1", "1", "t1", artist2, new Album("c"), "", "");
|
||||
session.beginTransaction();
|
||||
session.save(song1);
|
||||
session.save(song2);
|
||||
session.save(song3);
|
||||
session.getTransaction().commit();
|
||||
List<Artist> retrievedArtists = Gateway.listAllT(Artist.class).get();
|
||||
database.addSong(song1);
|
||||
database.addSong(song2);
|
||||
database.addSong(song3);
|
||||
List<Artist> retrievedArtists = database.listAllT(Artist.class).get();
|
||||
int index1 = retrievedArtists.indexOf(artist1);
|
||||
int index2 = retrievedArtists.indexOf(artist2);
|
||||
assertTrue(retrievedArtists.get(index1).getSongs().size() == 2);
|
||||
@ -87,7 +74,7 @@ public class GatewayTest {
|
||||
|
||||
@Test
|
||||
public void testGetOneArtistEmptyDB() throws Exception {
|
||||
assertTrue(!Gateway.getOneArtist("").isPresent());
|
||||
assertTrue(!database.getOneArtist("").isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -95,13 +82,11 @@ public class GatewayTest {
|
||||
Song song1 = new Song("1", "1", "s1", new Artist("a"), new Album("a"), "", "");
|
||||
Song song2 = new Song("2", "1","s2", new Artist("b"), new Album("a"), "", "");
|
||||
Song song3 = new Song("1", "1","t1", new Artist("c"), new Album("b"), "", "");
|
||||
session.beginTransaction();
|
||||
session.save(song1);
|
||||
session.save(song2);
|
||||
session.save(song3);
|
||||
session.getTransaction().commit();
|
||||
assertTrue(Gateway.listAllT(Song.class).isPresent());
|
||||
List<Song> result = Gateway.listAllT(Song.class).get();
|
||||
database.addSong(song1);
|
||||
database.addSong(song2);
|
||||
database.addSong(song3);
|
||||
assertTrue(database.listAllT(Song.class).isPresent());
|
||||
List<Song> result = database.listAllT(Song.class).get();
|
||||
assertTrue(result.size() == 3);
|
||||
assertTrue(result.contains(song1));
|
||||
assertTrue(result.contains(song2));
|
||||
@ -110,7 +95,7 @@ public class GatewayTest {
|
||||
|
||||
@Test
|
||||
public void testListAllSongsEmptyDB() throws Exception {
|
||||
assertTrue(!Gateway.listAllT(Song.class).isPresent());
|
||||
assertTrue(!database.listAllT(Song.class).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -120,12 +105,10 @@ public class GatewayTest {
|
||||
Song song1 = new Song("1", "1", "s1", new Artist("a"), album1, "", "");
|
||||
Song song2 = new Song("2", "1", "s2", new Artist("b"), album1, "", "");
|
||||
Song song3 = new Song("1", "1", "t1", new Artist("c"), album2, "", "");
|
||||
session.beginTransaction();
|
||||
session.save(song1);
|
||||
session.save(song2);
|
||||
session.save(song3);
|
||||
session.getTransaction().commit();
|
||||
List<Album> retrievedAlbums = Gateway.listAllT(Album.class).get();
|
||||
database.addSong(song1);
|
||||
database.addSong(song2);
|
||||
database.addSong(song3);
|
||||
List<Album> retrievedAlbums = database.listAllT(Album.class).get();
|
||||
int index1 = retrievedAlbums.indexOf(album1);
|
||||
int index2 = retrievedAlbums.indexOf(album2);
|
||||
assertTrue(retrievedAlbums.get(index1).getSongs().size() == 2);
|
||||
@ -145,8 +128,8 @@ public class GatewayTest {
|
||||
tags.addField(FieldKey.GENRE, "Test Genre");
|
||||
tags.addField(FieldKey.DISC_NO, "100");
|
||||
ExtractedMetadata metadata = new ExtractedMetadata(tags, new File(""));
|
||||
Gateway.addSong(metadata);
|
||||
Song song = (Song)session.createCriteria(Song.class).uniqueResult();
|
||||
database.addSong(metadata);
|
||||
Song song = database.getOneSong(metadata).get();
|
||||
assertEquals(song.getAlbum().getName(), metadata.getAlbum());
|
||||
assertEquals(song.getArtist().getName(), metadata.getArtist());
|
||||
assertEquals(song.getTrackNumber(), Integer.valueOf(1));
|
||||
|
Loading…
Reference in New Issue
Block a user