Skip to content

Commit 190303c

Browse files
authored
Merge pull request #546 from namehillsoftware/bugfix/zero-byte-stored-file-downloads
[Bugfix] Handle Zero-Byte Downloaded Stored File
2 parents 2c159be + 7555171 commit 190303c

File tree

18 files changed

+119
-40
lines changed

18 files changed

+119
-40
lines changed

projectBlueWater/src/main/java/com/lasthopesoftware/bluewater/client/stored/library/items/files/job/StoredFileJobProcessor.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,17 @@ class StoredFileJobProcessor(
8787
else outputStreamWrapper
8888
.promiseCopyFrom(inputStream)
8989
.also(cancellationProxy::doCancel)
90-
.eventually { updateStoredFiles.markStoredFileAsDownloaded(storedFile) }
91-
.then { sf -> StoredFileJobStatus(sf, StoredFileJobState.Downloaded) }
90+
.eventually { downloadedBytes ->
91+
if (downloadedBytes > 0) updateStoredFiles.markStoredFileAsDownloaded(storedFile)
92+
else storedFile.toPromise()
93+
}
94+
.then { sf ->
95+
StoredFileJobStatus(
96+
sf,
97+
if (sf.isDownloadComplete) StoredFileJobState.Downloaded
98+
else StoredFileJobState.Queued
99+
)
100+
}
92101
}
93102
}
94103
}

projectBlueWater/src/main/java/com/lasthopesoftware/resources/io/PromisingOutputStreamWrapper.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ class PromisingOutputStreamWrapper(private val outputStream: OutputStream) : Pro
2626
val buffer = ByteArray(bufferSize)
2727

2828
if (ct.isCancelled) throw CancellationException("promiseCopyFrom was cancelled.")
29+
var totalBytes = 0
2930
var bytes = inputStream.read(buffer)
3031
while (bytes >= 0) {
3132
if (ct.isCancelled) throw CancellationException("promiseCopyFrom was cancelled.")
3233
outputStream.write(buffer, 0, bytes)
3334
if (ct.isCancelled) throw CancellationException("promiseCopyFrom was cancelled.")
35+
totalBytes += bytes
3436
bytes = inputStream.read(buffer)
3537
}
36-
this
38+
totalBytes
3739
}
3840
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.lasthopesoftware.bluewater.client.stored.library.items.files.job.GivenAFileThatDoesNotYetExist.AndTheFileCanBeDownloaded.AndNoBytesAreDownloaded
2+
3+
import com.lasthopesoftware.bluewater.client.browsing.files.ServiceFile
4+
import com.lasthopesoftware.bluewater.client.browsing.library.repository.LibraryId
5+
import com.lasthopesoftware.bluewater.client.stored.library.items.files.job.StoredFileJob
6+
import com.lasthopesoftware.bluewater.client.stored.library.items.files.job.StoredFileJobProcessor
7+
import com.lasthopesoftware.bluewater.client.stored.library.items.files.job.StoredFileJobState
8+
import com.lasthopesoftware.bluewater.client.stored.library.items.files.repository.StoredFile
9+
import com.lasthopesoftware.promises.extensions.toPromise
10+
import com.lasthopesoftware.resources.emptyByteArray
11+
import com.namehillsoftware.handoff.promises.Promise
12+
import io.mockk.every
13+
import io.mockk.mockk
14+
import org.assertj.core.api.Assertions.assertThat
15+
import org.junit.jupiter.api.BeforeAll
16+
import org.junit.jupiter.api.Test
17+
import java.io.ByteArrayInputStream
18+
import java.io.OutputStream
19+
import java.net.URI
20+
21+
class WhenProcessingTheJob {
22+
23+
private val storedFile = StoredFile(LibraryId(5), ServiceFile("1"), URI("test-path"), true)
24+
private val states = ArrayList<StoredFileJobState>()
25+
26+
@BeforeAll
27+
fun before() {
28+
val storedFileJobProcessor = StoredFileJobProcessor(
29+
mockk {
30+
every { promiseOutputStream(any()) } returns Promise(OutputStream.nullOutputStream())
31+
},
32+
mockk {
33+
every { promiseDownload(any(), any()) } returns Promise(
34+
ByteArrayInputStream(emptyByteArray)
35+
)
36+
},
37+
mockk {
38+
every { markStoredFileAsDownloaded(storedFile) } returns storedFile.toPromise()
39+
},
40+
)
41+
storedFileJobProcessor.observeStoredFileDownload(
42+
setOf(
43+
StoredFileJob(
44+
LibraryId(5),
45+
ServiceFile("1"),
46+
storedFile
47+
)
48+
)
49+
)
50+
.map { f -> f.storedFileJobState }
51+
.blockingSubscribe(
52+
{ storedFileJobState -> states.add(storedFileJobState) },
53+
)
54+
}
55+
56+
@Test
57+
fun thenTheStoredFileIsPutBackIntoQueuedState() {
58+
assertThat(states).containsExactly(
59+
StoredFileJobState.Queued,
60+
StoredFileJobState.Downloading,
61+
StoredFileJobState.Queued
62+
)
63+
}
64+
}

projectBlueWater/src/test/java/com/lasthopesoftware/bluewater/client/stored/library/items/files/job/GivenAFileThatDoesNotYetExist/AndTheFileCanBeDownloaded/AndTheDownloadFails/WhenProcessingTheJob.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.lasthopesoftware.bluewater.client.stored.library.items.files.job.GivenAFileThatDoesNotYetExist.AndTheFileCanBeDownloaded.AndTheDownloadFails
22

3-
import android.os.Build
4-
import androidx.annotation.RequiresApi
53
import com.lasthopesoftware.bluewater.client.browsing.files.ServiceFile
64
import com.lasthopesoftware.bluewater.client.browsing.library.repository.LibraryId
75
import com.lasthopesoftware.bluewater.client.stored.library.items.files.job.StoredFileJob
@@ -25,7 +23,6 @@ class WhenProcessingTheJob {
2523
private val storedFile = StoredFile(LibraryId(5), ServiceFile("1"), URI("test-path"), true)
2624
private val states = ArrayList<StoredFileJobState>()
2725

28-
@RequiresApi(api = Build.VERSION_CODES.N)
2926
@BeforeAll
3027
fun before() {
3128
val storedFileJobProcessor = StoredFileJobProcessor(

projectBlueWater/src/test/java/com/lasthopesoftware/bluewater/client/stored/library/items/files/job/GivenAFileThatDoesNotYetExist/AndTheFileCanBeDownloaded/AndTheSubsriptionIsDisposedAfterAResponseIsReceived/WhenProcessingTheJob.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import java.net.URI
2222
class WhenProcessingTheJob {
2323
private val storedFile = StoredFile(LibraryId(15), ServiceFile("1"), URI("test-path"), true)
2424
private val updateStoredFiles = mockk<UpdateStoredFiles> {
25-
every { markStoredFileAsDownloaded(any()) } answers { Promise(firstArg<StoredFile>()) }
25+
every { markStoredFileAsDownloaded(any()) } answers { Promise(firstArg<StoredFile>().setIsDownloadComplete(true)) }
2626
}
2727
private var states: List<StoredFileJobState>? = null
2828

@@ -32,7 +32,7 @@ class WhenProcessingTheJob {
3232
mockk {
3333
every { promiseOutputStream(any()) } returns ByteArrayOutputStream().toPromise()
3434
},
35-
mockk { every { promiseDownload(any(), any()) } returns Promise(ByteArrayInputStream(ByteArray(0))) },
35+
mockk { every { promiseDownload(any(), any()) } returns Promise(ByteArrayInputStream(byteArrayOf(120, (573 % 128).toByte()))) },
3636
updateStoredFiles,
3737
)
3838
states = storedFileJobProcessor.observeStoredFileDownload(

projectBlueWater/src/test/java/com/lasthopesoftware/bluewater/client/stored/library/items/files/job/GivenAFileThatDoesNotYetExist/AndTheFileCanBeDownloaded/AndTheSubsriptionIsDisposedAfterItIsDownloaded/WhenProcessingTheJob.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,28 @@ import io.reactivex.rxjava3.disposables.Disposable
1818
import org.assertj.core.api.Assertions.assertThat
1919
import org.junit.jupiter.api.BeforeAll
2020
import org.junit.jupiter.api.Test
21-
import java.io.ByteArrayInputStream
2221
import java.io.ByteArrayOutputStream
2322
import java.net.URI
2423

2524
class WhenProcessingTheJob {
2625
private val storedFile = StoredFile(LibraryId(13), ServiceFile("1"), URI("test-path"), true)
2726
private val updateStoredFiles = mockk<UpdateStoredFiles> {
28-
every { markStoredFileAsDownloaded(any()) } answers { Promise(firstArg<StoredFile>()) }
27+
every { markStoredFileAsDownloaded(any()) } answers { Promise(firstArg<StoredFile>().setIsDownloadComplete(true)) }
2928
}
3029
private val states: MutableList<StoredFileJobState> = ArrayList()
3130

31+
@OptIn(ExperimentalStdlibApi::class)
3232
@BeforeAll
3333
fun before() {
3434
val storedFileJobProcessor = StoredFileJobProcessor(
3535
mockk {
3636
every { promiseOutputStream(any()) } returns ByteArrayOutputStream().toPromise()
3737
},
38-
mockk { every { promiseDownload(any(), any()) } returns Promise(ByteArrayInputStream(ByteArray(0))) },
38+
mockk {
39+
every { promiseDownload(any(), any()) } answers {
40+
"590d88fbd4994ee0a7d03ed155e6c969".hexToByteArray().inputStream().toPromise()
41+
}
42+
},
3943
updateStoredFiles,
4044
)
4145
storedFileJobProcessor

projectBlueWater/src/test/java/com/lasthopesoftware/bluewater/client/stored/library/items/files/job/GivenAFileThatDoesNotYetExist/AndTheFileCanBeDownloaded/WhenProcessingTheJob.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import java.net.URI
2222
class WhenProcessingTheJob {
2323
private val storedFile = StoredFile(LibraryId(5), ServiceFile("1"), URI("test-path"), true)
2424
private val storedFileAccess = mockk<UpdateStoredFiles> {
25-
every { markStoredFileAsDownloaded(any()) } answers { Promise(firstArg<StoredFile>()) }
25+
every { markStoredFileAsDownloaded(any()) } answers { Promise(firstArg<StoredFile>().setIsDownloadComplete(true)) }
2626
}
2727
private var states: List<StoredFileJobState>? = null
2828

@@ -32,7 +32,7 @@ class WhenProcessingTheJob {
3232
mockk {
3333
every { promiseOutputStream(any()) } returns ByteArrayOutputStream().toPromise()
3434
},
35-
mockk { every { promiseDownload(any(), any()) } returns Promise(ByteArrayInputStream(ByteArray(0))) },
35+
mockk { every { promiseDownload(any(), any()) } returns Promise(ByteArrayInputStream(byteArrayOf(907.toByte(), 403.toByte()))) },
3636
storedFileAccess,
3737
)
3838
states = storedFileJobProcessor.observeStoredFileDownload(

projectBlueWater/src/test/java/com/lasthopesoftware/bluewater/client/stored/library/items/files/job/GivenAQueueOfStoredFileJobs/AndAnUnexpectedConnectionErrorOccurs/WhenProcessingTheQueue.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ class WhenProcessingTheQueue {
7575
every { promiseOutputStream(match { !it.isDownloadComplete }) } returns ByteArrayOutputStream().toPromise()
7676
},
7777
mockk {
78-
every { promiseDownload(any(), any()) } returns Promise(ByteArrayInputStream(ByteArray(0)))
78+
every { promiseDownload(any(), any()) } answers {
79+
ByteArrayInputStream(byteArrayOf(663.toByte(), 40)).toPromise()
80+
}
7981
every { promiseDownload(any(), match { it.serviceId == "4" }) } returns Promise(UnexpectedException())
8082
},
8183
storedFilesUpdater,

projectBlueWater/src/test/java/com/lasthopesoftware/bluewater/client/stored/library/items/files/job/GivenAQueueOfStoredFileJobs/AndConnectingFaults/WhenProcessingTheQueue.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import io.mockk.mockk
1515
import org.assertj.core.api.Assertions.assertThat
1616
import org.junit.jupiter.api.BeforeAll
1717
import org.junit.jupiter.api.Test
18-
import java.io.ByteArrayInputStream
1918
import java.io.ByteArrayOutputStream
2019
import java.io.IOException
2120

@@ -76,7 +75,9 @@ class WhenProcessingTheQueue {
7675
every { promiseOutputStream(any()) } returns ByteArrayOutputStream().toPromise()
7776
},
7877
mockk {
79-
every { promiseDownload(any(), any()) } returns Promise(ByteArrayInputStream(ByteArray(0)))
78+
every { promiseDownload(any(), any()) } answers {
79+
byteArrayOf((327 % 128).toByte(), (955 % 128).toByte()).inputStream().toPromise()
80+
}
8081
every { promiseDownload(any(), match { it.serviceId == "2" }) } returns Promise(IOException())
8182
},
8283
storedFilesUpdater,

projectBlueWater/src/test/java/com/lasthopesoftware/bluewater/client/stored/library/items/files/job/GivenAQueueOfStoredFileJobs/AndObservingTwice/WhenProcessingTheQueue.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ import com.lasthopesoftware.bluewater.client.stored.library.items.files.job.Stor
99
import com.lasthopesoftware.bluewater.client.stored.library.items.files.job.StoredFileJobStatus
1010
import com.lasthopesoftware.bluewater.client.stored.library.items.files.repository.StoredFile
1111
import com.lasthopesoftware.promises.extensions.toPromise
12-
import com.namehillsoftware.handoff.promises.Promise
1312
import io.mockk.every
1413
import io.mockk.mockk
1514
import org.assertj.core.api.Assertions.assertThat
1615
import org.junit.jupiter.api.BeforeAll
1716
import org.junit.jupiter.api.Test
18-
import java.io.ByteArrayInputStream
1917
import java.io.ByteArrayOutputStream
2018

2119
class WhenProcessingTheQueue {
@@ -77,7 +75,7 @@ class WhenProcessingTheQueue {
7775
},
7876
mockk {
7977
every { promiseDownload(any(), any()) } answers {
80-
Promise(ByteArrayInputStream(ByteArray(0)))
78+
byteArrayOf(200.toByte(), 289.toByte()).inputStream().toPromise()
8179
}
8280
},
8381
storedFilesUpdater,

0 commit comments

Comments
 (0)