Skip to content

Commit e41405d

Browse files
committed
Also abort file backup while backup up a single huge file
previously switching to a metered network would have left the upload to continue until the file would be complete
1 parent 01d8694 commit e41405d

File tree

3 files changed

+25
-6
lines changed

3 files changed

+25
-6
lines changed

storage/lib/src/main/java/org/calyxos/backup/storage/backup/ChunkWriter.kt

+2
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ internal class ChunkWriter(
5050
inputStream: InputStream,
5151
chunks: List<Chunk>,
5252
missingChunkIds: List<String>,
53+
wasAborted: () -> Boolean,
5354
): ChunkWriterResult {
5455
var writtenChunks = 0
5556
var writtenBytes = 0L
5657
chunks.forEach { chunk ->
58+
if (wasAborted()) throw IOException("Metered Network")
5759
val cachedChunk = chunksCache.get(chunk.id)
5860
// TODO missing chunks used by several files will get uploaded several times
5961
val isMissing = chunk.id in missingChunkIds

storage/lib/src/main/java/org/calyxos/backup/storage/backup/FileBackup.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ internal class FileBackup(
4444
files.forEach { file ->
4545
if (wasAborted()) throw IOException("Metered Network")
4646
val result = try {
47-
backupFile(file, availableChunkIds)
47+
backupFile(file, availableChunkIds, wasAborted)
4848
} catch (e: IOException) {
4949
backupObserver?.onFileBackupError(file, "L")
5050
Log.e(TAG, "Error backing up ${file.uri}", e)
@@ -81,6 +81,7 @@ internal class FileBackup(
8181
private suspend fun backupFile(
8282
file: ContentFile,
8383
availableChunkIds: Set<String>,
84+
wasAborted: () -> Boolean,
8485
): FileBackupResult {
8586
val cachedFile = filesCache.getByUri(file.uri)
8687
val missingChunkIds = cachedFile?.chunks?.minus(availableChunkIds) ?: emptyList()
@@ -94,7 +95,7 @@ internal class FileBackup(
9495
chunker.makeChunks(inputStream)
9596
}
9697
val chunkWriterResult = uri.openInputStream(contentResolver).use { inputStream ->
97-
chunkWriter.writeChunk(inputStream, chunks, missingChunkIds)
98+
chunkWriter.writeChunk(inputStream, chunks, missingChunkIds, wasAborted)
9899
}
99100

100101
val chunkIds = chunks.map { it.id }

storage/lib/src/test/java/org/calyxos/backup/storage/backup/ChunkWriterTest.kt

+20-4
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ import org.junit.Assert.assertEquals
2929
import org.junit.Test
3030
import java.io.ByteArrayInputStream
3131
import java.io.ByteArrayOutputStream
32+
import java.io.IOException
3233
import java.io.OutputStream
3334
import kotlin.random.Random
35+
import kotlin.test.assertFailsWith
3436

3537
internal class ChunkWriterTest {
3638

@@ -116,7 +118,7 @@ internal class ChunkWriterTest {
116118
every { chunksCache.insert(chunks[1].toCachedChunk(3)) } just Runs
117119
every { chunksCache.insert(chunks[2].toCachedChunk(3)) } just Runs
118120

119-
chunkWriter.writeChunk(inputStream, chunks, emptyList())
121+
chunkWriter.writeChunk(inputStream, chunks, emptyList()) { false }
120122

121123
// check that version was written as the first byte
122124
outputStreams.forEach { outputStream ->
@@ -171,7 +173,7 @@ internal class ChunkWriterTest {
171173
// insert last not cached chunk into cache after upload
172174
every { chunksCache.insert(chunks[2].toCachedChunk(3)) } just Runs
173175

174-
chunkWriter.writeChunk(inputStream, chunks, listOf(chunkId1))
176+
chunkWriter.writeChunk(inputStream, chunks, listOf(chunkId1)) { false }
175177

176178
// check that output matches chunk data
177179
assertEquals(VERSION, chunk1Output.toByteArray()[0])
@@ -234,7 +236,7 @@ internal class ChunkWriterTest {
234236
chunksCache.insert(chunks[2].toCachedChunk(chunk3Bytes.size.toLong() + 1))
235237
} just Runs
236238

237-
chunkWriter.writeChunk(inputStream, chunks, emptyList())
239+
chunkWriter.writeChunk(inputStream, chunks, emptyList()) { false }
238240

239241
// check that version and wrapped key was written as the first byte
240242
outputStreams.forEach { outputStream ->
@@ -291,7 +293,7 @@ internal class ChunkWriterTest {
291293
chunksCache.insert(chunks[0].toCachedChunk(chunkBytes.size.toLong() + 1))
292294
} just Runs
293295

294-
chunkWriter.writeChunk(inputStream, chunks, emptyList())
296+
chunkWriter.writeChunk(inputStream, chunks, emptyList()) { false }
295297

296298
// check that output matches chunk data
297299
assertEquals(1 + chunks[0].plaintextSize.toInt(), chunkOutput1.size())
@@ -313,6 +315,20 @@ internal class ChunkWriterTest {
313315
assertEquals(chunkOutput3.size().toLong(), size2)
314316
}
315317

318+
@Test
319+
fun testAbort() = runBlocking {
320+
val chunkBytes = Random.nextBytes(16 * 1024 * 1024)
321+
val inputStream = ByteArrayInputStream(chunkBytes)
322+
val chunks = listOf(
323+
Chunk(chunkId1, 0, chunkBytes.size.toLong()),
324+
)
325+
326+
val e = assertFailsWith<IOException> {
327+
chunkWriter.writeChunk(inputStream, chunks, emptyList()) { true }
328+
}
329+
assertEquals("Metered Network", e.message)
330+
}
331+
316332
private fun MockKMatcherScope.bytes(size: Int) = match<ByteArray> {
317333
it.size == size
318334
}

0 commit comments

Comments
 (0)