Make PluginTest work for Nextcloud as well

Only issue left was a different maximum file name length for Nextcloud
This commit is contained in:
Torsten Grote 2020-08-12 15:21:22 -03:00 committed by Chirayu Desai
parent 2958c8fac8
commit 30e66f368e
3 changed files with 59 additions and 14 deletions

View file

@ -7,6 +7,8 @@ import com.stevesoltys.seedvault.metadata.MetadataManager
import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderBackupPlugin import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderBackupPlugin
import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderRestorePlugin import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderRestorePlugin
import com.stevesoltys.seedvault.plugins.saf.DocumentsStorage import com.stevesoltys.seedvault.plugins.saf.DocumentsStorage
import com.stevesoltys.seedvault.plugins.saf.MAX_KEY_LENGTH
import com.stevesoltys.seedvault.plugins.saf.MAX_KEY_LENGTH_NEXTCLOUD
import com.stevesoltys.seedvault.plugins.saf.deleteContents import com.stevesoltys.seedvault.plugins.saf.deleteContents
import com.stevesoltys.seedvault.settings.SettingsManager import com.stevesoltys.seedvault.settings.SettingsManager
import com.stevesoltys.seedvault.transport.backup.BackupPlugin import com.stevesoltys.seedvault.transport.backup.BackupPlugin
@ -25,9 +27,9 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
import org.koin.core.inject import org.koin.core.inject
import java.io.IOException
import kotlin.random.Random import kotlin.random.Random
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
class PluginTest : KoinComponent { class PluginTest : KoinComponent {
@ -48,7 +50,7 @@ class PluginTest : KoinComponent {
fun setup() { fun setup() {
every { mockedSettingsManager.getStorage() } returns settingsManager.getStorage() every { mockedSettingsManager.getStorage() } returns settingsManager.getStorage()
storage.rootBackupDir?.deleteContents() storage.rootBackupDir?.deleteContents()
?: error("Select a storage location in the app first!") ?: error("Select a storage location in the app first!")
} }
@After @After
@ -162,7 +164,7 @@ class PluginTest : KoinComponent {
// define key/value pair records // define key/value pair records
val record1 = Pair(getRandomBase64(23), getRandomByteArray(1337)) val record1 = Pair(getRandomBase64(23), getRandomByteArray(1337))
val record2 = Pair(getRandomBase64(42), getRandomByteArray(42 * 1024)) val record2 = Pair(getRandomBase64(42), getRandomByteArray(42 * 1024))
val record3 = Pair(getRandomBase64(255), getRandomByteArray(5 * 1024 * 1024)) val record3 = Pair(getRandomBase64(128), getRandomByteArray(5 * 1024 * 1024))
// write first record // write first record
kvBackup.ensureRecordStorageForPackage(packageInfo) kvBackup.ensureRecordStorageForPackage(packageInfo)
@ -177,7 +179,10 @@ class PluginTest : KoinComponent {
var records = kvRestore.listRecords(token, packageInfo) var records = kvRestore.listRecords(token, packageInfo)
assertEquals(1, records.size) assertEquals(1, records.size)
assertEquals(record1.first, records[0]) assertEquals(record1.first, records[0])
assertReadEquals(record1.second, kvRestore.getInputStreamForRecord(token, packageInfo, record1.first)) assertReadEquals(
record1.second,
kvRestore.getInputStreamForRecord(token, packageInfo, record1.first)
)
// write second and third record // write second and third record
kvBackup.ensureRecordStorageForPackage(packageInfo) kvBackup.ensureRecordStorageForPackage(packageInfo)
@ -187,9 +192,18 @@ class PluginTest : KoinComponent {
// all records for package are found and returned properly // all records for package are found and returned properly
records = kvRestore.listRecords(token, packageInfo) records = kvRestore.listRecords(token, packageInfo)
assertEquals(listOf(record1.first, record2.first, record3.first).sorted(), records.sorted()) assertEquals(listOf(record1.first, record2.first, record3.first).sorted(), records.sorted())
assertReadEquals(record1.second, kvRestore.getInputStreamForRecord(token, packageInfo, record1.first)) assertReadEquals(
assertReadEquals(record2.second, kvRestore.getInputStreamForRecord(token, packageInfo, record2.first)) record1.second,
assertReadEquals(record3.second, kvRestore.getInputStreamForRecord(token, packageInfo, record3.first)) kvRestore.getInputStreamForRecord(token, packageInfo, record1.first)
)
assertReadEquals(
record2.second,
kvRestore.getInputStreamForRecord(token, packageInfo, record2.first)
)
assertReadEquals(
record3.second,
kvRestore.getInputStreamForRecord(token, packageInfo, record3.first)
)
// delete record3 and ensure that the other two are still found // delete record3 and ensure that the other two are still found
kvBackup.deleteRecord(packageInfo, record3.first) kvBackup.deleteRecord(packageInfo, record3.first)
@ -211,13 +225,17 @@ class PluginTest : KoinComponent {
// initialize storage with given token // initialize storage with given token
initStorage(token) initStorage(token)
// FIXME get Nextcloud to have the same limit
val max = if (isNextcloud()) MAX_KEY_LENGTH_NEXTCLOUD else MAX_KEY_LENGTH
// define record with maximum key length and one above the maximum // define record with maximum key length and one above the maximum
val recordMax = Pair(getRandomBase64(255), getRandomByteArray(1024)) val recordMax = Pair(getRandomBase64(max), getRandomByteArray(1024))
val recordOver = Pair(getRandomBase64(256), getRandomByteArray(1024)) val recordOver = Pair(getRandomBase64(max + 1), getRandomByteArray(1024))
// write max record // write max record
kvBackup.ensureRecordStorageForPackage(packageInfo) kvBackup.ensureRecordStorageForPackage(packageInfo)
kvBackup.getOutputStreamForRecord(packageInfo, recordMax.first).writeAndClose(recordMax.second) kvBackup.getOutputStreamForRecord(packageInfo, recordMax.first)
.writeAndClose(recordMax.second)
// max record is found correctly // max record is found correctly
assertTrue(kvRestore.hasDataForPackage(token, packageInfo)) assertTrue(kvRestore.hasDataForPackage(token, packageInfo))
@ -226,8 +244,17 @@ class PluginTest : KoinComponent {
// write exceeding key length record // write exceeding key length record
kvBackup.ensureRecordStorageForPackage(packageInfo) kvBackup.ensureRecordStorageForPackage(packageInfo)
coAssertThrows(IllegalStateException::class.java) { if (isNextcloud()) {
kvBackup.getOutputStreamForRecord(packageInfo, recordOver.first).writeAndClose(recordOver.second) // Nextcloud simply refuses to write long filenames
coAssertThrows(IOException::class.java) {
kvBackup.getOutputStreamForRecord(packageInfo, recordOver.first)
.writeAndClose(recordOver.second)
}
} else {
coAssertThrows(IllegalStateException::class.java) {
kvBackup.getOutputStreamForRecord(packageInfo, recordOver.first)
.writeAndClose(recordOver.second)
}
} }
} }
@ -278,4 +305,8 @@ class PluginTest : KoinComponent {
assertTrue(backupPlugin.initializeDevice(newToken = token)) assertTrue(backupPlugin.initializeDevice(newToken = token))
} }
private fun isNextcloud(): Boolean {
return backupPlugin.providerPackageName == "com.nextcloud.client"
}
} }

View file

@ -54,7 +54,7 @@ class DocumentsStorageTest : KoinComponent {
fun setup() = runBlocking { fun setup() = runBlocking {
assertNotNull("Select a storage location in the app first!", storage.rootBackupDir) assertNotNull("Select a storage location in the app first!", storage.rootBackupDir)
file = storage.rootBackupDir?.createOrGetFile(context, filename) file = storage.rootBackupDir?.createOrGetFile(context, filename)
?: throw RuntimeException("Could not create test file") ?: error("Could not create test file")
} }
@After @After

View file

@ -2,12 +2,16 @@ package com.stevesoltys.seedvault.plugins.saf
import android.content.Context import android.content.Context
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.util.Log
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import com.stevesoltys.seedvault.transport.backup.DEFAULT_QUOTA_KEY_VALUE_BACKUP import com.stevesoltys.seedvault.transport.backup.DEFAULT_QUOTA_KEY_VALUE_BACKUP
import com.stevesoltys.seedvault.transport.backup.KVBackupPlugin import com.stevesoltys.seedvault.transport.backup.KVBackupPlugin
import java.io.IOException import java.io.IOException
import java.io.OutputStream import java.io.OutputStream
const val MAX_KEY_LENGTH = 255
const val MAX_KEY_LENGTH_NEXTCLOUD = 228
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
internal class DocumentsProviderKVBackup( internal class DocumentsProviderKVBackup(
private val storage: DocumentsStorage, private val storage: DocumentsStorage,
@ -37,7 +41,8 @@ internal class DocumentsProviderKVBackup(
override suspend fun removeDataOfPackage(packageInfo: PackageInfo) { override suspend fun removeDataOfPackage(packageInfo: PackageInfo) {
// we cannot use the cached this.packageFile here, // we cannot use the cached this.packageFile here,
// because this can be called before [ensureRecordStorageForPackage] // because this can be called before [ensureRecordStorageForPackage]
val packageFile = storage.currentKvBackupDir?.findFileBlocking(context, packageInfo.packageName) ?: return val packageFile =
storage.currentKvBackupDir?.findFileBlocking(context, packageInfo.packageName) ?: return
packageFile.delete() packageFile.delete()
} }
@ -54,6 +59,15 @@ internal class DocumentsProviderKVBackup(
packageInfo: PackageInfo, packageInfo: PackageInfo,
key: String key: String
): OutputStream { ): OutputStream {
check(key.length < MAX_KEY_LENGTH) {
"Key $key for ${packageInfo.packageName} is too long: ${key.length} chars."
}
if (key.length > MAX_KEY_LENGTH_NEXTCLOUD) {
Log.e(
DocumentsProviderKVBackup::class.simpleName,
"Key $key for ${packageInfo.packageName} is too long: ${key.length} chars."
)
}
val packageFile = this.packageFile ?: throw AssertionError() val packageFile = this.packageFile ?: throw AssertionError()
packageFile.assertRightFile(packageInfo) packageFile.assertRightFile(packageInfo)
val keyFile = packageFile.createOrGetFile(context, key) val keyFile = packageFile.createOrGetFile(context, key)