@onekeyfe/react-native-range-downloader 3.0.81-alpha.1 → 3.0.81-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactStore.kt +59 -127
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/ReactNativeRangeDownloader.kt +1 -9
- package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDeadlineTest.kt +26 -0
- package/ios/FirmwareArtifactStore.swift +68 -146
- package/ios/ReactNativeRangeDownloader.swift +15 -29
- package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts +0 -4
- package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.cpp +0 -19
- package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.hpp +0 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/HybridReactNativeRangeDownloaderSpec.kt +0 -4
- package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Umbrella.hpp +0 -3
- package/nitrogen/generated/ios/c++/HybridReactNativeRangeDownloaderSpecSwift.hpp +0 -11
- package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec.swift +0 -1
- package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec_cxx.swift +0 -19
- package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.cpp +0 -1
- package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.hpp +0 -4
- package/package.json +2 -2
- package/src/ReactNativeRangeDownloader.nitro.ts +1 -8
- package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseReconcileParams.hpp +0 -76
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseReconcileParams.kt +0 -38
- package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseReconcileParams.swift +0 -48
- package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseReconcileParams.hpp +0 -76
package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactStore.kt
CHANGED
|
@@ -6,7 +6,6 @@ import com.sniconnect.SniPinnedTransport
|
|
|
6
6
|
import java.io.BufferedInputStream
|
|
7
7
|
import java.io.File
|
|
8
8
|
import java.io.FileInputStream
|
|
9
|
-
import java.io.FileOutputStream
|
|
10
9
|
import java.io.IOException
|
|
11
10
|
import java.io.RandomAccessFile
|
|
12
11
|
import java.security.MessageDigest
|
|
@@ -14,14 +13,29 @@ import java.util.UUID
|
|
|
14
13
|
import java.util.concurrent.ConcurrentHashMap
|
|
15
14
|
import java.util.concurrent.TimeUnit
|
|
16
15
|
import java.util.zip.ZipInputStream
|
|
16
|
+
import javax.net.ssl.SSLException
|
|
17
|
+
import kotlin.math.ceil
|
|
17
18
|
import okhttp3.Call
|
|
18
19
|
import okhttp3.OkHttpClient
|
|
19
20
|
import okhttp3.Protocol
|
|
20
21
|
import okhttp3.Request
|
|
21
22
|
import okhttp3.Response
|
|
22
23
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
private const val DEFAULT_FIRMWARE_DOWNLOAD_DEADLINE_SECONDS = 180.0
|
|
26
|
+
private const val MAX_FIRMWARE_DOWNLOAD_DEADLINE_SECONDS = 24.0 * 60 * 60
|
|
27
|
+
|
|
28
|
+
internal fun validateFirmwareDownloadDeadlineSeconds(value: Double?): Double {
|
|
29
|
+
val deadline = value ?: DEFAULT_FIRMWARE_DOWNLOAD_DEADLINE_SECONDS
|
|
30
|
+
require(
|
|
31
|
+
deadline.isFinite() &&
|
|
32
|
+
deadline > 0 &&
|
|
33
|
+
deadline <= MAX_FIRMWARE_DOWNLOAD_DEADLINE_SECONDS
|
|
34
|
+
) {
|
|
35
|
+
"Invalid firmware download deadline"
|
|
36
|
+
}
|
|
37
|
+
return deadline
|
|
38
|
+
}
|
|
25
39
|
|
|
26
40
|
internal data class StoredFirmwareArtifact(
|
|
27
41
|
val artifactRef: String,
|
|
@@ -43,23 +57,26 @@ private data class StagedFirmwareArchiveEntry(
|
|
|
43
57
|
)
|
|
44
58
|
|
|
45
59
|
private data class FirmwareDownloadKey(
|
|
46
|
-
val transactionId: String,
|
|
47
60
|
val expectedSha256: String,
|
|
48
61
|
)
|
|
49
62
|
|
|
63
|
+
private class FirmwareDownloadLock {
|
|
64
|
+
val monitor = Any()
|
|
65
|
+
var references = 0
|
|
66
|
+
}
|
|
67
|
+
|
|
50
68
|
internal object FirmwareArtifactStore {
|
|
51
69
|
const val MAX_READ_BYTES = 256 * 1024
|
|
52
70
|
|
|
53
71
|
private const val MAX_ARTIFACT_BYTES = 512L * 1024 * 1024
|
|
54
|
-
private const val MAX_LEASE_METADATA_BYTES = 1024L * 1024
|
|
55
|
-
private const val MAX_TOTAL_LEASE_REFS = 8192
|
|
56
72
|
private const val FINAL_ARTIFACT_GRACE_MS = 24L * 60 * 60 * 1000
|
|
57
73
|
private const val PARTIAL_ARTIFACT_GRACE_MS = 7L * 24 * 60 * 60 * 1000
|
|
58
74
|
private val sha256Pattern = Regex("^[a-fA-F0-9]{64}$")
|
|
59
75
|
private val artifactRefPattern = Regex("^fw:[a-f0-9]{64}$")
|
|
60
76
|
private val leaseRefPattern = Regex("^fwlease:[a-f0-9-]{36}$")
|
|
61
77
|
private val identifierPattern = Regex("^[A-Za-z0-9._:-]{1,160}$")
|
|
62
|
-
private val downloadLocks =
|
|
78
|
+
private val downloadLocks =
|
|
79
|
+
ConcurrentHashMap<FirmwareDownloadKey, FirmwareDownloadLock>()
|
|
63
80
|
private val activeCalls =
|
|
64
81
|
ConcurrentHashMap<String, MutableSet<Call>>()
|
|
65
82
|
private val cancelledTransactions =
|
|
@@ -69,6 +86,7 @@ internal object FirmwareArtifactStore {
|
|
|
69
86
|
private val leaseLock = Any()
|
|
70
87
|
private val readerLock = Any()
|
|
71
88
|
private val readers = mutableMapOf<String, OpenReader>()
|
|
89
|
+
private val leases = mutableMapOf<String, LeaseState>()
|
|
72
90
|
|
|
73
91
|
private data class LeaseState(
|
|
74
92
|
val transactionId: String,
|
|
@@ -102,14 +120,15 @@ internal object FirmwareArtifactStore {
|
|
|
102
120
|
transactionId = params.transactionId,
|
|
103
121
|
artifactRef = "fw:${validated.expectedSha256}",
|
|
104
122
|
)
|
|
105
|
-
val lockKey = FirmwareDownloadKey(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
123
|
+
val lockKey = FirmwareDownloadKey(validated.expectedSha256)
|
|
124
|
+
val downloadLock = downloadLocks.compute(lockKey) { _, current ->
|
|
125
|
+
(current ?: FirmwareDownloadLock()).also {
|
|
126
|
+
it.references += 1
|
|
127
|
+
}
|
|
128
|
+
} ?: error("Firmware artifact lock is unavailable")
|
|
110
129
|
markDownloadActive(validated.expectedSha256, 1)
|
|
111
130
|
try {
|
|
112
|
-
return synchronized(
|
|
131
|
+
return synchronized(downloadLock.monitor) {
|
|
113
132
|
check(!cancelledTransactions.contains(params.transactionId)) {
|
|
114
133
|
"ARTIFACT_CANCELLED: firmware artifact download was cancelled"
|
|
115
134
|
}
|
|
@@ -117,6 +136,14 @@ internal object FirmwareArtifactStore {
|
|
|
117
136
|
}
|
|
118
137
|
} finally {
|
|
119
138
|
markDownloadActive(validated.expectedSha256, -1)
|
|
139
|
+
downloadLocks.compute(lockKey) { _, current ->
|
|
140
|
+
if (current !== downloadLock) {
|
|
141
|
+
current
|
|
142
|
+
} else {
|
|
143
|
+
current.references -= 1
|
|
144
|
+
current.takeIf { it.references > 0 }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
120
147
|
}
|
|
121
148
|
}
|
|
122
149
|
|
|
@@ -133,7 +160,7 @@ internal object FirmwareArtifactStore {
|
|
|
133
160
|
fun discard(artifactRef: String) {
|
|
134
161
|
val file = resolveArtifactFile(artifactRef)
|
|
135
162
|
synchronized(leaseLock) {
|
|
136
|
-
require(
|
|
163
|
+
require(leases.values.none { artifactRef in it.artifactRefs }) {
|
|
137
164
|
"ARTIFACT_LEASED: firmware artifact is retained"
|
|
138
165
|
}
|
|
139
166
|
}
|
|
@@ -284,6 +311,7 @@ internal object FirmwareArtifactStore {
|
|
|
284
311
|
val maxBytes: Long,
|
|
285
312
|
val expectedSha256: String,
|
|
286
313
|
val hostname: String,
|
|
314
|
+
val overallDeadlineSeconds: Double,
|
|
287
315
|
)
|
|
288
316
|
|
|
289
317
|
private fun validateDownloadParams(
|
|
@@ -324,6 +352,8 @@ internal object FirmwareArtifactStore {
|
|
|
324
352
|
require(sha256Pattern.matches(params.expectedSha256)) {
|
|
325
353
|
"Invalid firmware artifact SHA-256"
|
|
326
354
|
}
|
|
355
|
+
val overallDeadlineSeconds =
|
|
356
|
+
validateFirmwareDownloadDeadlineSeconds(params.overallDeadlineSeconds)
|
|
327
357
|
require(params.routeType == "domain" || params.routeType == "pinnedIp") {
|
|
328
358
|
"Invalid firmware route type"
|
|
329
359
|
}
|
|
@@ -341,6 +371,7 @@ internal object FirmwareArtifactStore {
|
|
|
341
371
|
maxBytes = maxBytes,
|
|
342
372
|
expectedSha256 = params.expectedSha256.lowercase(),
|
|
343
373
|
hostname = url.host,
|
|
374
|
+
overallDeadlineSeconds = overallDeadlineSeconds,
|
|
344
375
|
)
|
|
345
376
|
}
|
|
346
377
|
|
|
@@ -400,12 +431,10 @@ internal object FirmwareArtifactStore {
|
|
|
400
431
|
.build()
|
|
401
432
|
}
|
|
402
433
|
val call = client.newCall(requestBuilder.build())
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
call.timeout().timeout(deadline.toLong().coerceAtLeast(1), TimeUnit.SECONDS)
|
|
408
|
-
}
|
|
434
|
+
call.timeout().timeout(
|
|
435
|
+
ceil(validated.overallDeadlineSeconds * 1000).toLong(),
|
|
436
|
+
TimeUnit.MILLISECONDS,
|
|
437
|
+
)
|
|
409
438
|
|
|
410
439
|
registerCall(params.transactionId, call)
|
|
411
440
|
try {
|
|
@@ -428,6 +457,12 @@ internal object FirmwareArtifactStore {
|
|
|
428
457
|
error,
|
|
429
458
|
)
|
|
430
459
|
}
|
|
460
|
+
if (generateSequence<Throwable>(error) { it.cause }.any { it is SSLException }) {
|
|
461
|
+
throw IllegalStateException(
|
|
462
|
+
"ARTIFACT_TLS_FAILED: firmware TLS validation failed",
|
|
463
|
+
error,
|
|
464
|
+
)
|
|
465
|
+
}
|
|
431
466
|
throw IllegalStateException(
|
|
432
467
|
"ARTIFACT_NETWORK_FAILED: firmware request failed",
|
|
433
468
|
error,
|
|
@@ -563,13 +598,11 @@ internal object FirmwareArtifactStore {
|
|
|
563
598
|
"Invalid firmware transactionId"
|
|
564
599
|
}
|
|
565
600
|
return synchronized(leaseLock) {
|
|
566
|
-
val leases = loadLeasesLocked()
|
|
567
601
|
require(leases.size < 32) {
|
|
568
602
|
"Too many firmware artifact leases"
|
|
569
603
|
}
|
|
570
604
|
val leaseRef = "fwlease:${UUID.randomUUID()}"
|
|
571
605
|
leases[leaseRef] = LeaseState(transactionId, mutableSetOf())
|
|
572
|
-
saveLeasesLocked(leases)
|
|
573
606
|
leaseRef
|
|
574
607
|
}
|
|
575
608
|
}
|
|
@@ -592,41 +625,18 @@ internal object FirmwareArtifactStore {
|
|
|
592
625
|
"Invalid firmware lease disposition"
|
|
593
626
|
}
|
|
594
627
|
val transactionId = synchronized(leaseLock) {
|
|
595
|
-
val leases = loadLeasesLocked()
|
|
596
628
|
val removed = leases.remove(validateLeaseRef(leaseRef))
|
|
597
629
|
require(removed != null) {
|
|
598
630
|
"Firmware artifact lease is unavailable"
|
|
599
631
|
}
|
|
600
|
-
saveLeasesLocked(leases)
|
|
601
632
|
removed.transactionId
|
|
602
633
|
}
|
|
603
634
|
cancelledTransactions.remove(transactionId)
|
|
604
|
-
downloadLocks.keys.removeIf { it.transactionId == transactionId }
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
fun reconcileLeases(activeLeaseRefs: Array<String>) {
|
|
608
|
-
require(activeLeaseRefs.size <= 32) {
|
|
609
|
-
"Too many active firmware artifact leases"
|
|
610
|
-
}
|
|
611
|
-
val active = activeLeaseRefs.mapTo(mutableSetOf()) {
|
|
612
|
-
validateLeaseRef(it)
|
|
613
|
-
}
|
|
614
|
-
require(active.size == activeLeaseRefs.size) {
|
|
615
|
-
"Duplicate active firmware artifact lease"
|
|
616
|
-
}
|
|
617
|
-
synchronized(leaseLock) {
|
|
618
|
-
val leases = loadLeasesLocked()
|
|
619
|
-
require(active.all { leases.containsKey(it) }) {
|
|
620
|
-
"Firmware artifact lease reconciliation is incomplete"
|
|
621
|
-
}
|
|
622
|
-
leases.keys.retainAll(active)
|
|
623
|
-
saveLeasesLocked(leases)
|
|
624
|
-
}
|
|
625
635
|
}
|
|
626
636
|
|
|
627
637
|
fun sweepOrphans(): Pair<Int, Long> {
|
|
628
638
|
val retainedSha256 = synchronized(leaseLock) {
|
|
629
|
-
|
|
639
|
+
leases.values
|
|
630
640
|
.flatMap { it.artifactRefs }
|
|
631
641
|
.mapTo(mutableSetOf()) { it.removePrefix("fw:") }
|
|
632
642
|
}
|
|
@@ -640,7 +650,7 @@ internal object FirmwareArtifactStore {
|
|
|
640
650
|
var deletedFiles = 0
|
|
641
651
|
var deletedBytes = 0L
|
|
642
652
|
root.listFiles()?.forEach { file ->
|
|
643
|
-
if (!file.isFile
|
|
653
|
+
if (!file.isFile) return@forEach
|
|
644
654
|
val sha256 = file.name.take(64)
|
|
645
655
|
if (
|
|
646
656
|
!sha256Pattern.matches(sha256) ||
|
|
@@ -669,7 +679,7 @@ internal object FirmwareArtifactStore {
|
|
|
669
679
|
|
|
670
680
|
private fun requireLease(leaseRef: String) {
|
|
671
681
|
synchronized(leaseLock) {
|
|
672
|
-
require(
|
|
682
|
+
require(leases.containsKey(validateLeaseRef(leaseRef))) {
|
|
673
683
|
"Firmware artifact lease is unavailable"
|
|
674
684
|
}
|
|
675
685
|
}
|
|
@@ -684,7 +694,6 @@ internal object FirmwareArtifactStore {
|
|
|
684
694
|
"Invalid firmware artifactRef"
|
|
685
695
|
}
|
|
686
696
|
synchronized(leaseLock) {
|
|
687
|
-
val leases = loadLeasesLocked()
|
|
688
697
|
val lease = leases[validateLeaseRef(leaseRef)]
|
|
689
698
|
?: error("Firmware artifact lease is unavailable")
|
|
690
699
|
if (transactionId != null) {
|
|
@@ -692,9 +701,7 @@ internal object FirmwareArtifactStore {
|
|
|
692
701
|
"Firmware artifact lease transaction mismatch"
|
|
693
702
|
}
|
|
694
703
|
}
|
|
695
|
-
|
|
696
|
-
saveLeasesLocked(leases)
|
|
697
|
-
}
|
|
704
|
+
lease.artifactRefs.add(artifactRef)
|
|
698
705
|
}
|
|
699
706
|
}
|
|
700
707
|
|
|
@@ -705,81 +712,6 @@ internal object FirmwareArtifactStore {
|
|
|
705
712
|
return leaseRef
|
|
706
713
|
}
|
|
707
714
|
|
|
708
|
-
private fun loadLeasesLocked(): MutableMap<String, LeaseState> {
|
|
709
|
-
val file = File(root, "leases.json")
|
|
710
|
-
if (!file.exists()) return mutableMapOf()
|
|
711
|
-
require(file.length() in 1..MAX_LEASE_METADATA_BYTES) {
|
|
712
|
-
"Firmware lease metadata is too large"
|
|
713
|
-
}
|
|
714
|
-
val envelope = JSONObject(file.readText(Charsets.UTF_8))
|
|
715
|
-
require(envelope.optInt("schemaVersion") == 1) {
|
|
716
|
-
"Unsupported firmware lease schema"
|
|
717
|
-
}
|
|
718
|
-
val result = mutableMapOf<String, LeaseState>()
|
|
719
|
-
val jsonLeases = envelope.getJSONObject("leases")
|
|
720
|
-
val keys = jsonLeases.keys()
|
|
721
|
-
while (keys.hasNext()) {
|
|
722
|
-
val leaseRef = validateLeaseRef(keys.next())
|
|
723
|
-
val jsonLease = jsonLeases.getJSONObject(leaseRef)
|
|
724
|
-
val transactionId = jsonLease.getString("transactionId")
|
|
725
|
-
require(identifierPattern.matches(transactionId)) {
|
|
726
|
-
"Invalid persisted firmware transactionId"
|
|
727
|
-
}
|
|
728
|
-
val jsonRefs = jsonLease.getJSONArray("artifactRefs")
|
|
729
|
-
require(jsonRefs.length() <= 4096) {
|
|
730
|
-
"Too many persisted firmware artifact refs"
|
|
731
|
-
}
|
|
732
|
-
val refs = mutableSetOf<String>()
|
|
733
|
-
for (index in 0 until jsonRefs.length()) {
|
|
734
|
-
val artifactRef = jsonRefs.getString(index)
|
|
735
|
-
require(artifactRefPattern.matches(artifactRef) && refs.add(artifactRef)) {
|
|
736
|
-
"Invalid persisted firmware artifact ref"
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
result[leaseRef] = LeaseState(transactionId, refs)
|
|
740
|
-
}
|
|
741
|
-
require(result.size <= 32) {
|
|
742
|
-
"Too many persisted firmware artifact leases"
|
|
743
|
-
}
|
|
744
|
-
require(result.values.sumOf { it.artifactRefs.size } <= MAX_TOTAL_LEASE_REFS) {
|
|
745
|
-
"Too many persisted firmware artifact refs"
|
|
746
|
-
}
|
|
747
|
-
return result
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
private fun saveLeasesLocked(leases: Map<String, LeaseState>) {
|
|
751
|
-
require(
|
|
752
|
-
leases.size <= 32 &&
|
|
753
|
-
leases.values.sumOf { it.artifactRefs.size } <= MAX_TOTAL_LEASE_REFS
|
|
754
|
-
) {
|
|
755
|
-
"Firmware lease metadata is too large"
|
|
756
|
-
}
|
|
757
|
-
val jsonLeases = JSONObject()
|
|
758
|
-
leases.toSortedMap().forEach { (leaseRef, lease) ->
|
|
759
|
-
jsonLeases.put(
|
|
760
|
-
leaseRef,
|
|
761
|
-
JSONObject()
|
|
762
|
-
.put("transactionId", lease.transactionId)
|
|
763
|
-
.put("artifactRefs", JSONArray(lease.artifactRefs.sorted())),
|
|
764
|
-
)
|
|
765
|
-
}
|
|
766
|
-
val bytes = JSONObject()
|
|
767
|
-
.put("schemaVersion", 1)
|
|
768
|
-
.put("leases", jsonLeases)
|
|
769
|
-
.toString()
|
|
770
|
-
.toByteArray(Charsets.UTF_8)
|
|
771
|
-
require(bytes.size.toLong() <= MAX_LEASE_METADATA_BYTES) {
|
|
772
|
-
"Firmware lease metadata is too large"
|
|
773
|
-
}
|
|
774
|
-
val destination = File(root, "leases.json")
|
|
775
|
-
val temporary = File(root, ".leases-${UUID.randomUUID()}.tmp")
|
|
776
|
-
FileOutputStream(temporary).use { output ->
|
|
777
|
-
output.write(bytes)
|
|
778
|
-
output.fd.sync()
|
|
779
|
-
}
|
|
780
|
-
Os.rename(temporary.absolutePath, destination.absolutePath)
|
|
781
|
-
}
|
|
782
|
-
|
|
783
715
|
private fun markDownloadActive(sha256: String, delta: Int) {
|
|
784
716
|
synchronized(activeDownloadLock) {
|
|
785
717
|
val count = (activeDownloadCounts[sha256] ?: 0) + delta
|
|
@@ -268,7 +268,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
268
268
|
|
|
269
269
|
override fun getFirmwareArtifactCapabilities(): FirmwareArtifactCapabilities {
|
|
270
270
|
return FirmwareArtifactCapabilities(
|
|
271
|
-
firmwareArtifactProtocolVersion =
|
|
271
|
+
firmwareArtifactProtocolVersion = 2.0,
|
|
272
272
|
supportedRouteTypes = arrayOf("domain", "pinnedIp"),
|
|
273
273
|
supportsArchiveMaterialization = true,
|
|
274
274
|
maxReadBytes = FirmwareArtifactStore.MAX_READ_BYTES.toDouble(),
|
|
@@ -395,14 +395,6 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
|
|
|
395
395
|
}
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
-
override fun reconcileFirmwareArtifactLeases(
|
|
399
|
-
params: FirmwareArtifactLeaseReconcileParams,
|
|
400
|
-
): Promise<Unit> {
|
|
401
|
-
return Promise.async {
|
|
402
|
-
FirmwareArtifactStore.reconcileLeases(params.activeLeaseRefs)
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
398
|
override fun sweepFirmwareArtifactOrphans(): Promise<FirmwareArtifactSweepResult> {
|
|
407
399
|
return Promise.async {
|
|
408
400
|
val (deletedFiles, deletedBytes) = FirmwareArtifactStore.sweepOrphans()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package com.margelo.nitro.reactnativerangedownloader
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.assertEquals
|
|
4
|
+
import org.junit.Assert.assertThrows
|
|
5
|
+
import org.junit.Test
|
|
6
|
+
|
|
7
|
+
class FirmwareArtifactDeadlineTest {
|
|
8
|
+
@Test
|
|
9
|
+
fun defaultsToABoundedThreeMinuteDeadline() {
|
|
10
|
+
assertEquals(180.0, validateFirmwareDownloadDeadlineSeconds(null), 0.0)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@Test
|
|
14
|
+
fun preservesFractionalDeadlines() {
|
|
15
|
+
assertEquals(2.75, validateFirmwareDownloadDeadlineSeconds(2.75), 0.0)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Test
|
|
19
|
+
fun rejectsUnboundedOrInvalidDeadlines() {
|
|
20
|
+
listOf(0.0, -1.0, Double.NaN, Double.POSITIVE_INFINITY, 86_400.1).forEach {
|
|
21
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
22
|
+
validateFirmwareDownloadDeadlineSeconds(it)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|