Use DigestInputStream to spy on backup data
this is to save memory to prevent OOM errors we saw on CI
This commit is contained in:
parent
1d17adedff
commit
a538a64395
4 changed files with 26 additions and 77 deletions
|
@ -9,7 +9,6 @@ import android.content.pm.PackageInfo
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import androidx.test.uiautomator.Until
|
import androidx.test.uiautomator.Until
|
||||||
import com.stevesoltys.seedvault.e2e.io.BackupDataInputIntercept
|
import com.stevesoltys.seedvault.e2e.io.BackupDataInputIntercept
|
||||||
import com.stevesoltys.seedvault.e2e.io.InputStreamIntercept
|
|
||||||
import com.stevesoltys.seedvault.e2e.screen.impl.BackupScreen
|
import com.stevesoltys.seedvault.e2e.screen.impl.BackupScreen
|
||||||
import com.stevesoltys.seedvault.transport.backup.FullBackup
|
import com.stevesoltys.seedvault.transport.backup.FullBackup
|
||||||
import com.stevesoltys.seedvault.transport.backup.InputFactory
|
import com.stevesoltys.seedvault.transport.backup.InputFactory
|
||||||
|
@ -21,8 +20,10 @@ import io.mockk.every
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
|
import org.calyxos.seedvault.core.toHexString
|
||||||
import org.koin.core.component.get
|
import org.koin.core.component.get
|
||||||
import java.io.ByteArrayOutputStream
|
import java.security.DigestInputStream
|
||||||
|
import java.security.MessageDigest
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import kotlin.test.fail
|
import kotlin.test.fail
|
||||||
|
|
||||||
|
@ -154,7 +155,8 @@ internal interface LargeBackupTestBase : LargeTestBase {
|
||||||
|
|
||||||
private fun spyOnFullBackupData(backupResult: SeedvaultLargeTestResult) {
|
private fun spyOnFullBackupData(backupResult: SeedvaultLargeTestResult) {
|
||||||
var packageName: String? = null
|
var packageName: String? = null
|
||||||
var dataIntercept = ByteArrayOutputStream()
|
val messageDigest = MessageDigest.getInstance("SHA-256")
|
||||||
|
var digestInputStream: DigestInputStream? = null
|
||||||
|
|
||||||
coEvery {
|
coEvery {
|
||||||
spyFullBackup.performFullBackup(any(), any(), any())
|
spyFullBackup.performFullBackup(any(), any(), any())
|
||||||
|
@ -166,20 +168,19 @@ internal interface LargeBackupTestBase : LargeTestBase {
|
||||||
every {
|
every {
|
||||||
spyInputFactory.getInputStream(any())
|
spyInputFactory.getInputStream(any())
|
||||||
} answers {
|
} answers {
|
||||||
InputStreamIntercept(
|
digestInputStream = DigestInputStream(callOriginal(), messageDigest)
|
||||||
inputStream = callOriginal(),
|
digestInputStream!!
|
||||||
intercept = dataIntercept
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
coEvery {
|
coEvery {
|
||||||
spyFullBackup.finishBackup()
|
spyFullBackup.finishBackup()
|
||||||
} answers {
|
} answers {
|
||||||
val result = callOriginal()
|
val result = callOriginal()
|
||||||
backupResult.full[packageName!!] = dataIntercept.toByteArray().sha256()
|
val digest = digestInputStream?.messageDigest ?: fail("No digestInputStream")
|
||||||
|
backupResult.full[packageName!!] = digest.digest().toHexString()
|
||||||
|
|
||||||
packageName = null
|
packageName = null
|
||||||
dataIntercept = ByteArrayOutputStream()
|
digest.reset()
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ package com.stevesoltys.seedvault.e2e
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import com.stevesoltys.seedvault.e2e.io.BackupDataOutputIntercept
|
import com.stevesoltys.seedvault.e2e.io.BackupDataOutputIntercept
|
||||||
import com.stevesoltys.seedvault.e2e.io.OutputStreamIntercept
|
|
||||||
import com.stevesoltys.seedvault.e2e.screen.impl.RecoveryCodeScreen
|
import com.stevesoltys.seedvault.e2e.screen.impl.RecoveryCodeScreen
|
||||||
import com.stevesoltys.seedvault.e2e.screen.impl.RestoreScreen
|
import com.stevesoltys.seedvault.e2e.screen.impl.RestoreScreen
|
||||||
import com.stevesoltys.seedvault.transport.restore.FullRestore
|
import com.stevesoltys.seedvault.transport.restore.FullRestore
|
||||||
|
@ -24,8 +23,11 @@ import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
|
import org.calyxos.seedvault.core.toHexString
|
||||||
import org.koin.core.component.get
|
import org.koin.core.component.get
|
||||||
import java.io.ByteArrayOutputStream
|
import java.security.DigestOutputStream
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
internal interface LargeRestoreTestBase : LargeTestBase {
|
internal interface LargeRestoreTestBase : LargeTestBase {
|
||||||
|
|
||||||
|
@ -196,7 +198,8 @@ internal interface LargeRestoreTestBase : LargeTestBase {
|
||||||
|
|
||||||
private fun spyOnFullRestoreData(restoreResult: SeedvaultLargeTestResult) {
|
private fun spyOnFullRestoreData(restoreResult: SeedvaultLargeTestResult) {
|
||||||
var packageName: String? = null
|
var packageName: String? = null
|
||||||
var dataIntercept = ByteArrayOutputStream()
|
val messageDigest = MessageDigest.getInstance("SHA-256")
|
||||||
|
var digestOutputStream: DigestOutputStream? = null
|
||||||
|
|
||||||
clearMocks(spyFullRestore)
|
clearMocks(spyFullRestore)
|
||||||
|
|
||||||
|
@ -204,11 +207,13 @@ internal interface LargeRestoreTestBase : LargeTestBase {
|
||||||
packageInfoIndex: Int
|
packageInfoIndex: Int
|
||||||
): MockKAnswerScope<Unit, Unit>.(Call) -> Unit = {
|
): MockKAnswerScope<Unit, Unit>.(Call) -> Unit = {
|
||||||
packageName?.let {
|
packageName?.let {
|
||||||
restoreResult.full[it] = dataIntercept.toByteArray().sha256()
|
// sometimes finishRestore() doesn't get called, so get data from last package here
|
||||||
|
digestOutputStream?.messageDigest?.let { digest ->
|
||||||
|
restoreResult.full[packageName!!] = digest.digest().toHexString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
packageName = arg<PackageInfo>(packageInfoIndex).packageName
|
packageName = arg<PackageInfo>(packageInfoIndex).packageName
|
||||||
dataIntercept = ByteArrayOutputStream()
|
|
||||||
|
|
||||||
callOriginal()
|
callOriginal()
|
||||||
}
|
}
|
||||||
|
@ -228,27 +233,26 @@ internal interface LargeRestoreTestBase : LargeTestBase {
|
||||||
every {
|
every {
|
||||||
spyOutputFactory.getOutputStream(any())
|
spyOutputFactory.getOutputStream(any())
|
||||||
} answers {
|
} answers {
|
||||||
OutputStreamIntercept(
|
digestOutputStream = DigestOutputStream(callOriginal(), messageDigest)
|
||||||
outputStream = callOriginal(),
|
digestOutputStream!!
|
||||||
intercept = dataIntercept
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
every {
|
every {
|
||||||
spyFullRestore.abortFullRestore()
|
spyFullRestore.abortFullRestore()
|
||||||
} answers {
|
} answers {
|
||||||
packageName = null
|
packageName = null
|
||||||
dataIntercept = ByteArrayOutputStream()
|
digestOutputStream?.messageDigest?.reset()
|
||||||
callOriginal()
|
callOriginal()
|
||||||
}
|
}
|
||||||
|
|
||||||
every {
|
every {
|
||||||
spyFullRestore.finishRestore()
|
spyFullRestore.finishRestore()
|
||||||
} answers {
|
} answers {
|
||||||
restoreResult.full[packageName!!] = dataIntercept.toByteArray().sha256()
|
val digest = digestOutputStream?.messageDigest ?: fail("No digestOutputStream")
|
||||||
|
restoreResult.full[packageName!!] = digest.digest().toHexString()
|
||||||
|
|
||||||
packageName = null
|
packageName = null
|
||||||
dataIntercept = ByteArrayOutputStream()
|
digest.reset()
|
||||||
callOriginal()
|
callOriginal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2023 The Calyx Institute
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.stevesoltys.seedvault.e2e.io
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
class InputStreamIntercept(
|
|
||||||
private val inputStream: InputStream,
|
|
||||||
private val intercept: ByteArrayOutputStream
|
|
||||||
) : InputStream() {
|
|
||||||
|
|
||||||
override fun read(): Int {
|
|
||||||
val byte = inputStream.read()
|
|
||||||
if (byte != -1) {
|
|
||||||
intercept.write(byte)
|
|
||||||
}
|
|
||||||
return byte
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(buffer: ByteArray, offset: Int, length: Int): Int {
|
|
||||||
val bytesRead = inputStream.read(buffer, offset, length)
|
|
||||||
if (bytesRead != -1) {
|
|
||||||
intercept.write(buffer, offset, bytesRead)
|
|
||||||
}
|
|
||||||
return bytesRead
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2023 The Calyx Institute
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.stevesoltys.seedvault.e2e.io
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.OutputStream
|
|
||||||
|
|
||||||
class OutputStreamIntercept(
|
|
||||||
private val outputStream: OutputStream,
|
|
||||||
private val intercept: ByteArrayOutputStream
|
|
||||||
) : OutputStream() {
|
|
||||||
|
|
||||||
override fun write(byte: Int) {
|
|
||||||
intercept.write(byte)
|
|
||||||
outputStream.write(byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(buffer: ByteArray, offset: Int, length: Int) {
|
|
||||||
intercept.write(buffer, offset, length)
|
|
||||||
outputStream.write(buffer, offset, length)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue