Commit a74a6a12 authored by Habib Kazemi's avatar Habib Kazemi Committed by Geoffrey Métais

Use Room for customDirectory

I added Dao and repository for CustomDirectory and previous
customDirectories will be added to the database in migration.
Also I wrote tests for Dao and Repository and migration
Signed-off-by: default avatarGeoffrey Métais <geoffrey.metais@gmail.com>
parent a6a75197
package org.videolan.vlc.database
import android.support.test.runner.AndroidJUnit4
import org.hamcrest.CoreMatchers.hasItem
import org.hamcrest.core.Is.`is`
import org.junit.Assert.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.videolan.vlc.util.TestUtil
@RunWith(AndroidJUnit4::class)
class CustomDirectoryDaoTest: DbTest() {
@Test
fun insertTwoCustomDirectory_GetAllShouldReturnTwo() {
val fakeCustomDirectories = TestUtil.createCustomDirectories(2)
fakeCustomDirectories.forEach {
db.customDirectoryDao().insert(it)
}
/*===========================================================*/
val customDirectories = db.customDirectoryDao().getAll()
assertThat(customDirectories.size, `is`(2))
assertThat(customDirectories, hasItem(fakeCustomDirectories[0]))
assertThat(customDirectories, hasItem(fakeCustomDirectories[1]))
}
@Test
fun insertTwoSameCustomDirectory_GetAllShouldReturnOne() {
val fakeCustomDirectories = TestUtil.createCustomDirectories(1)
db.customDirectoryDao().insert(fakeCustomDirectories[0])
db.customDirectoryDao().insert(fakeCustomDirectories[0])
/*===========================================================*/
val customDirectories = db.customDirectoryDao().getAll()
assertThat(customDirectories.size, `is`(1))
assertThat(customDirectories, hasItem(fakeCustomDirectories[0]))
}
@Test
fun insertTwoCustomDirectory_GetShouldReturnEachOne() {
val fakeCustomDirectories = TestUtil.createCustomDirectories(2)
fakeCustomDirectories.forEach {
db.customDirectoryDao().insert(it)
}
/*===========================================================*/
val firstCustomDirectory = db.customDirectoryDao().get(fakeCustomDirectories[0].path)[0]
val secondCustomDirectory = db.customDirectoryDao().get(fakeCustomDirectories[1].path)[0]
assertThat(firstCustomDirectory, `is`(fakeCustomDirectories[0]))
assertThat(secondCustomDirectory, `is`(fakeCustomDirectories[1]))
}
@Test
fun insertNoneCustomDirectory_GetShouldReturnNull() {
val customDirectory = db.customDirectoryDao().get("foo")
assertThat(customDirectory.size, `is`(0))
}
@Test
fun insertTwoCustomDirectory_DeleteOneShouldDeleteThatOne() {
val fakeCustomDirectories = TestUtil.createCustomDirectories(2)
fakeCustomDirectories.forEach {
db.customDirectoryDao().insert(it)
}
/*===========================================================*/
db.customDirectoryDao().delete(fakeCustomDirectories[0])
val customDirectories = db.customDirectoryDao().getAll()
assertThat(customDirectories.size, `is`(1))
assertThat(customDirectories, hasItem(fakeCustomDirectories[1]))
}
}
......@@ -22,37 +22,32 @@ package org.videolan.vlc.database
import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory
import android.arch.persistence.room.Room
import android.arch.persistence.room.migration.Migration
import android.arch.persistence.room.testing.MigrationTestHelper
import android.net.Uri
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.CoreMatchers.hasItem
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.videolan.libvlc.Media
import org.videolan.vlc.VLCApplication
import org.videolan.vlc.database.helpers.*
import org.videolan.vlc.database.models.BrowserFav
import org.videolan.vlc.database.models.ExternalSub
import org.videolan.vlc.database.models.Slave
import org.videolan.vlc.util.Settings
import org.videolan.vlc.util.TYPE_NETWORK_FAV
import org.videolan.vlc.util.TestUtil
private const val TEST_DB_NAME = "test-db"
@RunWith(AndroidJUnit4::class)
class MigrationTest {
// Slave
private val slaveMedia1Path = "/storage/emulated/0/Android/data/org.videolan.vlc.debug/files/subs/file1.mkv"
private val slaveMedia1UriFa = "file:///storage/emulated/0/Android/data/org.videolan.vlc.debug/files/subs/file1.fa.srt"
// External Sub
private val exSubMedia1Name = "file1.mkv"
private val exSubMedisubsFolder = "/storage/emulated/0/Android/data/org.videolan.vlc.debug/files/subs/"
private val exSubfile1Sub1 = "${exSubMedisubsFolder}file1.eng.srt"
// Favs
private val favUri = Uri.parse("/storage/emulated/0/Android/data/org.videolan.vlc.debug/files/subs/file1.mkv")
private val favTitle = "test1"
private val TEST_DB_NAME = "test-db"
@get:Rule
val migrationTestHelper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
......@@ -60,6 +55,17 @@ class MigrationTest {
FrameworkSQLiteOpenHelperFactory())
@Test fun migrate26To27() {
// Slave
val slaveMedia1Path = "/storage/emulated/0/Android/data/org.videolan.vlc.debug/files/subs/file1.mkv"
val slaveMedia1UriFa = "file:///storage/emulated/0/Android/data/org.videolan.vlc.debug/files/subs/file1.fa.srt"
// External Sub
val exSubMedia1Name = "file1.mkv"
val exSubMedisubsFolder = "/storage/emulated/0/Android/data/org.videolan.vlc.debug/files/subs/"
val exSubfile1Sub1 = "${exSubMedisubsFolder}file1.eng.srt"
// Favs
val favUri = Uri.parse("/storage/emulated/0/Android/data/org.videolan.vlc.debug/files/subs/file1.mkv")
val favTitle = "test1"
val sqliteTestDbOpenHelper = SqliteTestDbOpenHelper(InstrumentationRegistry.getTargetContext(), TEST_DB_NAME, 26)
createSlavesTable(sqliteTestDbOpenHelper)
createExternalSubsTable(sqliteTestDbOpenHelper)
......@@ -70,9 +76,9 @@ class MigrationTest {
saveExSubtitle(exSubfile1Sub1, exSubMedia1Name, sqliteTestDbOpenHelper)
saveNetworkFavItem(favUri, favTitle, null, sqliteTestDbOpenHelper)
migrationTestHelper.runMigrationsAndValidate(TEST_DB_NAME, 27, true, migration_26_27)
migrationTestHelper.runMigrationsAndValidate(TEST_DB_NAME, 27, true, migration_26_27, migration_27_28)
val migratedDb = getMigratedRoomDatabase()
val migratedDb = getMigratedRoomDatabase(migration_26_27, migration_27_28)
val slave: Slave = migratedDb.slaveDao().get(slaveMedia1Path)[0]
val exSub: ExternalSub = migratedDb.externalSubDao().get(exSubMedia1Name)[0]
......@@ -94,11 +100,26 @@ class MigrationTest {
clearDatabase(sqliteTestDbOpenHelper)
}
fun getMigratedRoomDatabase(): MediaDatabase {
@Test fun migrate27to28() {
migrationTestHelper.createDatabase(TEST_DB_NAME, 27)
val preferences = Settings.getInstance(VLCApplication.getAppContext()).edit()
val fakeCustomDirectories = TestUtil.createCustomDirectories(2)
// 27_28 migration rule moves the data from prefs to room
val prefCustomDirectories = fakeCustomDirectories.map { it.path }.reduce{ acc, path -> "$acc:$path" }
preferences.putString("custom_paths", prefCustomDirectories ).commit()
migrationTestHelper.runMigrationsAndValidate(TEST_DB_NAME, 28, true, migration_27_28)
val roomCustomDirectories = getMigratedRoomDatabase(migration_27_28).customDirectoryDao().getAll()
assertThat(roomCustomDirectories.size, `is`(2))
assertThat(roomCustomDirectories, hasItem(fakeCustomDirectories[0]))
assertThat(roomCustomDirectories, hasItem(fakeCustomDirectories[1]))
}
fun getMigratedRoomDatabase(vararg migrations:Migration): MediaDatabase {
val database: MediaDatabase = Room.databaseBuilder(
InstrumentationRegistry.getTargetContext(),
MediaDatabase::class.java, TEST_DB_NAME )
.addMigrations(migration_26_27)
.addMigrations(*migrations)
.build()
// close the database and release any stream resources when the test finishes
......
{
"formatVersion": 1,
"database": {
"version": 28,
"identityHash": "3383d1efd00d67a01b86067a8321c4b0",
"entities": [
{
"tableName": "external_subtitles_table",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `media_name` TEXT NOT NULL, PRIMARY KEY(`uri`))",
"fields": [
{
"fieldPath": "uri",
"columnName": "uri",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "mediaName",
"columnName": "media_name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"uri"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "SLAVES_table",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`slave_media_mrl` TEXT NOT NULL, `slave_type` INTEGER NOT NULL, `slave_priority` INTEGER NOT NULL, `slave_uri` TEXT NOT NULL, PRIMARY KEY(`slave_media_mrl`))",
"fields": [
{
"fieldPath": "mediaPath",
"columnName": "slave_media_mrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "slave_type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "priority",
"columnName": "slave_priority",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uri",
"columnName": "slave_uri",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"slave_media_mrl"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "fav_table",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `type` INTEGER NOT NULL, `title` TEXT NOT NULL, `icon_url` TEXT, PRIMARY KEY(`uri`))",
"fields": [
{
"fieldPath": "uri",
"columnName": "uri",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "iconUrl",
"columnName": "icon_url",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"uri"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "CustomDirectory",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`path` TEXT NOT NULL, PRIMARY KEY(`path`))",
"fields": [
{
"fieldPath": "path",
"columnName": "path",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"path"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"3383d1efd00d67a01b86067a8321c4b0\")"
]
}
}
\ No newline at end of file
package org.videolan.vlc.database
import android.arch.persistence.room.*
import org.videolan.vlc.database.models.CustomDirectory
@Dao
interface CustomDirectoryDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(customDirectory: CustomDirectory)
@Delete
fun delete(customDirectory: CustomDirectory)
@Query("SELECT * FROM CustomDirectory")
fun getAll(): List<CustomDirectory>
@Query("SELECT * FROM CustomDirectory WHERE path = :path")
fun get(path: String): List<CustomDirectory>
}
......@@ -28,17 +28,19 @@ import android.arch.persistence.room.TypeConverters
import android.content.Context
import org.videolan.tools.SingletonHolder
import org.videolan.vlc.database.models.BrowserFav
import org.videolan.vlc.database.models.CustomDirectory
import org.videolan.vlc.database.models.ExternalSub
import org.videolan.vlc.database.models.Slave
private const val DB_NAME = "vlc_database"
@Database(entities = [ExternalSub::class, Slave::class, BrowserFav::class], version = 27)
@Database(entities = [ExternalSub::class, Slave::class, BrowserFav::class, CustomDirectory::class], version = 28)
@TypeConverters(Converters::class)
abstract class MediaDatabase: RoomDatabase() {
abstract fun externalSubDao(): ExternalSubDao
abstract fun slaveDao(): SlaveDao
abstract fun browserFavDao(): BrowserFavDao
abstract fun customDirectoryDao(): CustomDirectoryDao
companion object : SingletonHolder<MediaDatabase, Context>({ buildDatabase(it.applicationContext) })
}
......@@ -51,7 +53,7 @@ private fun buildDatabase(context: Context) = Room.databaseBuilder(context.appli
migration_13_14, migration_14_15, migration_15_16, migration_16_17,
migration_17_18, migration_18_19, migration_19_20, migration_20_21,
migration_21_22, migration_22_23, migration_23_24, migration_24_25,
migration_25_26, migration_26_27)
migration_25_26, migration_26_27, migration_27_28)
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) { populateDB(context) }
})
......
......@@ -24,8 +24,10 @@ import android.arch.persistence.db.SupportSQLiteDatabase
import android.arch.persistence.room.migration.Migration
import android.content.Context
import kotlinx.coroutines.experimental.launch
import org.videolan.vlc.VLCApplication
import org.videolan.vlc.repository.BrowserFavRepository
import org.videolan.vlc.util.AndroidDevices
import org.videolan.vlc.util.Settings
import org.videolan.vlc.util.VLCIO
private const val DIR_TABLE_NAME = "directories_table"
......@@ -39,6 +41,8 @@ private const val HISTORY_TABLE_NAME = "history_table"
private const val EXTERNAL_SUBTITLES_TABLE_NAME = "external_subtitles_table"
private const val SLAVES_TABLE_NAME = "SLAVES_table"
private const val FAV_TABLE_NAME = "fav_table"
private const val CUSTOM_DIRECTORY_TABLE_NAME = "CustomDirectory"
fun dropUnnecessaryTables(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS $DIR_TABLE_NAME;")
......@@ -177,6 +181,21 @@ val migration_26_27 = object:Migration(26, 27) {
}
}
val migration_27_28 = object:Migration(27, 28) {
override fun migrate(database: SupportSQLiteDatabase) {
val preferences = Settings.getInstance(VLCApplication.getAppContext())
val customPaths = preferences.getString("custom_paths", "")
var oldPaths = listOf<String>()
if (customPaths.isNotEmpty())
oldPaths = customPaths.split(":")
database.execSQL("CREATE TABLE IF NOT EXISTS $CUSTOM_DIRECTORY_TABLE_NAME(path TEXT PRIMARY KEY NOT NULL);")
oldPaths.forEach {
database.execSQL("INSERT INTO $CUSTOM_DIRECTORY_TABLE_NAME(path) VALUES (\"$it\")")
}
}
}
fun populateDB(context: Context) = launch(VLCIO) {
val favRepo = BrowserFavRepository.getInstance(context)
val uris = listOf(AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_MOVIES_DIRECTORY_URI,
......
package org.videolan.vlc.database.models
import android.arch.persistence.room.Entity
import android.arch.persistence.room.PrimaryKey
@Entity()
data class CustomDirectory(
@PrimaryKey
val path: String
)
......@@ -70,7 +70,7 @@ class BrowserFavRepository(private val browserFavDao: BrowserFavDao) {
@WorkerThread
fun browserFavExists(uri: Uri): Boolean = browserFavDao.get(uri).isNotEmpty()
fun deleteBrowserFav(uri: Uri) = browserFavDao.delete(uri)
fun deleteBrowserFav(uri: Uri) = launch(VLCIO) { browserFavDao.delete(uri) }
private fun List<MediaWrapper>.filterNetworkFavs() : List<MediaWrapper> {
return when {
......
package org.videolan.vlc.repository
import android.content.Context
import android.support.annotation.WorkerThread
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext
import org.videolan.tools.SingletonHolder
import org.videolan.vlc.database.CustomDirectoryDao
import org.videolan.vlc.database.MediaDatabase
import org.videolan.vlc.database.models.CustomDirectory
import org.videolan.vlc.util.VLCIO
class CustomDirectoryRepository (private val customDirectoryDao: CustomDirectoryDao) {
fun addCustomDirectory(path: String): Job {
return launch(VLCIO) {
customDirectoryDao.insert(CustomDirectory(path))
}
}
@WorkerThread
fun getCustomDirectories(): List<CustomDirectory> {
return customDirectoryDao.getAll()
}
fun deleteCustomDirectory(path: String) {
launch(VLCIO) { customDirectoryDao.delete(CustomDirectory(path)) }
}
@WorkerThread
fun customDirectoryExists(path: String) = customDirectoryDao.get(path).isNotEmpty()
companion object : SingletonHolder<CustomDirectoryRepository, Context>({ CustomDirectoryRepository(MediaDatabase.getInstance(it).customDirectoryDao()) })
}
\ No newline at end of file
......@@ -23,6 +23,7 @@ package org.videolan.vlc.util
import android.net.Uri
import org.videolan.libvlc.Media
import org.videolan.vlc.database.models.BrowserFav
import org.videolan.vlc.database.models.CustomDirectory
import org.videolan.vlc.database.models.ExternalSub
import org.videolan.vlc.database.models.Slave
......@@ -88,4 +89,15 @@ object TestUtil {
createSubtitleSlave( "$fakeMediaUri$mediaName", "$fakeSubUri$mediaName$it.srt" )
}
}
fun createCustomDirectory(path: String): CustomDirectory{
return CustomDirectory(path)
}
fun createCustomDirectories(count: Int): List<CustomDirectory> {
val directory = "/sdcard/foo"
return (0 until count).map {
createCustomDirectory("$directory$it")
}
}
}
package org.videolan.vlc.repository
import android.arch.core.executor.testing.InstantTaskExecutorRule
import kotlinx.coroutines.experimental.runBlocking
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.CoreMatchers.hasItem
import org.junit.Assert.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.*
import org.powermock.modules.junit4.PowerMockRunner
import org.videolan.vlc.database.CustomDirectoryDao
import org.videolan.vlc.database.MediaDatabase
import org.videolan.vlc.database.models.CustomDirectory
import org.videolan.vlc.util.TestUtil
import org.videolan.vlc.util.argumentCaptor
import org.videolan.vlc.util.mock
import org.videolan.vlc.util.uninitialized
@RunWith(PowerMockRunner::class)
class CustomDirectoryRepositoryTest {
private val customDirectoryDao = mock<CustomDirectoryDao>()
private lateinit var customDirectoryRepository: CustomDirectoryRepository
@Rule
@JvmField
val instantExecutorRule = InstantTaskExecutorRule()
@Before fun init() {
System.setProperty("kotlinx.coroutines.blocking.checker", "disable")
val db = mock<MediaDatabase>()
`when`(db.customDirectoryDao()).thenReturn(customDirectoryDao)
customDirectoryRepository = CustomDirectoryRepository(customDirectoryDao)
}
@Test
fun insertTwoCustomDirectory_GetAllShouldReturnTwo() = runBlocking{
val fakeCustomDirectories = TestUtil.createCustomDirectories(2)
fakeCustomDirectories.forEach {
customDirectoryRepository.addCustomDirectory(it.path).join()
}
val inserted = argumentCaptor<CustomDirectory>()
verify(customDirectoryDao, times(2)).insert(inserted.capture() ?: uninitialized())
assertThat(inserted.allValues.size, `is`(2))
assertThat(inserted.allValues[0], `is`(fakeCustomDirectories[0]))
assertThat(inserted.allValues[1], `is`(fakeCustomDirectories[1]))
`when`(customDirectoryDao.getAll()).thenReturn(fakeCustomDirectories)
val customDirectories = customDirectoryRepository.getCustomDirectories()
verify(customDirectoryDao).getAll()
assertThat(customDirectories.size, `is`(2))
}
@Test
fun insertTwoCustomDirectory_DeleteOneShouldDeleteOne() = runBlocking{
val fakeCustomDirectories = TestUtil.createCustomDirectories(2)
fakeCustomDirectories.forEach {
customDirectoryRepository.addCustomDirectory(it.path).join()
}
val inserted = argumentCaptor<CustomDirectory>()
verify(customDirectoryDao, times(2)).insert(inserted.capture() ?: uninitialized())
assertThat(inserted.allValues.size, `is`(2))
assertThat(inserted.allValues[0], `is`(fakeCustomDirectories[0]))
assertThat(inserted.allValues[1], `is`(fakeCustomDirectories[1]))
customDirectoryRepository.deleteCustomDirectory(fakeCustomDirectories[0].path)
val deleted = argumentCaptor<CustomDirectory>()
verify(customDirectoryDao).delete(deleted.capture() ?: uninitialized())
assertThat(deleted.value, `is`(fakeCustomDirectories[0]))
}
@Test
fun insertOneCustomDirectory_CheckExistenceShouldBeTrue() = runBlocking{
val fakeCustomDirectories = TestUtil.createCustomDirectories(1)
fakeCustomDirectories.forEach {
customDirectoryRepository.addCustomDirectory(it.path).join()
}
val inserted = argumentCaptor<CustomDirectory>()
verify(customDirectoryDao).insert(inserted.capture() ?: uninitialized())
assertThat(inserted.allValues.size, `is`(1))
assertThat(inserted.allValues[0], `is`(fakeCustomDirectories[0]))
`when`(customDirectoryDao.get(fakeCustomDirectories[0].path)).thenReturn(fakeCustomDirectories)
val bool = customDirectoryRepository.customDirectoryExists(fakeCustomDirectories[0].path)
assertTrue(bool)
}
@Test
fun insertOneCustomDirectory_CheckExistenceForWrongPathShouldBeFalse() = runBlocking{
val fakeCustomDirectories = TestUtil.createCustomDirectories(1)
fakeCustomDirectories.forEach {
customDirectoryRepository.addCustomDirectory(it.path).join()
}
val inserted = argumentCaptor<CustomDirectory>()
verify(customDirectoryDao).insert(inserted.capture() ?: uninitialized())
assertThat(inserted.allValues.size, `is`(1))
assertThat(inserted.allValues[0], `is`(fakeCustomDirectories[0]))
`when`(customDirectoryDao.get(fakeCustomDirectories[0].path)).thenReturn(fakeCustomDirectories)
val bool = customDirectoryRepository.customDirectoryExists(fakeCustomDirectories[0].path+"foo")
assertFalse(bool)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment