Unittest/mediaprovider (#251)

Implemented integration tests for music items
This commit is contained in:
Martijn Brekhof 2016-09-23 20:08:22 +02:00 committed by Synced Synapse
parent d746f6643f
commit 080b5809f3
18 changed files with 1229 additions and 158 deletions

View File

@ -30,6 +30,7 @@ import org.xbmc.kore.jsonrpc.type.LibraryType;
import org.xbmc.kore.jsonrpc.type.VideoType;
import org.xbmc.kore.provider.MediaContract;
import org.xbmc.kore.provider.MediaProvider;
import org.xbmc.kore.service.library.SyncMusic;
import org.xbmc.kore.service.library.SyncUtils;
import org.xbmc.kore.utils.LogUtils;
@ -45,12 +46,12 @@ public class Database {
mediaProvider.onCreate();
HostInfo hostInfo = addHost(context);
SyncMusic syncMusic = new SyncMusic(hostInfo.getId(), null);
insertMovies(context, hostInfo.getId());
insertArtists(context, hostInfo.getId());
insertGenres(context, hostInfo.getId());
insertAlbums(context, hostInfo.getId());
insertSongs(context, hostInfo.getId());
insertArtists(context, syncMusic);
insertGenres(context, syncMusic);
insertAlbums(context, syncMusic);
insertSongs(context, syncMusic);
return hostInfo;
}
@ -96,86 +97,34 @@ public class Database {
context.getContentResolver().bulkInsert(MediaContract.MovieCast.CONTENT_URI, movieCastValuesBatch);
}
private static void insertArtists(Context context, int hostId) throws ApiException, IOException {
private static void insertArtists(Context context, SyncMusic syncMusic) throws ApiException, IOException {
AudioLibrary.GetArtists getArtists = new AudioLibrary.GetArtists(false);
String result = Utils.readFile(context, "AudioLibrary.GetArtists.json");
ArrayList<AudioType.DetailsArtist> artistList = (ArrayList) getArtists.resultFromJson(result).items;
ContentValues artistValuesBatch[] = new ContentValues[artistList.size()];
for (int i = 0; i < artistList.size(); i++) {
AudioType.DetailsArtist artist = artistList.get(i);
artistValuesBatch[i] = SyncUtils.contentValuesFromArtist(hostId, artist);
syncMusic.insertArtists(artistList, context.getContentResolver());
}
context.getContentResolver().bulkInsert(MediaContract.Artists.CONTENT_URI, artistValuesBatch);
}
private static void insertGenres(Context context, int hostId) throws ApiException, IOException {
private static void insertGenres(Context context, SyncMusic syncMusic) throws ApiException, IOException {
AudioLibrary.GetGenres getGenres = new AudioLibrary.GetGenres();
ArrayList<LibraryType.DetailsGenre> genreList = (ArrayList) getGenres.resultFromJson(Utils.readFile(context, "AudioLibrary.GetGenres.json"));
ContentValues genresValuesBatch[] = new ContentValues[genreList.size()];
for (int i = 0; i < genreList.size(); i++) {
LibraryType.DetailsGenre genre = genreList.get(i);
genresValuesBatch[i] = SyncUtils.contentValuesFromAudioGenre(hostId, genre);
syncMusic.insertGenresItems(genreList, context.getContentResolver());
}
context.getContentResolver().bulkInsert(MediaContract.AudioGenres.CONTENT_URI, genresValuesBatch);
}
private static void insertAlbums(Context context, int hostId) throws ApiException, IOException {
private static void insertAlbums(Context context, SyncMusic syncMusic) throws ApiException, IOException {
AudioLibrary.GetAlbums getAlbums = new AudioLibrary.GetAlbums();
String result = Utils.readFile(context, "AudioLibrary.GetAlbums.json");
ArrayList<AudioType.DetailsAlbum> albumList = (ArrayList) getAlbums.resultFromJson(result).items;
ContentResolver contentResolver = context.getContentResolver();
ContentValues albumValuesBatch[] = new ContentValues[albumList.size()];
int artistsCount = 0, genresCount = 0;
for (int i = 0; i < albumList.size(); i++) {
AudioType.DetailsAlbum album = albumList.get(i);
albumValuesBatch[i] = SyncUtils.contentValuesFromAlbum(hostId, album);
artistsCount += album.artistid.size();
genresCount += album.genreid.size();
}
contentResolver.bulkInsert(MediaContract.Albums.CONTENT_URI, albumValuesBatch);
// Iterate on each album, collect the artists and the genres and insert them
ContentValues albumArtistsValuesBatch[] = new ContentValues[artistsCount];
ContentValues albumGenresValuesBatch[] = new ContentValues[genresCount];
int artistCount = 0, genreCount = 0;
for (AudioType.DetailsAlbum album : albumList) {
for (int artistId : album.artistid) {
albumArtistsValuesBatch[artistCount] = new ContentValues();
albumArtistsValuesBatch[artistCount].put(MediaContract.AlbumArtists.HOST_ID, hostId);
albumArtistsValuesBatch[artistCount].put(MediaContract.AlbumArtists.ALBUMID, album.albumid);
albumArtistsValuesBatch[artistCount].put(MediaContract.AlbumArtists.ARTISTID, artistId);
artistCount++;
syncMusic.insertAlbumsItems(albumList, context.getContentResolver());
}
for (int genreId : album.genreid) {
albumGenresValuesBatch[genreCount] = new ContentValues();
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.HOST_ID, hostId);
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.ALBUMID, album.albumid);
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.GENREID, genreId);
genreCount++;
}
}
contentResolver.bulkInsert(MediaContract.AlbumArtists.CONTENT_URI, albumArtistsValuesBatch);
contentResolver.bulkInsert(MediaContract.AlbumGenres.CONTENT_URI, albumGenresValuesBatch);
}
private static void insertSongs(Context context, int hostId) throws ApiException, IOException {
private static void insertSongs(Context context, SyncMusic syncMusic) throws ApiException, IOException {
AudioLibrary.GetSongs getSongs = new AudioLibrary.GetSongs();
ArrayList<AudioType.DetailsSong> songList = (ArrayList) getSongs.resultFromJson(Utils.readFile(context, "AudioLibrary.GetSongs.json")).items;
ArrayList<AudioType.DetailsSong> songList = (ArrayList)
getSongs.resultFromJson(Utils.readFile(context, "AudioLibrary.GetSongs.json")).items;
ContentValues songValuesBatch[] = new ContentValues[songList.size()];
for (int i = 0; i < songList.size(); i++) {
AudioType.DetailsSong song = songList.get(i);
songValuesBatch[i] = SyncUtils.contentValuesFromSong(hostId, song);
}
context.getContentResolver().bulkInsert(MediaContract.Songs.CONTENT_URI, songValuesBatch);
syncMusic.insertSongsItems(songList, context.getContentResolver());
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright 2016 Martijn Brekhof. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xbmc.kore.testhelpers;
import android.database.Cursor;
import org.xbmc.kore.ui.SongsListFragment;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class TestUtils {
/**
* Tests if cursor contains all numbers from ids given column index.
* @param cursor
* @param columnIndex
* @param numbers
*/
public static void testCursorContainsNumbers(Cursor cursor, int columnIndex, int... numbers) {
HashMap<Integer, Boolean> idsFound = new HashMap<>();
for(int number : numbers) {
idsFound.put(number, false);
}
assertTrue(cursor.moveToFirst());
do {
idsFound.put(cursor.getInt(columnIndex), true);
} while(cursor.moveToNext());
for(Map.Entry<Integer, Boolean> entry : idsFound.entrySet() ) {
int key = entry.getKey();
assertTrue("Id " + key + " not found", entry.getValue());
}
}
/**
* Tests if cursor contains all numbers from start until end for given column index.
* @param columnIndex
* @param cursor
* @param start
* @param end
*/
public static void testCursorContainsRange(Cursor cursor, int columnIndex, int start, int end) {
HashMap<Integer, Boolean> idsFound = new HashMap<>();
for(int i = start; i <= end; i++) {
idsFound.put(i, false);
}
assertTrue(cursor.moveToFirst());
do {
idsFound.put(cursor.getInt(columnIndex), true);
} while(cursor.moveToNext());
for(Map.Entry<Integer, Boolean> entry : idsFound.entrySet() ) {
int key = entry.getKey();
assertTrue("Id " + key + " not found", entry.getValue());
}
}
}

View File

@ -18,6 +18,7 @@ package org.xbmc.kore.testhelpers;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.IBinder;
import android.support.test.rule.ActivityTestRule;
import android.support.v4.widget.DrawerLayout;
@ -92,6 +93,15 @@ public class Utils {
isInitialized = false;
}
public static String cursorToString(Cursor cursor) {
StringBuffer stringBuffer = new StringBuffer();
for (String name : cursor.getColumnNames()) {
int index = cursor.getColumnIndex(name);
stringBuffer.append(name + "=" + cursor.getString(index) + "\n");
}
return stringBuffer.toString();
}
private static void disableAnimations() {
int permStatus = context.checkCallingOrSelfPermission(ANIMATION_PERMISSION);
if (permStatus == PackageManager.PERMISSION_GRANTED) {
@ -127,4 +137,16 @@ public class Utils {
Log.e("SystemAnimations", "Could not change animation scale to " + animationScale + " :'(");
}
}
public static boolean moveCursorTo(Cursor cursor, int index, int item) {
if (( cursor == null ) || ( ! cursor.moveToFirst() ))
return false;
do {
if ( cursor.getInt(index) == item )
return true;
} while (cursor.moveToNext());
return false;
}
}

View File

@ -0,0 +1,335 @@
/*
* Copyright 2016 Martijn Brekhof. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xbmc.kore.tests.mediaprovider;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xbmc.kore.host.HostInfo;
import org.xbmc.kore.provider.MediaContract;
import org.xbmc.kore.testhelpers.Database;
import org.xbmc.kore.testhelpers.TestUtils;
import org.xbmc.kore.testhelpers.Utils;
import org.xbmc.kore.ui.MoviesActivity;
import org.xbmc.kore.utils.LogUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@RunWith(AndroidJUnit4.class)
public class MediaProviderMusicTest {
private static HostInfo hostInfo;
private static Context context;
private ContentResolver contentResolver;
/**
* Note that the activity MoviesActivity is only needed for context and is not tested
*/
@Rule
public ActivityTestRule<MoviesActivity> mActivityRule = new ActivityTestRule<>(
MoviesActivity.class);
@Before
public void setUp() throws Exception {
context = mActivityRule.getActivity();
if (hostInfo == null) // We only need to fill the database the first time
hostInfo = Database.fill(context);
contentResolver = mActivityRule.getActivity().getContentResolver();
}
@After
public void tearDown() throws Exception {
}
@AfterClass
public static void cleanup() {
Database.flush(context, hostInfo);
}
@Test
public void queryAllArtistsTest() {
Uri uri = MediaContract.Artists.buildArtistsListUri(hostInfo.getId());
Cursor cursor = contentResolver.query(uri, TestValues.Artist.PROJECTION, null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 227, cursor.getCount());
TestUtils.testCursorContainsRange(cursor, cursor.getColumnIndex(MediaContract.ArtistsColumns.ARTISTID),
1, 94);
//Artist id 95 should be missing
TestUtils.testCursorContainsRange(cursor, cursor.getColumnIndex(MediaContract.ArtistsColumns.ARTISTID),
96, 228);
}
@Test
public void queryArtistTest() {
Uri uri = MediaContract.Artists.buildArtistUri(hostInfo.getId(), TestValues.Artist.artistId);
Cursor cursor = contentResolver.query(uri, TestValues.Artist.PROJECTION, null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 1, cursor.getCount());
assertTrue(cursor.moveToFirst());
TestValues.Artist.test(cursor);
}
@Test
public void queryAllAlbumsTest() {
Uri uri = MediaContract.Albums.buildAlbumsListUri(hostInfo.getId());
Cursor cursor = contentResolver.query(uri, TestValues.Album.PROJECTION, null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 232, cursor.getCount());
int columnIndex = cursor.getColumnIndex(MediaContract.AlbumsColumns.ALBUMID);
TestUtils.testCursorContainsRange(cursor, columnIndex, 1, 75);
TestUtils.testCursorContainsRange(cursor, columnIndex, 77, 82);
TestUtils.testCursorContainsRange(cursor, columnIndex, 84, 234);
}
@Test
public void queryAlbumTest() {
Uri uri = MediaContract.Albums.buildAlbumUri(hostInfo.getId(), TestValues.Album.albumId);
Cursor cursor = contentResolver.query(uri, TestValues.Album.PROJECTION, null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 1, cursor.getCount());
assertTrue(cursor.moveToFirst());
TestValues.Album.test(cursor);
}
@Test
public void queryAlbumsForArtistTest() {
Uri uri = MediaContract.AlbumArtists.buildAlbumsForArtistListUri(hostInfo.getId(),
TestValues.Artist.artistId);
Cursor cursor = contentResolver.query(uri, TestValues.Album.PROJECTION, null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 1, cursor.getCount());
assertTrue(cursor.moveToFirst());
TestValues.Album.test(cursor);
}
@Test
public void queryAlbumsForGenreTest() {
int genreId = 13;
Uri uri = MediaContract.AlbumGenres.buildAlbumsForGenreListUri(hostInfo.getId(), genreId);
Cursor cursor = contentResolver.query(uri, TestValues.Album.PROJECTION, null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 31, cursor.getCount());
TestUtils.testCursorContainsNumbers(cursor, cursor.getColumnIndex(MediaContract.Albums.ALBUMID),
28, 43, 47, 66, 100);
TestUtils.testCursorContainsRange(cursor, cursor.getColumnIndex(MediaContract.Albums.ALBUMID),
50, 55);
TestUtils.testCursorContainsRange(cursor, cursor.getColumnIndex(MediaContract.Albums.ALBUMID),
201, 220);
}
@Test
public void queryAlbumSongsTest() {
Uri uri = MediaContract.Songs.buildAlbumSongsListUri(hostInfo.getId(), TestValues.Album.albumId);
Cursor cursor = contentResolver.query(uri, new String[] {MediaContract.Songs.SONGID}, null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 17, cursor.getCount());
TestUtils.testCursorContainsRange(cursor, cursor.getColumnIndex(MediaContract.SongsColumns.SONGID),
96, 112);
}
@Test
public void queryAlbumWithoutArtist() {
Uri uri = MediaContract.Albums.buildAlbumUri(hostInfo.getId(),
TestValues.AlbumWithoutArtist.albumId);
Cursor cursor = contentResolver.query(uri, TestValues.AlbumWithoutArtist.PROJECTION, null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 1, cursor.getCount());
assertTrue(cursor.moveToFirst());
TestValues.AlbumWithoutArtist.test(cursor);
}
@Test
public void queryAlbumWithMultipleArtists() {
Uri uri = MediaContract.Albums.buildAlbumUri(hostInfo.getId(),
TestValues.AlbumWithMultipleArtists.albumId);
Cursor cursor = contentResolver.query(uri, TestValues.AlbumWithMultipleArtists.PROJECTION,
null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 1, cursor.getCount());
assertTrue(cursor.moveToFirst());
TestValues.AlbumWithMultipleArtists.test(cursor);
}
@Test
public void queryArtistSongsTest() {
Uri uri = MediaContract.Songs.buildArtistSongsListUri(hostInfo.getId(), TestValues.ArtistSong.artistId);
Cursor cursor = contentResolver.query(uri, TestValues.ArtistSong.PROJECTION, null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 17, cursor.getCount());
TestUtils.testCursorContainsRange(cursor, cursor.getColumnIndex(MediaContract.SongsColumns.SONGID),
96, 112);
assertTrue(Utils.moveCursorTo(cursor, cursor.getColumnIndex(MediaContract.Songs.SONGID),
TestValues.ArtistSong.songId));
}
@Test
public void querySongWithArtistWithoutAlbumTest() {
Uri uri = MediaContract.Songs.buildArtistSongsListUri(hostInfo.getId(),
TestValues.SongWithArtistWithoutAlbum.artistId);
Cursor cursor = contentResolver.query(uri, TestValues.SongWithArtistWithoutAlbum.PROJECTION,
null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 1, cursor.getCount());
assertTrue(cursor.moveToFirst());
Utils.cursorToString(cursor);
TestValues.SongWithArtistWithoutAlbum.test(cursor);
}
@Test
public void queryFirstArtistSongWithMultipleArtistsTest() {
Uri uri = MediaContract.Songs.buildArtistSongsListUri(hostInfo.getId(),
TestValues.SongWithMultipleArtists.firstArtistId);
Cursor cursor = contentResolver.query(uri, TestValues.SongWithMultipleArtists.PROJECTION,
null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 1, cursor.getCount());
assertTrue(cursor.moveToFirst());
TestValues.SongWithMultipleArtists.test(cursor);
}
@Test
public void querySecondArtistSongWithMultipleArtistsTest() {
Uri uri = MediaContract.Songs.buildArtistSongsListUri(hostInfo.getId(),
TestValues.SongWithMultipleArtists.secondArtistId);
Cursor cursor = contentResolver.query(uri, TestValues.SongWithMultipleArtists.PROJECTION,
null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 1, cursor.getCount());
assertTrue(cursor.moveToFirst());
TestValues.SongWithMultipleArtists.test(cursor);
}
@Test
public void queryThirdArtistSongWithMultipleArtistsTest() {
Uri uri = MediaContract.Songs.buildArtistSongsListUri(hostInfo.getId(),
TestValues.SongWithMultipleArtists.thirdArtistId);
Cursor cursor = contentResolver.query(uri,
TestValues.SongWithMultipleArtists.PROJECTION,
null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 1, cursor.getCount());
assertTrue(cursor.moveToFirst());
TestValues.SongWithMultipleArtists.test(cursor);
}
@Test
public void queryAllSongsTest() {
Uri uri = MediaContract.Songs.buildSongsListUri(hostInfo.getId());
Cursor cursor = contentResolver.query(uri,
TestValues.ArtistSong.PROJECTION,
null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 1804, cursor.getCount());
TestUtils.testCursorContainsRange(cursor, cursor.getColumnIndex(MediaContract.Songs.SONGID),
1, 1804);
//Test if list also contains a song WITH an album AND an artist
assertTrue(Utils.moveCursorTo(cursor, cursor.getColumnIndex(MediaContract.Songs.SONGID),
TestValues.SongWithAlbumAndArtist.songId));
TestValues.SongWithAlbumAndArtist.test(cursor);
//Test if list also contains a song WITHOUT an album but WITH an artist
assertTrue(Utils.moveCursorTo(cursor, cursor.getColumnIndex(MediaContract.Songs.SONGID),
TestValues.SongWithArtistWithoutAlbum.songId));
TestValues.SongWithArtistWithoutAlbum.test(cursor);
//Test if list also contains a song WITH an album but WITHOUT an artist
assertTrue(Utils.moveCursorTo(cursor, cursor.getColumnIndex(MediaContract.Songs.SONGID),
TestValues.SongWithAlbumWithoutArtist.songId));
TestValues.SongWithAlbumWithoutArtist.test(cursor);
//Test if list contains a song WITH MULTIPLE artists
assertTrue(Utils.moveCursorTo(cursor, cursor.getColumnIndex(MediaContract.Songs.SONGID),
TestValues.SongWithMultipleArtists.songId));
TestValues.SongWithMultipleArtists.test(cursor);
}
@Test
public void queryAlbumWithMultipleArtistsTest() {
Uri uri = MediaContract.Albums.buildAlbumUri(hostInfo.getId(),
TestValues.AlbumWithMultipleArtists.albumId);
Cursor cursor = contentResolver.query(uri,
TestValues.AlbumWithMultipleArtists.PROJECTION,
null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 1, cursor.getCount());
assertTrue(cursor.moveToFirst());
LogUtils.LOGD("MediaProviderMusicTest", Utils.cursorToString(cursor));
TestValues.AlbumWithMultipleArtists.test(cursor);
}
@Test
public void queryAllGenresTest() {
Uri uri = MediaContract.AudioGenres.buildAudioGenresListUri(hostInfo.getId());
Cursor cursor = contentResolver.query(uri,
new String[] {MediaContract.AudioGenresColumns.GENREID},
null, null, null);
assertNotNull(cursor);
assertEquals("cursor size ", 39, cursor.getCount());
TestUtils.testCursorContainsRange(cursor,
cursor.getColumnIndex(MediaContract.AudioGenresColumns.GENREID),
1, 39);
}
}

View File

@ -0,0 +1,203 @@
/*
* Copyright 2016 Martijn Brekhof. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xbmc.kore.tests.mediaprovider;
import android.database.Cursor;
import org.xbmc.kore.provider.MediaContract;
import org.xbmc.kore.provider.MediaDatabase;
import org.xbmc.kore.provider.MediaProvider;
import org.xbmc.kore.ui.AlbumListFragment;
import org.xbmc.kore.ui.ArtistListFragment;
import org.xbmc.kore.ui.ArtistOverviewFragment;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import static org.junit.Assert.assertEquals;
public class TestValues {
public static class Artist {
public static int artistId = 13;
public static String artist = "Bernstein, Charles";
public static String[] PROJECTION = MediaContract.Artists.ALL_COLUMNS;
public static void test(Cursor cursor) {
assertEquals(TestValues.Artist.artistId, cursor.getInt(cursor.getColumnIndex(MediaContract.ArtistsColumns.ARTISTID)));
assertEquals(TestValues.Artist.artist, cursor.getString(cursor.getColumnIndex(MediaContract.ArtistsColumns.ARTIST)));
}
}
public static class Album {
public static int albumId = 13;
public static String title = "The Entity";
public static String displayArtist = "Bernstein, Charles";
public static int year = 1982;
public static String genre = "Soundtrack";
public static String[] PROJECTION = MediaContract.Albums.ALL_COLUMNS;
public static void test(Cursor cursor) {
int resultAlbumId = cursor.getInt(cursor.getColumnIndex(MediaContract.AlbumsColumns.ALBUMID));
assertEquals(albumId, resultAlbumId);
String resultTitle = cursor.getString(cursor.getColumnIndex(MediaContract.AlbumsColumns.TITLE));
assertEquals(title, resultTitle);
String resultArtist = cursor.getString(cursor.getColumnIndex(MediaContract.AlbumsColumns.DISPLAYARTIST));
assertEquals(displayArtist, resultArtist);
String resultGenre = cursor.getString(cursor.getColumnIndex(MediaContract.AlbumsColumns.GENRE));
assertEquals(genre, resultGenre);
int resultYear = cursor.getInt(cursor.getColumnIndex(MediaContract.AlbumsColumns.YEAR));
assertEquals(year, resultYear);
}
}
public static class AlbumWithoutArtist {
public static int albumId = 82;
public static String title = "The Album";
public static String displayArtist = "";
public static int year = 0;
public static String genre = "";
public static String[] PROJECTION = MediaContract.Albums.ALL_COLUMNS;
public static void test(Cursor cursor) {
int resultAlbumId = cursor.getInt(cursor.getColumnIndex(MediaContract.AlbumsColumns.ALBUMID));
assertEquals(albumId, resultAlbumId);
String resultTitle = cursor.getString(cursor.getColumnIndex(MediaContract.AlbumsColumns.TITLE));
assertEquals(title, resultTitle);
String resultArtist = cursor.getString(cursor.getColumnIndex(MediaContract.AlbumsColumns.DISPLAYARTIST));
assertEquals(displayArtist, resultArtist);
String resultGenre = cursor.getString(cursor.getColumnIndex(MediaContract.AlbumsColumns.GENRE));
assertEquals(genre, resultGenre);
int resultYear = cursor.getInt(cursor.getColumnIndex(MediaContract.AlbumsColumns.YEAR));
assertEquals(year, resultYear);
}
}
public static class AlbumWithMultipleArtists {
public static int albumId = 234;
public static String title = "ThreeArtistsAlbum";
public static String displayArtist = "First artist / Second artist / Third artist";
public static int year = 0;
public static String genre = "";
public static String[] PROJECTION = MediaContract.Albums.ALL_COLUMNS;
public static void test(Cursor cursor) {
int resultAlbumId = cursor.getInt(cursor.getColumnIndex(MediaContract.AlbumsColumns.ALBUMID));
assertEquals(albumId, resultAlbumId);
String resultTitle = cursor.getString(cursor.getColumnIndex(MediaContract.AlbumsColumns.TITLE));
assertEquals(title, resultTitle);
String resultArtist = cursor.getString(cursor.getColumnIndex(MediaContract.AlbumsColumns.DISPLAYARTIST));
assertEquals(displayArtist, resultArtist);
String resultGenre = cursor.getString(cursor.getColumnIndex(MediaContract.AlbumsColumns.GENRE));
assertEquals(genre, resultGenre);
int resultYear = cursor.getInt(cursor.getColumnIndex(MediaContract.AlbumsColumns.YEAR));
assertEquals(year, resultYear);
}
}
public static class ArtistSong {
public static int songId = 96;
public static int artistId = Artist.artistId;
public static int albumId = Album.albumId;
public static String title = "Intro & Main Title";
public static String[] PROJECTION = new String[] { MediaContract.Songs.SONGID,
MediaContract.Songs.TITLE,
MediaContract.Songs.ALBUMID,
MediaContract.SongArtists.ARTISTID,
MediaContract.Artists.ARTIST };
public static void test(Cursor cursor) {
assertEquals(songId, cursor.getInt(cursor.getColumnIndex(MediaContract.Songs.SONGID)));
assertEquals(title, cursor.getString(cursor.getColumnIndex(MediaContract.Songs.TITLE)));
assertEquals(albumId, cursor.getInt(cursor.getColumnIndex(MediaContract.Songs.ALBUMID)));
assertEquals(artistId, cursor.getInt(cursor.getColumnIndex(MediaContract.SongArtists.ARTISTID)));
}
}
public static class SongWithAlbumAndArtist {
public static int songId = 1487;
public static int artistId = 195;
public static int albumId = 201;
public static String title = "The Lone Ranger (William Tell Overture)";
public static String[] PROJECTION = ArtistSong.PROJECTION;
public static void test(Cursor cursor) {
assertEquals(songId, cursor.getInt(cursor.getColumnIndex(MediaContract.Songs.SONGID)));
assertEquals(title, cursor.getString(cursor.getColumnIndex(MediaContract.Songs.TITLE)));
assertEquals(albumId, cursor.getInt(cursor.getColumnIndex(MediaContract.Songs.ALBUMID)));
assertEquals(artistId, cursor.getInt(cursor.getColumnIndex(MediaContract.SongArtists.ARTISTID)));
}
}
public static class SongWithAlbumWithoutArtist {
public static int songId = 1219;
public static int artistId = 0;
public static String title = "Unknown";
public static int albumId = 82;
public static String[] PROJECTION = ArtistSong.PROJECTION;
public static void test(Cursor cursor) {
assertEquals(songId, cursor.getInt(cursor.getColumnIndex(MediaContract.Songs.SONGID)));
assertEquals(title, cursor.getString(cursor.getColumnIndex(MediaContract.Songs.TITLE)));
assertEquals(albumId, cursor.getInt(cursor.getColumnIndex(MediaContract.Songs.ALBUMID)));
assertEquals(artistId, cursor.getInt(cursor.getColumnIndex(MediaContract.SongArtists.ARTISTID)));
}
}
public static class SongWithArtistWithoutAlbum {
public static int songId = 1128;
public static int artistId = 73;
public static int albumId = 76;
public static String title = "Unknown";
public static String[] PROJECTION = ArtistSong.PROJECTION;
public static void test(Cursor cursor) {
assertEquals(songId, cursor.getInt(cursor.getColumnIndex(MediaContract.Songs.SONGID)));
assertEquals(title, cursor.getString(cursor.getColumnIndex(MediaContract.Songs.TITLE)));
assertEquals(albumId, cursor.getInt(cursor.getColumnIndex(MediaContract.Songs.ALBUMID)));
assertEquals(artistId, cursor.getInt(cursor.getColumnIndex(MediaContract.SongArtists.ARTISTID)));
}
}
public static class SongWithMultipleArtists {
public static int songId = 1804;
public static int firstArtistId = 226;
public static int secondArtistId = 227;
public static int thirdArtistId = 228;
public static int albumId = 234;
public static String title = "threeartists";
public static String[] PROJECTION = ArtistSong.PROJECTION;
public static void test(Cursor cursor) {
assertEquals(songId, cursor.getInt(cursor.getColumnIndex(MediaContract.Songs.SONGID)));
assertEquals(title, cursor.getString(cursor.getColumnIndex(MediaContract.Songs.TITLE)));
assertEquals(albumId, cursor.getInt(cursor.getColumnIndex(MediaContract.Songs.ALBUMID)));
}
}
}

View File

@ -49,8 +49,8 @@ public class RestoreSearchQueryViewPagerTest {
private final int ARTIST_SEARCH_QUERY_LIST_SIZE = 2;
private final String ALBUMS_SEARCH_QUERY = "tes";
private final int ALBUM_SEARCH_QUERY_LIST_SIZE = 3;
private final int ARTIST_COMPLETE_LIST_SIZE = 224;
private final int ALBUM_COMPLETE_LIST_SIZE = 231;
private final int ARTIST_COMPLETE_LIST_SIZE = 227;
private final int ALBUM_COMPLETE_LIST_SIZE = 232;
private LoaderIdlingResource loaderIdlingResource;

View File

@ -3,9 +3,9 @@
"jsonrpc" : "2.0",
"result" : {
"limits" : {
"total" : 231,
"total" : 232,
"start" : 0,
"end" : 231
"end" : 234
},
"albums" : [
{
@ -7538,6 +7538,41 @@
"theme" : [],
"musicbrainzalbumid" : "",
"playcount" : 0
},
{
"musicbrainzalbumid" : "",
"description" : "",
"artist" : [
"First artist",
"Second artist",
"Third artist"
],
"type" : "",
"title" : "ThreeArtistsAlbum",
"label" : "ThreeArtistsAlbum",
"rating" : 0,
"albumlabel" : "",
"playcount" : 0,
"albumid" : 234,
"thumbnail" : "",
"genreid" : [],
"artistid" : [
226,
227,
228
],
"mood" : [],
"theme" : [],
"genre" : [],
"displayartist" : "First artist / Second artist / Third artist",
"style" : [],
"fanart" : "",
"year" : 0,
"musicbrainzalbumartistid" : [
"",
"",
""
]
}
]
}

View File

@ -4482,11 +4482,71 @@
"fanart" : "",
"mood" : [],
"style" : []
},
{
"description" : "",
"disbanded" : "",
"artist" : "First artist",
"instrument" : [],
"label" : "First artist",
"born" : "",
"yearsactive" : [],
"genre" : [],
"thumbnail" : "",
"mood" : [],
"artistid" : 226,
"musicbrainzartistid" : [
""
],
"died" : "",
"formed" : "",
"style" : [],
"fanart" : ""
},
{
"description" : "",
"disbanded" : "",
"artist" : "Second artist",
"instrument" : [],
"label" : "Second artist",
"born" : "",
"yearsactive" : [],
"genre" : [],
"thumbnail" : "",
"mood" : [],
"artistid" : 227,
"musicbrainzartistid" : [
""
],
"died" : "",
"formed" : "",
"style" : [],
"fanart" : ""
},
{
"description" : "",
"disbanded" : "",
"artist" : "Third artist",
"instrument" : [],
"label" : "Third artist",
"born" : "",
"yearsactive" : [],
"genre" : [],
"thumbnail" : "",
"mood" : [],
"artistid" : 228,
"musicbrainzartistid" : [
""
],
"died" : "",
"formed" : "",
"style" : [],
"fanart" : ""
}
],
"limits" : {
"total" : 224,
"end" : 224,
"total" : 227,
"end" : 228,
"start" : 0
}
}

View File

@ -75109,12 +75109,58 @@
"songid" : 1618,
"title" : "Reel - Upstairs in a Tent",
"album" : "Ireland's Best Fiddle Tunes Disk 2"
},
{
"musicbrainzalbumartistid" : [],
"year" : 0,
"albumartistid" : [
226,
227,
228
],
"disc" : 0,
"musicbrainztrackid" : "",
"fanart" : "",
"file" : "/Users/martijn/Projects/dummymediafiles/media/music/ThreeArtists/ThreeArtistsAlbum/01-threeartists.mp3",
"genre" : [],
"displayartist" : "First artist / Second artist / Third artist",
"comment" : "",
"musicbrainzartistid" : [],
"artistid" : [
226,
227,
228
],
"lastplayed" : "",
"songid" : 1804,
"playcount" : 0,
"thumbnail" : "",
"albumartist" : [
"First artist",
"Second artist",
"Third artist"
],
"albumid" : 234,
"genreid" : [],
"track" : 1,
"label" : "threeartists",
"title" : "threeartists",
"rating" : 0,
"artist" : [
"First artist",
"Second artist",
"Third artist"
],
"lyrics" : "",
"duration" : 5,
"album" : "ThreeArtistsAlbum",
"musicbrainzalbumid" : ""
}
],
"limits" : {
"end" : 1803,
"end" : 1804,
"start" : 0,
"total" : 1803
"total" : 1804
}
},
"jsonrpc" : "2.0"

View File

@ -805,7 +805,7 @@ public class MediaProvider extends ContentProvider {
* {@link MediaContract} fields that are fully qualified with a specific
* parent {@link MediaDatabase.Tables}. Used when needed to work around SQL ambiguity.
*/
private interface Qualified {
public interface Qualified {
String ALBUM_ARTISTS_HOST_ID =
MediaDatabase.Tables.ALBUM_ARTISTS + "." + MediaContract.AlbumArtists.HOST_ID;
String ALBUM_ARTISTS_ARTISTID =

View File

@ -86,6 +86,7 @@ public class SyncMusic extends SyncItem {
AudioType.FieldsArtists.FANART,
AudioType.FieldsArtists.THUMBNAIL
};
/**
* Gets all artists recursively and forwards the call to Genres
* Genres->Albums->Songs
@ -115,13 +116,7 @@ public class SyncMusic extends SyncItem {
// First delete all music info
if (startIdx == 0) deleteMusicInfo(contentResolver, hostId);
// Insert artists
ContentValues artistValuesBatch[] = new ContentValues[items.size()];
for (int i = 0; i < items.size(); i++) {
AudioType.DetailsArtist artist = items.get(i);
artistValuesBatch[i] = SyncUtils.contentValuesFromArtist(hostId, artist);
}
contentResolver.bulkInsert(MediaContract.Artists.CONTENT_URI, artistValuesBatch);
insertArtists(items, contentResolver);
if (moreItemsAvailable(limitsReturned)) {
LogUtils.LOGD(TAG, "chainCallSyncArtists: More results on media center, recursing.");
@ -151,6 +146,8 @@ public class SyncMusic extends SyncItem {
where, new String[]{String.valueOf(hostId)});
contentResolver.delete(MediaContract.AlbumGenres.CONTENT_URI,
where, new String[]{String.valueOf(hostId)});
contentResolver.delete(MediaContract.SongArtists.CONTENT_URI,
where, new String[]{String.valueOf(hostId)});
contentResolver.delete(MediaContract.Songs.CONTENT_URI,
where, new String[]{String.valueOf(hostId)});
contentResolver.delete(MediaContract.AudioGenres.CONTENT_URI,
@ -178,16 +175,9 @@ public class SyncMusic extends SyncItem {
action.execute(hostConnection, new ApiCallback<List<LibraryType.DetailsGenre>>() {
@Override
public void onSuccess(List<LibraryType.DetailsGenre> result) {
if (result == null) result = new ArrayList<>(0); // Safeguard
ContentValues genresValuesBatch[] = new ContentValues[result.size()];
if (result != null)
insertGenresItems(result, contentResolver);
for (int i = 0; i < result.size(); i++) {
LibraryType.DetailsGenre genre = result.get(i);
genresValuesBatch[i] = SyncUtils.contentValuesFromAudioGenre(hostId, genre);
}
// Insert the genres and proceed to albums
contentResolver.bulkInsert(MediaContract.AudioGenres.CONTENT_URI, genresValuesBatch);
chainCallSyncAlbums(orchestrator, hostConnection, callbackHandler, contentResolver, 0);
}
@ -240,44 +230,7 @@ public class SyncMusic extends SyncItem {
}
// Insert the partial results
ContentValues albumValuesBatch[] = new ContentValues[items.size()];
int artistsCount = 0, genresCount = 0;
for (int i = 0; i < items.size(); i++) {
AudioType.DetailsAlbum album = items.get(i);
albumValuesBatch[i] = SyncUtils.contentValuesFromAlbum(hostId, album);
artistsCount += album.artistid.size();
genresCount += album.genreid.size();
}
contentResolver.bulkInsert(MediaContract.Albums.CONTENT_URI, albumValuesBatch);
LogUtils.LOGD(TAG, "Finished inserting albums in: " +
(System.currentTimeMillis() - albumSyncStartTime));
// Iterate on each album, collect the artists and the genres and insert them
ContentValues albumArtistsValuesBatch[] = new ContentValues[artistsCount];
ContentValues albumGenresValuesBatch[] = new ContentValues[genresCount];
int artistCount = 0, genreCount = 0;
for (AudioType.DetailsAlbum album : items) {
for (int artistId : album.artistid) {
albumArtistsValuesBatch[artistCount] = new ContentValues();
albumArtistsValuesBatch[artistCount].put(MediaContract.AlbumArtists.HOST_ID, hostId);
albumArtistsValuesBatch[artistCount].put(MediaContract.AlbumArtists.ALBUMID, album.albumid);
albumArtistsValuesBatch[artistCount].put(MediaContract.AlbumArtists.ARTISTID, artistId);
artistCount++;
}
for (int genreId : album.genreid) {
albumGenresValuesBatch[genreCount] = new ContentValues();
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.HOST_ID, hostId);
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.ALBUMID, album.albumid);
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.GENREID, genreId);
genreCount++;
}
}
contentResolver.bulkInsert(MediaContract.AlbumArtists.CONTENT_URI, albumArtistsValuesBatch);
contentResolver.bulkInsert(MediaContract.AlbumGenres.CONTENT_URI, albumGenresValuesBatch);
insertAlbumsItems(items, contentResolver);
LogUtils.LOGD(TAG, "Finished inserting artists and genres in: " +
(System.currentTimeMillis() - albumSyncStartTime));
@ -346,12 +299,95 @@ public class SyncMusic extends SyncItem {
limitsReturned = result.limits;
}
int totalArtistsCount = 0;
// Save partial results to DB
insertSongsItems(items, contentResolver);
if (moreItemsAvailable(limitsReturned)) {
LogUtils.LOGD(TAG, "chainCallSyncSongs: More results on media center, recursing.");
result = null; // Help the GC?
chainCallSyncSongs(orchestrator, hostConnection, callbackHandler, contentResolver,
startIdx + LIMIT_SYNC_SONGS);
} else {
// Ok, we have all the songs, insert them
LogUtils.LOGD(TAG, "chainCallSyncSongs: Got all results, continuing");
orchestrator.syncItemFinished();
}
}
@Override
public void onError(int errorCode, String description) {
// Ok, something bad happened, just quit
orchestrator.syncItemFailed(errorCode, description);
}
}, callbackHandler);
}
public void insertArtists(List<AudioType.DetailsArtist> items, ContentResolver contentResolver) {
ContentValues artistValuesBatch[] = new ContentValues[items.size()];
for (int i = 0; i < items.size(); i++) {
AudioType.DetailsArtist artist = items.get(i);
artistValuesBatch[i] = SyncUtils.contentValuesFromArtist(hostId, artist);
}
contentResolver.bulkInsert(MediaContract.Artists.CONTENT_URI, artistValuesBatch);
}
public void insertGenresItems(List<LibraryType.DetailsGenre> items, ContentResolver contentResolver) {
ContentValues genresValuesBatch[] = new ContentValues[items.size()];
for (int i = 0; i < items.size(); i++) {
LibraryType.DetailsGenre genre = items.get(i);
genresValuesBatch[i] = SyncUtils.contentValuesFromAudioGenre(hostId, genre);
}
// Insert the genres and proceed to albums
contentResolver.bulkInsert(MediaContract.AudioGenres.CONTENT_URI, genresValuesBatch);
}
public void insertAlbumsItems(List<AudioType.DetailsAlbum> items, ContentResolver contentResolver) {
ContentValues albumValuesBatch[] = new ContentValues[items.size()];
int artistsCount = 0, genresCount = 0;
for (int i = 0; i < items.size(); i++) {
AudioType.DetailsAlbum album = items.get(i);
albumValuesBatch[i] = SyncUtils.contentValuesFromAlbum(hostId, album);
artistsCount += album.artistid.size();
genresCount += album.genreid.size();
}
contentResolver.bulkInsert(MediaContract.Albums.CONTENT_URI, albumValuesBatch);
// Iterate on each album, collect the artists and the genres and insert them
ContentValues albumArtistsValuesBatch[] = new ContentValues[artistsCount];
ContentValues albumGenresValuesBatch[] = new ContentValues[genresCount];
int artistCount = 0, genreCount = 0;
for (AudioType.DetailsAlbum album : items) {
for (int artistId : album.artistid) {
albumArtistsValuesBatch[artistCount] = new ContentValues();
albumArtistsValuesBatch[artistCount].put(MediaContract.AlbumArtists.HOST_ID, hostId);
albumArtistsValuesBatch[artistCount].put(MediaContract.AlbumArtists.ALBUMID, album.albumid);
albumArtistsValuesBatch[artistCount].put(MediaContract.AlbumArtists.ARTISTID, artistId);
artistCount++;
}
for (int genreId : album.genreid) {
albumGenresValuesBatch[genreCount] = new ContentValues();
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.HOST_ID, hostId);
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.ALBUMID, album.albumid);
albumGenresValuesBatch[genreCount].put(MediaContract.AlbumGenres.GENREID, genreId);
genreCount++;
}
}
contentResolver.bulkInsert(MediaContract.AlbumArtists.CONTENT_URI, albumArtistsValuesBatch);
contentResolver.bulkInsert(MediaContract.AlbumGenres.CONTENT_URI, albumGenresValuesBatch);
}
public void insertSongsItems(List<AudioType.DetailsSong> items, ContentResolver contentResolver) {
ContentValues songValuesBatch[] = new ContentValues[items.size()];
int totalArtistsCount = 0;
for (int i = 0; i < items.size(); i++) {
AudioType.DetailsSong song = items.get(i);
songValuesBatch[i] = SyncUtils.contentValuesFromSong(hostId, song);
totalArtistsCount += song.artistid.size();
}
contentResolver.bulkInsert(MediaContract.Songs.CONTENT_URI, songValuesBatch);
@ -368,26 +404,8 @@ public class SyncMusic extends SyncItem {
artistCount++;
}
}
contentResolver.bulkInsert(MediaContract.SongArtists.CONTENT_URI, songArtistsValuesBatch);
if (moreItemsAvailable(limitsReturned)) {
LogUtils.LOGD(TAG, "chainCallSyncSongs: More results on media center, recursing.");
result = null; // Help the GC?
chainCallSyncSongs(orchestrator, hostConnection, callbackHandler, contentResolver,
startIdx + LIMIT_SYNC_SONGS);
} else {
// Ok, we have all the songs, insert them
LogUtils.LOGD(TAG, "chainCallSyncSongs: Got all results, continuing");
orchestrator.syncItemFinished();
}
}
@Override
public void onError(int errorCode, String description) {
// Ok, something bad happend, just quit
orchestrator.syncItemFailed(errorCode, description);
}
}, callbackHandler);
}
private boolean moreItemsAvailable(ListType.LimitsReturned limitsReturned) {

View File

@ -710,7 +710,7 @@ public class AlbumDetailsFragment extends AbstractDetailsFragment
/**
* Album details query parameters.
*/
private interface AlbumDetailsQuery {
public interface AlbumDetailsQuery {
String[] PROJECTION = {
BaseColumns._ID,
MediaContract.Albums.TITLE,
@ -739,7 +739,7 @@ public class AlbumDetailsFragment extends AbstractDetailsFragment
/**
* Movie cast list query parameters.
*/
private interface AlbumSongsListQuery {
public interface AlbumSongsListQuery {
String[] PROJECTION = {
BaseColumns._ID,
MediaContract.Songs.TITLE,

View File

@ -163,7 +163,7 @@ public class AlbumListFragment extends AbstractCursorListFragment {
/**
* Album list query parameters.
*/
private interface AlbumListQuery {
public interface AlbumListQuery {
String[] PROJECTION = {
BaseColumns._ID,
MediaContract.Albums.ALBUMID,

View File

@ -108,7 +108,7 @@ public class AudioGenresListFragment extends AbstractCursorListFragment {
/**
* Audio genres list query parameters.
*/
private interface AudioGenreListQuery {
public interface AudioGenreListQuery {
String[] PROJECTION = {
BaseColumns._ID,
MediaContract.AudioGenres.GENREID,

View File

@ -134,7 +134,7 @@ public class SongsListFragment extends AbstractCursorListFragment {
/**
* Album songs list query parameters.
*/
private interface SongsListQuery {
public interface SongsListQuery {
String[] PROJECTION = {
MediaDatabase.Tables.SONGS + "." + BaseColumns._ID,
MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.TITLE,

View File

@ -76,6 +76,22 @@ public class Utils {
return builder.toString();
}
/**
* Concats a list of integers...
* @param list
* @param delimiter
* @return
*/
public static String listIntegerConcat(List<Integer> list, String delimiter) {
StringBuilder builder = new StringBuilder();
boolean first = true;
for (Integer item : list) {
if (!first) builder.append(delimiter);
builder.append(item);
first = false;
}
return builder.toString();
}
/**
* Calls {@link Context#startActivity(Intent)} with the given <b>implicit</b> {@link Intent}

27
tools/json/README.md Normal file
View File

@ -0,0 +1,27 @@
# JSON Tools
Here you will find perl scripts that can be used to retrieve JSON responses from a Kodi instance.
The scripts are primarily used to create the JSON source files needed by the instrumentation tests.
## Getting json responses
Currently there are two scripts available to get media details from a Kodi instance.
* `getmovies.pl`: retrieves movie details
* `getmusic.pl`: retrieves music details
By default the scripts connect to Kodi running on the same machine (`127.0.0.1`) on port `8080`.
If you need to contact a Kodi instance on a different port and/or IP-address change the
following line in each script:
```
my $url = "http://127.0.0.1:8080/jsonrpc";
```
## Generating test values
The instrumentation tests require some values such as "total number of songs", "list of artistids", "genre ids", etc. etc.
To generate these values from the JSON files, you can run
`gentestnumbers.pl`. This script expects the JSON files to be located in the same directory you execute the script.
It also assumes the names of the JSON files are the same as generated by `getmovies.pl` and `getmusic.pl`.

283
tools/json/gentestnumbers.pl Executable file
View File

@ -0,0 +1,283 @@
#!/usr/bin/perl
#
# Copyright 2016 Martijn Brekhof. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use strict;
use warnings;
use Cpanel::JSON::XS qw(encode_json decode_json);
sub printRanges($\@) {
my $key = shift;
my $arg = shift;
my $count = 0;
my @list = @{$arg};
my $current;
for (my $i = 1; $i < @list; $i++) {
$current = $list[$i]->{$key};
my $prev = $list[$i-1]->{$key};
if ( $current - $prev == 1 ) {
$count++;
} else {
if ( $count == 0 ) {
print $prev;
} else {
print $prev - $count . "-" . $prev;
}
print " ";
$count = 0;
}
}
if ( $count == 0 ) {
print $current;
} else {
print $current - $count . "-" . $current;
}
}
sub decodeJson($) {
my $filename = shift;
local $/ = undef;
open (FH, $filename) or die "Error opening file $filename\n";
my $json_hash = decode_json(<FH>);
close FH;
return $json_hash;
}
sub printSong(\%) {
my $song = shift;
print "title: " . $song->{"title"} . "\n";
print "artistid: ";
for my $artistid ( @{$song->{"artistid"}} ) {
print $artistid . " ";
}
print "\n";
print "albumid: " . $song->{"albumid"} . "\n";
print "songid: " . $song->{"songid"} . "\n";
}
sub printAlbum(\%) {
my $album = shift;
print "title: " . $album->{"title"} . "\n";
print "albumid: " . $album->{"albumid"} . "\n";
print "displayartist: " . $album->{"displayartist"} . "\n";
print "year: " . $album->{"year"} . "\n";
print "genre: " . @{$album->{"genre"}} . "\n";
}
sub getArtists($) {
my $json_hash = shift;
return $json_hash->{"result"}->{"artists"};
}
sub getArtist($$) {
my $json_hash = shift;
my $artistid = shift;
my $artists = getArtists($json_hash);
for my $artist (@{$artists}) {
if ( $artistid == $artist->{"artistid"} ) {
return $artist;
}
}
return undef;
}
sub getAlbums($) {
my $json_hash = shift;
return $json_hash->{"result"}->{"albums"};
}
sub getAlbum($$) {
my $json_hash = shift;
my $albumid = shift;
my $albums = getAlbums($json_hash);
for my $album (@{$albums}) {
if ( $albumid == $album->{"albumid"}) {
return $album;
}
}
}
sub getAlbumsForGenre($$) {
my $json_hash = shift;
my $genreid = shift;
my @result;
my $albums = getAlbums($json_hash);
for my $album (@{$albums}) {
for my $albumGenreId (@{$album->{"genreid"}}) {
if ( $albumGenreId == $genreid ) {
push @result, $album;
}
}
}
return @result;
}
sub getSongs(%) {
my $json_hash = shift;
return $json_hash->{"result"}->{"songs"};
}
sub getSong(\%$) {
my $json_hash = shift;
my $songid = shift;
my $songs = getSongs($json_hash);
for my $song (@{$songs}) {
if ( $songid == $song->{"songid"}) {
return $song;
}
}
}
sub printArtistTestNumbers($) {
my $artistid = shift;
my $json_hash = decodeJson( "AudioLibrary.GetArtists.json" );
my $result = getArtists($json_hash);
print "Amount of artists: ", scalar @{$result}, "\n\n";
print "Artist ids: ";
my @artists = sort {$a->{"artistid"} <=> $b->{"artistid"}} @{$result};
printRanges("artistid", @artists);
print "\n\n";
print "Artist with artistId $artistid\n";
my $artist = getArtist($json_hash, $artistid);
print "artist: " . $artist->{"artist"} . "\n";
print "artistid: " . $artist->{"artistid"} . "\n";
print "\n\n";
}
sub printAlbumTestNumbers($$) {
my $albumid = shift;
my $genreid = shift;
my $json_hash = decodeJson( "AudioLibrary.GetAlbums.json" );
my $result = getAlbums($json_hash);
print "Amount of albums: ", scalar @{$result}, "\n\n";
print "Album ids: ";
my @albums = sort {$a->{"albumid"} <=> $b->{"albumid"}} @{$result};
printRanges("albumid", @albums);
print "\n\n";
print "Albums for genre id $genreid: ";
my @result = getAlbumsForGenre( $json_hash, $genreid );
@albums = sort {$a->{"albumid"} <=> $b->{"albumid"}} @result;
printRanges("albumid", @albums);
print "\n\n";
print "Album with albumId $albumid\n";
my $album = getAlbum($json_hash, $albumid);
printAlbum(%$album);
print "\n\n";
}
sub printSongTestNumbers($$) {
my $artistid = shift;
my $albumid = shift;
my $json_hash = decodeJson( "AudioLibrary.GetSongs.json" );
my $result = getSongs($json_hash);
print "Amount of songs: ", scalar @{$result}, "\n\n";
my @songsforartist;
my @songsforalbum;
print "Song ids: ";
my @songids = sort {$a->{"songid"} <=> $b->{"songid"}} @{$result};
printRanges("songid", @songids);
for my $song (@songids) {
for my $id (@{$song->{"artistid"}}) {
if ( $id == $artistid ) {
push @songsforartist, $song;
}
}
if ( $song->{"albumid"} == $albumid ) {
push @songsforalbum, $song;
}
}
print "\n\n";
print "Songs for artistid " . $artistid . ": total=" . scalar @songsforartist . ": ids=";
printRanges("songid", @songsforartist);
print "\n\n";
print "Songs for albumid " . $albumid . ": total=" . scalar @songsforalbum . ": ids=";
printRanges("songid", @songsforalbum);
print "\n\n";
}
sub printSongCornerCases() {
my $json_hash = decodeJson( "AudioLibrary.GetSongs.json" );
print "Song with album and artist\n";
my $song = getSong(%$json_hash, 1487);
printSong(%$song);
print "\n\n";
print "Songs with album but without artist\n";
$song = getSong(%$json_hash, 1219);
printSong(%$song);
print "\n\n";
print "Song without album but with artist\n";
$song = getSong(%$json_hash, 1128);
printSong(%$song);
print "\n\n";
print "Song with multiple artists\n";
$song = getSong(%$json_hash, 1804);
printSong(%$song);
}
sub printAlbumCornerCases() {
my $json_hash = decodeJson( "AudioLibrary.GetAlbums.json" );
print "Album without an artist\n";
my $album = getAlbum($json_hash, 82);
printAlbum(%$album);
print "\n\n";
print "Album with multiple artists\n";
$album = getAlbum($json_hash, 234);
printAlbum(%$album);
print "\n\n";
}
printArtistTestNumbers(13);
printAlbumTestNumbers(13, 13);
printAlbumCornerCases();
printSongTestNumbers(13, 13);
printSongCornerCases();