Skip to content

Commit

Permalink
Switch everything to new backends
Browse files Browse the repository at this point in the history
  • Loading branch information
grote committed Aug 27, 2024
1 parent ca02cea commit b14b6b2
Show file tree
Hide file tree
Showing 91 changed files with 443 additions and 1,568 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class KoinInstrumentationTestApp : App() {
apkRestore = get(),
iconManager = get(),
storageBackup = get(),
pluginManager = get(),
backendManager = get(),
fileSelectionManager = get(),
)
)
Expand Down
82 changes: 36 additions & 46 deletions app/src/androidTest/java/com/stevesoltys/seedvault/PluginTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ import androidx.test.core.content.pm.PackageInfoBuilder
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.platform.app.InstrumentationRegistry
import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderLegacyPlugin
import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderStoragePlugin
import com.stevesoltys.seedvault.plugins.saf.DocumentsStorage
import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
import com.stevesoltys.seedvault.backend.LegacyStoragePlugin
import com.stevesoltys.seedvault.backend.getAvailableBackups
import com.stevesoltys.seedvault.backend.saf.DocumentsProviderLegacyPlugin
import com.stevesoltys.seedvault.backend.saf.DocumentsStorage
import com.stevesoltys.seedvault.settings.SettingsManager
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
import org.calyxos.seedvault.core.backends.saf.SafBackend
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
Expand All @@ -38,11 +39,10 @@ class PluginTest : KoinComponent {
private val mockedSettingsManager: SettingsManager = mockk()
private val storage = DocumentsStorage(
appContext = context,
settingsManager = mockedSettingsManager,
safStorage = settingsManager.getSafStorage() ?: error("No SAF storage"),
safStorage = settingsManager.getSafProperties() ?: error("No SAF storage"),
)

private val storagePlugin = DocumentsProviderStoragePlugin(context, storage.safStorage)
private val backend = SafBackend(context, storage.safStorage)

@Suppress("Deprecation")
private val legacyStoragePlugin: LegacyStoragePlugin = DocumentsProviderLegacyPlugin(context) {
Expand All @@ -55,29 +55,30 @@ class PluginTest : KoinComponent {

@Before
fun setup() = runBlocking {
every { mockedSettingsManager.getSafStorage() } returns settingsManager.getSafStorage()
storagePlugin.removeAll()
every {
mockedSettingsManager.getSafProperties()
} returns settingsManager.getSafProperties()
backend.removeAll()
}

@After
fun tearDown() = runBlocking {
storagePlugin.removeAll()
Unit
backend.removeAll()
}

@Test
fun testProviderPackageName() {
assertNotNull(storagePlugin.providerPackageName)
assertNotNull(backend.providerPackageName)
}

@Test
fun testTest() = runBlocking(Dispatchers.IO) {
assertTrue(storagePlugin.test())
assertTrue(backend.test())
}

@Test
fun testGetFreeSpace() = runBlocking(Dispatchers.IO) {
val freeBytes = storagePlugin.getFreeSpace() ?: error("no free space retrieved")
val freeBytes = backend.getFreeSpace() ?: error("no free space retrieved")
assertTrue(freeBytes > 0)
}

Expand All @@ -91,49 +92,39 @@ class PluginTest : KoinComponent {
@Test
fun testInitializationAndRestoreSets() = runBlocking(Dispatchers.IO) {
// no backups available initially
assertEquals(0, storagePlugin.getAvailableBackups()?.toList()?.size)
assertEquals(0, backend.getAvailableBackups()?.toList()?.size)

// prepare returned tokens requested when initializing device
every { mockedSettingsManager.getToken() } returnsMany listOf(token, token + 1, token + 1)

// start new restore set and initialize device afterwards
storagePlugin.startNewRestoreSet(token)
storagePlugin.initializeDevice()

// write metadata (needed for backup to be recognized)
storagePlugin.getOutputStream(token, FILE_BACKUP_METADATA)
backend.save(LegacyAppBackupFile.Metadata(token))
.writeAndClose(getRandomByteArray())

// one backup available now
assertEquals(1, storagePlugin.getAvailableBackups()?.toList()?.size)
assertEquals(1, backend.getAvailableBackups()?.toList()?.size)

// initializing again (with another restore set) does add a restore set
storagePlugin.startNewRestoreSet(token + 1)
storagePlugin.initializeDevice()
storagePlugin.getOutputStream(token + 1, FILE_BACKUP_METADATA)
backend.save(LegacyAppBackupFile.Metadata(token + 1))
.writeAndClose(getRandomByteArray())
assertEquals(2, storagePlugin.getAvailableBackups()?.toList()?.size)
assertEquals(2, backend.getAvailableBackups()?.toList()?.size)

// initializing again (without new restore set) doesn't change number of restore sets
storagePlugin.initializeDevice()
storagePlugin.getOutputStream(token + 1, FILE_BACKUP_METADATA)
backend.save(LegacyAppBackupFile.Metadata(token + 1))
.writeAndClose(getRandomByteArray())
assertEquals(2, storagePlugin.getAvailableBackups()?.toList()?.size)
assertEquals(2, backend.getAvailableBackups()?.toList()?.size)
}

@Test
fun testMetadataWriteRead() = runBlocking(Dispatchers.IO) {
every { mockedSettingsManager.getToken() } returns token

storagePlugin.startNewRestoreSet(token)
storagePlugin.initializeDevice()

// write metadata
val metadata = getRandomByteArray()
storagePlugin.getOutputStream(token, FILE_BACKUP_METADATA).writeAndClose(metadata)
backend.save(LegacyAppBackupFile.Metadata(token)).writeAndClose(metadata)

// get available backups, expect only one with our token and no error
var availableBackups = storagePlugin.getAvailableBackups()?.toList()
var availableBackups = backend.getAvailableBackups()?.toList()
check(availableBackups != null)
assertEquals(1, availableBackups.size)
assertEquals(token, availableBackups[0].token)
Expand All @@ -142,9 +133,8 @@ class PluginTest : KoinComponent {
assertReadEquals(metadata, availableBackups[0].inputStreamRetriever())

// initializing again (without changing storage) keeps restore set with same token
storagePlugin.initializeDevice()
storagePlugin.getOutputStream(token, FILE_BACKUP_METADATA).writeAndClose(metadata)
availableBackups = storagePlugin.getAvailableBackups()?.toList()
backend.save(LegacyAppBackupFile.Metadata(token)).writeAndClose(metadata)
availableBackups = backend.getAvailableBackups()?.toList()
check(availableBackups != null)
assertEquals(1, availableBackups.size)
assertEquals(token, availableBackups[0].token)
Expand All @@ -161,7 +151,8 @@ class PluginTest : KoinComponent {

// write random bytes as APK
val apk1 = getRandomByteArray(1337 * 1024)
storagePlugin.getOutputStream(token, "${packageInfo.packageName}.apk").writeAndClose(apk1)
backend.save(LegacyAppBackupFile.Blob(token, "${packageInfo.packageName}.apk"))
.writeAndClose(apk1)

// assert that read APK bytes match what was written
assertReadEquals(
Expand All @@ -173,7 +164,7 @@ class PluginTest : KoinComponent {
val suffix2 = getRandomBase64(23)
val apk2 = getRandomByteArray(23 * 1024 * 1024)

storagePlugin.getOutputStream(token, "${packageInfo2.packageName}$suffix2.apk")
backend.save(LegacyAppBackupFile.Blob(token, "${packageInfo2.packageName}$suffix2.apk"))
.writeAndClose(apk2)

// assert that read APK bytes match what was written
Expand All @@ -193,26 +184,25 @@ class PluginTest : KoinComponent {

// write full backup data
val data = getRandomByteArray(5 * 1024 * 1024)
storagePlugin.getOutputStream(token, name1).writeAndClose(data)
backend.save(LegacyAppBackupFile.Blob(token, name1)).writeAndClose(data)

// restore data matches backed up data
assertReadEquals(data, storagePlugin.getInputStream(token, name1))
assertReadEquals(data, backend.load(LegacyAppBackupFile.Blob(token, name1)))

// write and check data for second package
val data2 = getRandomByteArray(5 * 1024 * 1024)
storagePlugin.getOutputStream(token, name2).writeAndClose(data2)
assertReadEquals(data2, storagePlugin.getInputStream(token, name2))
backend.save(LegacyAppBackupFile.Blob(token, name2)).writeAndClose(data2)
assertReadEquals(data2, backend.load(LegacyAppBackupFile.Blob(token, name2)))

// remove data of first package again and ensure that no more data is found
storagePlugin.removeData(token, name1)
backend.remove(LegacyAppBackupFile.Blob(token, name1))

// ensure that it gets deleted as well
storagePlugin.removeData(token, name2)
backend.remove(LegacyAppBackupFile.Blob(token, name2))
}

private fun initStorage(token: Long) = runBlocking {
every { mockedSettingsManager.getToken() } returns token
storagePlugin.initializeDevice()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

package com.stevesoltys.seedvault.plugins.saf
package com.stevesoltys.seedvault.backend.saf

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
Expand All @@ -13,7 +13,7 @@ import kotlinx.coroutines.runBlocking
import org.calyxos.seedvault.core.backends.Backend
import org.calyxos.seedvault.core.backends.BackendTest
import org.calyxos.seedvault.core.backends.saf.SafBackend
import org.calyxos.seedvault.core.backends.saf.SafConfig
import org.calyxos.seedvault.core.backends.saf.SafProperties
import org.junit.Test
import org.junit.runner.RunWith
import org.koin.core.component.KoinComponent
Expand All @@ -27,15 +27,15 @@ class SafBackendTest : BackendTest(), KoinComponent {
private val settingsManager by inject<SettingsManager>()
override val plugin: Backend
get() {
val safStorage = settingsManager.getSafStorage() ?: error("No SAF storage")
val safConfig = SafConfig(
val safStorage = settingsManager.getSafProperties() ?: error("No SAF storage")
val safProperties = SafProperties(
config = safStorage.config,
name = safStorage.name,
isUsb = safStorage.isUsb,
requiresNetwork = safStorage.requiresNetwork,
rootId = safStorage.rootId,
)
return SafBackend(context, safConfig, ".SeedvaultTest")
return SafBackend(context, safProperties, ".SeedvaultTest")
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal class BackupRestoreTest : SeedvaultLargeTest() {
confirmCode()
}

if (settingsManager.getSafStorage() == null) {
if (settingsManager.getSafProperties() == null) {
chooseStorageLocation()
} else {
changeBackupLocation()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import android.content.pm.PackageInfo
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.stevesoltys.seedvault.plugins.StoragePluginManager
import com.stevesoltys.seedvault.backend.BackendManager
import com.stevesoltys.seedvault.settings.AppStatus
import com.stevesoltys.seedvault.settings.SettingsManager
import io.mockk.every
Expand All @@ -30,9 +30,9 @@ class PackageServiceTest : KoinComponent {

private val settingsManager: SettingsManager by inject()

private val storagePluginManager: StoragePluginManager by inject()
private val backendManager: BackendManager by inject()

private val backend: Backend get() = storagePluginManager.backend
private val backend: Backend get() = backendManager.backend

@Test
fun testNotAllowedPackages() {
Expand Down
20 changes: 11 additions & 9 deletions app/src/main/java/com/stevesoltys/seedvault/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import com.stevesoltys.seedvault.crypto.cryptoModule
import com.stevesoltys.seedvault.header.headerModule
import com.stevesoltys.seedvault.metadata.MetadataManager
import com.stevesoltys.seedvault.metadata.metadataModule
import com.stevesoltys.seedvault.plugins.StoragePluginManager
import com.stevesoltys.seedvault.plugins.saf.storagePluginModuleSaf
import com.stevesoltys.seedvault.plugins.webdav.storagePluginModuleWebDav
import com.stevesoltys.seedvault.backend.BackendManager
import com.stevesoltys.seedvault.backend.saf.storagePluginModuleSaf
import com.stevesoltys.seedvault.backend.webdav.storagePluginModuleWebDav
import com.stevesoltys.seedvault.restore.install.installModule
import com.stevesoltys.seedvault.restore.restoreUiModule
import com.stevesoltys.seedvault.settings.AppListRetriever
Expand All @@ -42,6 +42,7 @@ import com.stevesoltys.seedvault.ui.storage.BackupStorageViewModel
import com.stevesoltys.seedvault.ui.storage.RestoreStorageViewModel
import com.stevesoltys.seedvault.worker.AppBackupWorker
import com.stevesoltys.seedvault.worker.workerModule
import org.calyxos.seedvault.core.backends.BackendFactory
import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
Expand All @@ -61,7 +62,8 @@ open class App : Application() {
private val appModule = module {
single { SettingsManager(this@App) }
single { BackupNotificationManager(this@App) }
single { StoragePluginManager(this@App, get(), get(), get()) }
single { BackendManager(this@App, get(), get()) }
single { BackendFactory(this@App) }
single { BackupStateManager(this@App) }
single { Clock() }
factory<IBackupManager> { IBackupManager.Stub.asInterface(getService(BACKUP_SERVICE)) }
Expand All @@ -72,7 +74,7 @@ open class App : Application() {
app = this@App,
settingsManager = get(),
keyManager = get(),
pluginManager = get(),
backendManager = get(),
metadataManager = get(),
appListRetriever = get(),
storageBackup = get(),
Expand All @@ -91,7 +93,7 @@ open class App : Application() {
safHandler = get(),
webDavHandler = get(),
settingsManager = get(),
storagePluginManager = get(),
backendManager = get(),
)
}
viewModel { RestoreStorageViewModel(this@App, get(), get(), get(), get()) }
Expand Down Expand Up @@ -146,7 +148,7 @@ open class App : Application() {
private val settingsManager: SettingsManager by inject()
private val metadataManager: MetadataManager by inject()
private val backupManager: IBackupManager by inject()
private val pluginManager: StoragePluginManager by inject()
private val backendManager: BackendManager by inject()
private val backupStateManager: BackupStateManager by inject()

/**
Expand All @@ -170,13 +172,13 @@ open class App : Application() {
protected open fun migrateToOwnScheduling() {
if (!backupStateManager.isFrameworkSchedulingEnabled) { // already on own scheduling
// fix things for removable drive users who had a job scheduled here before
if (pluginManager.isOnRemovableDrive) AppBackupWorker.unschedule(applicationContext)
if (backendManager.isOnRemovableDrive) AppBackupWorker.unschedule(applicationContext)
return
}

if (backupManager.currentTransport == TRANSPORT_ID) {
backupManager.setFrameworkSchedulingEnabledForUser(UserHandle.myUserId(), false)
if (backupManager.isBackupEnabled && !pluginManager.isOnRemovableDrive) {
if (backupManager.isBackupEnabled && !backendManager.isOnRemovableDrive) {
AppBackupWorker.schedule(applicationContext, settingsManager, UPDATE)
}
// cancel old D2D worker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
* SPDX-License-Identifier: Apache-2.0
*/

package com.stevesoltys.seedvault.plugins
package com.stevesoltys.seedvault.backend

import android.util.Log
import at.bitfire.dav4jvm.exception.HttpException
import org.calyxos.seedvault.core.backends.Backend
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream

suspend fun Backend.getMetadataOutputStream(token: Long): OutputStream {
Expand Down Expand Up @@ -35,3 +38,16 @@ suspend fun Backend.getAvailableBackups(): Sequence<EncryptedMetadata>? {
null
}
}

fun Exception.isOutOfSpace(): Boolean {
return when (this) {
is IOException -> message?.contains("No space left on device") == true ||
(cause as? HttpException)?.code == 507

is HttpException -> code == 507

else -> false
}
}

class EncryptedMetadata(val token: Long, val inputStreamRetriever: suspend () -> InputStream)
Loading

0 comments on commit b14b6b2

Please sign in to comment.