Make a protobuf based backup system (#3936)

* Make a protobuf based backup system

* Cleanup

* More cleanup

* Fix restores always loading the full backup restore, even when legacy restore was used

* Make offline the default

(cherry picked from commit f6fd8a8ddb90869f3e28fd8fcd81a2125f8e0527)

* Find chapter based on the url

(cherry picked from commit 326dc2700944a60da381d82cd9782c5f0d335902)

* Dont break after finding one chapter

(cherry picked from commit f91d1af37398619cf371e4920b60f6d309799c74)

* Also apply changes to online restore

(cherry picked from commit e7c16cd0d14ea5d50ce4a9a3dfa8ca768be702f2)

* Rewrite backup categories

(cherry picked from commit f4200e2146a9c540675767206ed4664894aa1216)

* Dedupe some code, move over read and bookmarks properly

(cherry picked from commit d9ce86aca66945c831670a1523d8bc69966312df)

* Move some functions to the abstract backup manager

(cherry picked from commit b0c658741a2f506bc31823f1f0347772bc119d2e)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt

* Fix some backup duplication issues

(cherry picked from commit a4a1c2827c4537d2d07a0cb589dc1c3be1d65185)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt

* Fix a missed bundleOf

* So glad this wasnt merged before now, everything should be working with this commit
This commit is contained in:
jobobby04
2020-11-20 22:34:24 -05:00
committed by GitHub
parent a150762c63
commit 682fae12b6
36 changed files with 1844 additions and 545 deletions

View File

@@ -8,8 +8,9 @@ import com.google.gson.JsonArray
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.CustomRobolectricGradleTestRunner
import eu.kanade.tachiyomi.data.backup.models.Backup
import eu.kanade.tachiyomi.data.backup.models.DHistory
import eu.kanade.tachiyomi.data.backup.legacy.LegacyBackupManager
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup
import eu.kanade.tachiyomi.data.backup.legacy.models.DHistory
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Chapter
@@ -37,7 +38,7 @@ import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addSingleton
/**
* Test class for the [BackupManager].
* Test class for the [LegacyBackupManager].
* Note that this does not include the backup create/restore services.
*/
@Config(constants = BuildConfig::class, sdk = [Build.VERSION_CODES.LOLLIPOP])
@@ -59,7 +60,7 @@ class BackupTest {
lateinit var context: Context
lateinit var source: HttpSource
lateinit var backupManager: BackupManager
lateinit var legacyBackupManager: LegacyBackupManager
lateinit var db: DatabaseHelper
@@ -67,8 +68,8 @@ class BackupTest {
fun setup() {
app = RuntimeEnvironment.application
context = app.applicationContext
backupManager = BackupManager(context)
db = backupManager.databaseHelper
legacyBackupManager = LegacyBackupManager(context)
db = legacyBackupManager.databaseHelper
// Mock the source manager
val module = object : InjektModule {
@@ -79,7 +80,7 @@ class BackupTest {
Injekt.importModule(module)
source = mock(HttpSource::class.java)
`when`(backupManager.sourceManager.get(anyLong())).thenReturn(source)
`when`(legacyBackupManager.sourceManager.get(anyLong())).thenReturn(source)
root.add(Backup.MANGAS, mangaEntries)
root.add(Backup.CATEGORIES, categoryEntries)
@@ -94,10 +95,10 @@ class BackupTest {
initializeJsonTest(2)
// Create backup of empty database
backupManager.backupCategories(categoryEntries)
legacyBackupManager.backupCategories(categoryEntries)
// Restore Json
backupManager.restoreCategories(categoryEntries)
legacyBackupManager.restoreCategories(categoryEntries)
// Check if empty
val dbCats = db.getCategories().executeAsBlocking()
@@ -116,10 +117,10 @@ class BackupTest {
val category = addSingleCategory("category")
// Restore Json
backupManager.restoreCategories(categoryEntries)
legacyBackupManager.restoreCategories(categoryEntries)
// Check if successful
val dbCats = backupManager.databaseHelper.getCategories().executeAsBlocking()
val dbCats = legacyBackupManager.databaseHelper.getCategories().executeAsBlocking()
assertThat(dbCats).hasSize(1)
assertThat(dbCats[0].name).isEqualTo(category.name)
}
@@ -143,10 +144,10 @@ class BackupTest {
db.insertCategory(category).executeAsBlocking()
// Restore Json
backupManager.restoreCategories(categoryEntries)
legacyBackupManager.restoreCategories(categoryEntries)
// Check if successful
val dbCats = backupManager.databaseHelper.getCategories().executeAsBlocking()
val dbCats = legacyBackupManager.databaseHelper.getCategories().executeAsBlocking()
assertThat(dbCats).hasSize(5)
assertThat(dbCats[0].name).isEqualTo(category.name)
assertThat(dbCats[1].name).isEqualTo(category2.name)
@@ -168,27 +169,27 @@ class BackupTest {
manga.viewer = 3
manga.id = db.insertManga(manga).executeAsBlocking().insertedId()
var favoriteManga = backupManager.databaseHelper.getFavoriteMangas().executeAsBlocking()
var favoriteManga = legacyBackupManager.databaseHelper.getFavoriteMangas().executeAsBlocking()
assertThat(favoriteManga).hasSize(1)
assertThat(favoriteManga[0].viewer).isEqualTo(3)
// Update json with all options enabled
mangaEntries.add(backupManager.backupMangaObject(manga, 1))
mangaEntries.add(legacyBackupManager.backupMangaObject(manga, 1))
// Change manga in database to default values
val dbManga = getSingleManga("One Piece")
dbManga.id = manga.id
db.insertManga(dbManga).executeAsBlocking()
favoriteManga = backupManager.databaseHelper.getFavoriteMangas().executeAsBlocking()
favoriteManga = legacyBackupManager.databaseHelper.getFavoriteMangas().executeAsBlocking()
assertThat(favoriteManga).hasSize(1)
assertThat(favoriteManga[0].viewer).isEqualTo(0)
// Restore local manga
backupManager.restoreMangaNoFetch(manga, dbManga)
legacyBackupManager.restoreMangaNoFetch(manga, dbManga)
// Test if restore successful
favoriteManga = backupManager.databaseHelper.getFavoriteMangas().executeAsBlocking()
favoriteManga = legacyBackupManager.databaseHelper.getFavoriteMangas().executeAsBlocking()
assertThat(favoriteManga).hasSize(1)
assertThat(favoriteManga[0].viewer).isEqualTo(3)
@@ -196,28 +197,28 @@ class BackupTest {
clearDatabase()
// Test if successful
favoriteManga = backupManager.databaseHelper.getFavoriteMangas().executeAsBlocking()
favoriteManga = legacyBackupManager.databaseHelper.getFavoriteMangas().executeAsBlocking()
assertThat(favoriteManga).hasSize(0)
// Restore Json
// Create JSON from manga to test parser
val json = backupManager.parser.toJsonTree(manga)
val json = legacyBackupManager.parser.toJsonTree(manga)
// Restore JSON from manga to test parser
val jsonManga = backupManager.parser.fromJson<MangaImpl>(json)
val jsonManga = legacyBackupManager.parser.fromJson<MangaImpl>(json)
// Restore manga with fetch observable
val networkManga = getSingleManga("One Piece")
networkManga.description = "This is a description"
`when`(source.fetchMangaDetails(jsonManga)).thenReturn(Observable.just(networkManga))
val obs = backupManager.restoreMangaFetchObservable(source, jsonManga)
val obs = legacyBackupManager.restoreMangaFetchObservable(source, jsonManga)
val testSubscriber = TestSubscriber<Manga>()
obs.subscribe(testSubscriber)
testSubscriber.assertNoErrors()
// Check if restore successful
val dbCats = backupManager.databaseHelper.getFavoriteMangas().executeAsBlocking()
val dbCats = legacyBackupManager.databaseHelper.getFavoriteMangas().executeAsBlocking()
assertThat(dbCats).hasSize(1)
assertThat(dbCats[0].viewer).isEqualTo(3)
assertThat(dbCats[0].description).isEqualTo("This is a description")
@@ -233,7 +234,7 @@ class BackupTest {
// Insert manga
val manga = getSingleManga("One Piece")
manga.id = backupManager.databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
manga.id = legacyBackupManager.databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
// Create restore list
val chapters = mutableListOf<Chapter>()
@@ -244,8 +245,8 @@ class BackupTest {
}
// Check parser
val chaptersJson = backupManager.parser.toJsonTree(chapters)
val restoredChapters = backupManager.parser.fromJson<List<ChapterImpl>>(chaptersJson)
val chaptersJson = legacyBackupManager.parser.toJsonTree(chapters)
val restoredChapters = legacyBackupManager.parser.fromJson<List<ChapterImpl>>(chaptersJson)
// Fetch chapters from upstream
// Create list
@@ -254,13 +255,13 @@ class BackupTest {
`when`(source.fetchChapterList(manga)).thenReturn(Observable.just(chaptersRemote))
// Call restoreChapterFetchObservable
val obs = backupManager.restoreChapterFetchObservable(source, manga, restoredChapters)
val obs = legacyBackupManager.restoreChapterFetchObservable(source, manga, restoredChapters)
val testSubscriber = TestSubscriber<Pair<List<Chapter>, List<Chapter>>>()
obs.subscribe(testSubscriber)
testSubscriber.assertNoErrors()
val dbCats = backupManager.databaseHelper.getChapters(manga).executeAsBlocking()
val dbCats = legacyBackupManager.databaseHelper.getChapters(manga).executeAsBlocking()
assertThat(dbCats).hasSize(10)
assertThat(dbCats[0].read).isEqualTo(true)
}
@@ -274,13 +275,13 @@ class BackupTest {
initializeJsonTest(2)
val manga = getSingleManga("One Piece")
manga.id = backupManager.databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
manga.id = legacyBackupManager.databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
// Create chapter
val chapter = getSingleChapter("Chapter 1")
chapter.manga_id = manga.id
chapter.read = true
chapter.id = backupManager.databaseHelper.insertChapter(chapter).executeAsBlocking().insertedId()
chapter.id = legacyBackupManager.databaseHelper.insertChapter(chapter).executeAsBlocking().insertedId()
val historyJson = getSingleHistory(chapter)
@@ -288,13 +289,13 @@ class BackupTest {
historyList.add(historyJson)
// Check parser
val historyListJson = backupManager.parser.toJsonTree(historyList)
val history = backupManager.parser.fromJson<List<DHistory>>(historyListJson)
val historyListJson = legacyBackupManager.parser.toJsonTree(historyList)
val history = legacyBackupManager.parser.fromJson<List<DHistory>>(historyListJson)
// Restore categories
backupManager.restoreHistoryForManga(history)
legacyBackupManager.restoreHistoryForManga(history)
val historyDB = backupManager.databaseHelper.getHistoryByMangaId(manga.id!!).executeAsBlocking()
val historyDB = legacyBackupManager.databaseHelper.getHistoryByMangaId(manga.id!!).executeAsBlocking()
assertThat(historyDB).hasSize(1)
assertThat(historyDB[0].last_read).isEqualTo(1000)
}
@@ -310,15 +311,15 @@ class BackupTest {
// Create mangas
val manga = getSingleManga("One Piece")
val manga2 = getSingleManga("Bleach")
manga.id = backupManager.databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
manga2.id = backupManager.databaseHelper.insertManga(manga2).executeAsBlocking().insertedId()
manga.id = legacyBackupManager.databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
manga2.id = legacyBackupManager.databaseHelper.insertManga(manga2).executeAsBlocking().insertedId()
// Create track and add it to database
// This tests duplicate errors.
val track = getSingleTrack(manga)
track.last_chapter_read = 5
backupManager.databaseHelper.insertTrack(track).executeAsBlocking()
var trackDB = backupManager.databaseHelper.getTracks(manga).executeAsBlocking()
legacyBackupManager.databaseHelper.insertTrack(track).executeAsBlocking()
var trackDB = legacyBackupManager.databaseHelper.getTracks(manga).executeAsBlocking()
assertThat(trackDB).hasSize(1)
assertThat(trackDB[0].last_chapter_read).isEqualTo(5)
track.last_chapter_read = 7
@@ -330,22 +331,22 @@ class BackupTest {
// Check parser and restore already in database
var trackList = listOf(track)
// Check parser
var trackListJson = backupManager.parser.toJsonTree(trackList)
var trackListRestore = backupManager.parser.fromJson<List<TrackImpl>>(trackListJson)
backupManager.restoreTrackForManga(manga, trackListRestore)
var trackListJson = legacyBackupManager.parser.toJsonTree(trackList)
var trackListRestore = legacyBackupManager.parser.fromJson<List<TrackImpl>>(trackListJson)
legacyBackupManager.restoreTrackForManga(manga, trackListRestore)
// Assert if restore works.
trackDB = backupManager.databaseHelper.getTracks(manga).executeAsBlocking()
trackDB = legacyBackupManager.databaseHelper.getTracks(manga).executeAsBlocking()
assertThat(trackDB).hasSize(1)
assertThat(trackDB[0].last_chapter_read).isEqualTo(7)
// Check parser and restore already in database with lower chapter_read
track.last_chapter_read = 5
trackList = listOf(track)
backupManager.restoreTrackForManga(manga, trackList)
legacyBackupManager.restoreTrackForManga(manga, trackList)
// Assert if restore works.
trackDB = backupManager.databaseHelper.getTracks(manga).executeAsBlocking()
trackDB = legacyBackupManager.databaseHelper.getTracks(manga).executeAsBlocking()
assertThat(trackDB).hasSize(1)
assertThat(trackDB[0].last_chapter_read).isEqualTo(7)
@@ -353,12 +354,12 @@ class BackupTest {
trackList = listOf(track2)
// Check parser
trackListJson = backupManager.parser.toJsonTree(trackList)
trackListRestore = backupManager.parser.fromJson<List<TrackImpl>>(trackListJson)
backupManager.restoreTrackForManga(manga2, trackListRestore)
trackListJson = legacyBackupManager.parser.toJsonTree(trackList)
trackListRestore = legacyBackupManager.parser.fromJson<List<TrackImpl>>(trackListJson)
legacyBackupManager.restoreTrackForManga(manga2, trackListRestore)
// Assert if restore works.
trackDB = backupManager.databaseHelper.getTracks(manga2).executeAsBlocking()
trackDB = legacyBackupManager.databaseHelper.getTracks(manga2).executeAsBlocking()
assertThat(trackDB).hasSize(1)
assertThat(trackDB[0].last_chapter_read).isEqualTo(10)
}
@@ -372,12 +373,12 @@ class BackupTest {
fun initializeJsonTest(version: Int) {
clearJson()
backupManager.setVersion(version)
legacyBackupManager.setVersion(version)
}
fun addSingleCategory(name: String): Category {
val category = Category.create(name)
val catJson = backupManager.parser.toJsonTree(category)
val catJson = legacyBackupManager.parser.toJsonTree(category)
categoryEntries.add(catJson)
return category
}