Unit test for BackupAgent

This commit is contained in:
Koitharu
2022-07-18 11:27:56 +03:00
parent dfa413da6f
commit f0ee64bafa
4 changed files with 183 additions and 9 deletions

View File

@@ -64,8 +64,11 @@ android {
disable 'MissingTranslation', 'PrivateResource', 'NotifyDataSetChanged' disable 'MissingTranslation', 'PrivateResource', 'NotifyDataSetChanged'
} }
testOptions { testOptions {
unitTests.includeAndroidResources = true unitTests.includeAndroidResources true
unitTests.returnDefaultValues = false unitTests.returnDefaultValues false
kotlinOptions {
freeCompilerArgs += ['-opt-in=org.koitharu.kotatsu.parsers.InternalParsersApi']
}
} }
} }
afterEvaluate { afterEvaluate {

View File

@@ -0,0 +1,102 @@
package org.koitharu.kotatsu
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.parsers.model.*
import java.util.*
object SampleData {
val manga = Manga(
id = 1105355890252749533,
title = "Sasurai Emanon",
altTitle = null,
url = "/manga/sasurai_emanon/",
publicUrl = "https://www.mangatown.com/manga/sasurai_emanon/",
rating = 1.0f,
isNsfw = false,
coverUrl = "https://fmcdn.mangahere.com/store/manga/10992/ocover.jpg?token=905148d2f052f9d3604135933b958771c8b00077&ttl=1658214000&v=1578490983",
tags = setOf(
MangaTag(title = "Adventure", key = "0-adventure-0-0-0-0", source = MangaSource.MANGATOWN),
MangaTag(title = "Mature", key = "0-mature-0-0-0-0", source = MangaSource.MANGATOWN),
MangaTag(title = "Psychological", key = "0-psychological-0-0-0-0", source = MangaSource.MANGATOWN),
MangaTag(title = "Slice Of Life", key = "0-slice_of_life-0-0-0-0", source = MangaSource.MANGATOWN),
MangaTag(title = "Supernatural", key = "0-supernatural-0-0-0-0", source = MangaSource.MANGATOWN),
),
state = MangaState.ONGOING,
author = "Kajio Shinji",
largeCoverUrl = null,
source = MangaSource.MANGATOWN,
)
val mangaDetails = manga.copy(
tags = setOf(
MangaTag(title = "Adventure", key = "0-adventure-0-0-0-0", source = MangaSource.MANGATOWN),
MangaTag(title = "Mature", key = "0-mature-0-0-0-0", source = MangaSource.MANGATOWN),
MangaTag(title = "Psychological", key = "0-psychological-0-0-0-0", source = MangaSource.MANGATOWN),
MangaTag(title = "Slice Of Life", key = "0-slice_of_life-0-0-0-0", source = MangaSource.MANGATOWN),
MangaTag(title = "Supernatural", key = "0-supernatural-0-0-0-0", source = MangaSource.MANGATOWN),
),
largeCoverUrl = null,
description = """
Based on the award-winning novel by Shinji Kajio, Memories of Emanon tells the story of a mysterious girl
who holds a 3-billion-year old memory, dating back to the moment life first appeared on Earth. The first
half of the volume is the colored Wandering Emanon '67 chapters (published before as Emanon Episode: 1).
The second half is Wandering Emanon set before the '67 chapters.
""".trimIndent(),
chapters = listOf(
MangaChapter(
id = -7214407414868456892,
name = "Sasurai Emanon - 1",
number = 1,
url = "/manga/sasurai_emanon/c001/",
scanlator = null,
uploadDate = 1335906000000,
branch = null,
source = MangaSource.MANGATOWN,
),
MangaChapter(
id = -7214407414868456861,
name = "Sasurai Emanon - 2",
number = 2,
url = "/manga/sasurai_emanon/c002/",
scanlator = null,
uploadDate = 1335906000000,
branch = null,
source = MangaSource.MANGATOWN,
),
MangaChapter(
id = -7214407414868456830,
name = "Sasurai Emanon - 3",
number = 3,
url = "/manga/sasurai_emanon/c003/",
scanlator = null,
uploadDate = 1335906000000,
branch = null,
source = MangaSource.MANGATOWN,
),
MangaChapter(
id = -7214407414868456799,
name = "Sasurai Emanon - 4",
number = 3,
url = "/manga/sasurai_emanon/c004/",
scanlator = null,
uploadDate = 1335906000000,
branch = null,
source = MangaSource.MANGATOWN,
),
),
)
val tag = mangaDetails.tags.elementAt(2)
val chapter = checkNotNull(mangaDetails.chapters)[2]
val favouriteCategory = FavouriteCategory(
id = 4,
title = "Read later",
sortKey = 1,
order = SortOrder.NEWEST,
createdAt = Date(1335906000000),
isTrackingEnabled = true,
)
}

View File

@@ -0,0 +1,67 @@
package org.koitharu.kotatsu.settings.backup
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.koin.test.KoinTest
import org.koin.test.get
import org.koin.test.inject
import org.koitharu.kotatsu.SampleData
import org.koitharu.kotatsu.core.backup.BackupRepository
import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.db.entity.toMangaTags
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.history.domain.HistoryRepository
import kotlin.test.*
@RunWith(AndroidJUnit4::class)
class AppBackupAgentTest : KoinTest {
private val historyRepository by inject<HistoryRepository>()
private val favouritesRepository by inject<FavouritesRepository>()
private val backupRepository by inject<BackupRepository>()
private val database by inject<MangaDatabase>()
@Before
fun setUp() {
database.clearAllTables()
}
@Test
fun testBackupRestore() = runTest {
val category = favouritesRepository.createCategory(
title = SampleData.favouriteCategory.title,
sortOrder = SampleData.favouriteCategory.order,
isTrackerEnabled = SampleData.favouriteCategory.isTrackingEnabled,
)
favouritesRepository.addToCategory(categoryId = category.id, mangas = listOf(SampleData.manga))
historyRepository.addOrUpdate(
manga = SampleData.mangaDetails,
chapterId = SampleData.mangaDetails.chapters!![2].id,
page = 3,
scroll = 40,
percent = 0.2f,
)
val history = checkNotNull(historyRepository.getOne(SampleData.mangaDetails))
val agent = AppBackupAgent()
val backup = agent.createBackupFile(get(), backupRepository)
database.clearAllTables()
assertTrue(favouritesRepository.getAllManga().isEmpty())
assertNull(historyRepository.getLastOrNull())
backup.inputStream().use {
agent.restoreBackupFile(it.fd, backup.length(), backupRepository)
}
assertEquals(category, favouritesRepository.getCategory(category.id))
assertEquals(history, historyRepository.getOne(SampleData.manga))
assertContentEquals(listOf(SampleData.manga), favouritesRepository.getManga(category.id))
val allTags = database.tagsDao.findTags(SampleData.tag.source.name).toMangaTags()
assertContains(allTags, SampleData.tag)
}
}

View File

@@ -4,7 +4,9 @@ import android.app.backup.BackupAgent
import android.app.backup.BackupDataInput import android.app.backup.BackupDataInput
import android.app.backup.BackupDataOutput import android.app.backup.BackupDataOutput
import android.app.backup.FullBackupDataOutput import android.app.backup.FullBackupDataOutput
import android.content.Context
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.koitharu.kotatsu.core.backup.BackupEntry import org.koitharu.kotatsu.core.backup.BackupEntry
import org.koitharu.kotatsu.core.backup.BackupRepository import org.koitharu.kotatsu.core.backup.BackupRepository
@@ -29,7 +31,7 @@ class AppBackupAgent : BackupAgent() {
override fun onFullBackup(data: FullBackupDataOutput) { override fun onFullBackup(data: FullBackupDataOutput) {
super.onFullBackup(data) super.onFullBackup(data)
val file = createBackupFile() val file = createBackupFile(this, BackupRepository(MangaDatabase(applicationContext)))
try { try {
fullBackupFile(file, data) fullBackupFile(file, data)
} finally { } finally {
@@ -46,16 +48,16 @@ class AppBackupAgent : BackupAgent() {
mtime: Long mtime: Long
) { ) {
if (destination?.name?.endsWith(".bk.zip") == true) { if (destination?.name?.endsWith(".bk.zip") == true) {
restoreBackupFile(data.fileDescriptor, size) restoreBackupFile(data.fileDescriptor, size, BackupRepository(MangaDatabase(applicationContext)))
destination.delete() destination.delete()
} else { } else {
super.onRestoreFile(data, size, destination, type, mode, mtime) super.onRestoreFile(data, size, destination, type, mode, mtime)
} }
} }
private fun createBackupFile() = runBlocking { @VisibleForTesting
val repository = BackupRepository(MangaDatabase(applicationContext)) fun createBackupFile(context: Context, repository: BackupRepository) = runBlocking {
BackupZipOutput(this@AppBackupAgent).use { backup -> BackupZipOutput(context).use { backup ->
backup.put(repository.createIndex()) backup.put(repository.createIndex())
backup.put(repository.dumpHistory()) backup.put(repository.dumpHistory())
backup.put(repository.dumpCategories()) backup.put(repository.dumpCategories())
@@ -65,8 +67,8 @@ class AppBackupAgent : BackupAgent() {
} }
} }
private fun restoreBackupFile(fd: FileDescriptor, size: Long) { @VisibleForTesting
val repository = BackupRepository(MangaDatabase(applicationContext)) fun restoreBackupFile(fd: FileDescriptor, size: Long, repository: BackupRepository) {
val tempFile = File.createTempFile("backup_", ".tmp") val tempFile = File.createTempFile("backup_", ".tmp")
FileInputStream(fd).use { input -> FileInputStream(fd).use { input ->
tempFile.outputStream().use { output -> tempFile.outputStream().use { output ->