@hot-updater/react-native 1.0.0-rc.17 → 1.0.0-rc.21
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/lib/commonjs/checkForUpdate.js +7 -9
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/clientPlugin.js +7 -43
- package/lib/commonjs/clientPlugin.js.map +1 -1
- package/lib/commonjs/httpClient.js +7 -9
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/index.js +10 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native.js +4 -4
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/pluginHost.js +26 -190
- package/lib/commonjs/pluginHost.js.map +1 -1
- package/lib/commonjs/releaseCatalogCache.js +7 -59
- package/lib/commonjs/releaseCatalogCache.js.map +1 -1
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/module/checkForUpdate.js +1 -3
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/clientPlugin.js +1 -41
- package/lib/module/clientPlugin.js.map +1 -1
- package/lib/module/httpClient.js +1 -3
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/native.js +1 -1
- package/lib/module/native.js.map +1 -1
- package/lib/module/pluginHost.js +22 -187
- package/lib/module/pluginHost.js.map +1 -1
- package/lib/module/releaseCatalogCache.js +3 -54
- package/lib/module/releaseCatalogCache.js.map +1 -1
- package/lib/module/sdkVersion.js +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/clientPlugin.d.ts +1 -184
- package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -1
- package/lib/typescript/commonjs/httpClient.d.ts +1 -2
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +1 -1
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/pluginHost.d.ts +3 -28
- package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -1
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +1 -13
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/commonjs/types.d.ts +1 -1
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +2 -2
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/clientPlugin.d.ts +1 -184
- package/lib/typescript/module/clientPlugin.d.ts.map +1 -1
- package/lib/typescript/module/httpClient.d.ts +1 -2
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +1 -1
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/pluginHost.d.ts +3 -28
- package/lib/typescript/module/pluginHost.d.ts.map +1 -1
- package/lib/typescript/module/releaseCatalogCache.d.ts +1 -13
- package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/types.d.ts +1 -1
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +2 -2
- package/package.json +7 -16
- package/src/checkForUpdate.ts +4 -6
- package/src/clientPlugin.ts +17 -231
- package/src/httpClient.ts +4 -4
- package/src/index.ts +7 -0
- package/src/native.ts +1 -1
- package/src/pluginHost.ts +24 -241
- package/src/releaseCatalogCache.ts +7 -136
- package/src/sdkVersion.ts +1 -1
- package/src/types.ts +1 -1
- package/src/wrap.types.ts +2 -2
- package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -2236
- package/android/src/test/java/com/hotupdater/HotUpdaterExceptionTest.kt +0 -220
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -264
- package/android/src/test/java/com/hotupdater/HotUpdaterRecoveryStateTest.kt +0 -97
- package/android/src/test/java/com/hotupdater/InstallIdentityServiceTest.kt +0 -71
- package/android/src/test/java/com/hotupdater/KeyValueStorageServiceTest.kt +0 -106
- package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
- package/android/src/test/java/com/hotupdater/NoBackupContext.kt +0 -11
- package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -397
- package/android/src/test/java/com/hotupdater/PendingCrashRecoveryTest.kt +0 -44
- package/android/src/test/java/com/hotupdater/PreviousProcessExitTest.kt +0 -57
- package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
- package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
- package/android/src/test/java/com/hotupdater/TarBrArchiveExtractorTest.kt +0 -206
- package/lib/commonjs/appReady.spec.js +0 -360
- package/lib/commonjs/appReady.spec.js.map +0 -1
- package/lib/commonjs/appReady.test-utils.js +0 -25
- package/lib/commonjs/appReady.test-utils.js.map +0 -1
- package/lib/commonjs/baseURL.js +0 -16
- package/lib/commonjs/baseURL.js.map +0 -1
- package/lib/commonjs/catalogCacheNative.spec.js +0 -31
- package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -936
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
- package/lib/commonjs/entryImportGraph.spec.js +0 -50
- package/lib/commonjs/entryImportGraph.spec.js.map +0 -1
- package/lib/commonjs/httpClient.spec.js +0 -171
- package/lib/commonjs/httpClient.spec.js.map +0 -1
- package/lib/commonjs/index.spec.js +0 -395
- package/lib/commonjs/index.spec.js.map +0 -1
- package/lib/commonjs/iosInterop.spec.js +0 -18
- package/lib/commonjs/iosInterop.spec.js.map +0 -1
- package/lib/commonjs/native.spec.js +0 -910
- package/lib/commonjs/native.spec.js.map +0 -1
- package/lib/commonjs/pluginHost.spec.js +0 -292
- package/lib/commonjs/pluginHost.spec.js.map +0 -1
- package/lib/commonjs/plugins/insights/eventId.js +0 -129
- package/lib/commonjs/plugins/insights/eventId.js.map +0 -1
- package/lib/commonjs/plugins/insights/eventId.spec.js +0 -47
- package/lib/commonjs/plugins/insights/eventId.spec.js.map +0 -1
- package/lib/commonjs/plugins/insights/index.js +0 -272
- package/lib/commonjs/plugins/insights/index.js.map +0 -1
- package/lib/commonjs/plugins/insights/insights.spec.js +0 -640
- package/lib/commonjs/plugins/insights/insights.spec.js.map +0 -1
- package/lib/commonjs/plugins/insights/sender.js +0 -128
- package/lib/commonjs/plugins/insights/sender.js.map +0 -1
- package/lib/commonjs/plugins/insights/sender.spec.js +0 -265
- package/lib/commonjs/plugins/insights/sender.spec.js.map +0 -1
- package/lib/commonjs/plugins/insights/state.js +0 -145
- package/lib/commonjs/plugins/insights/state.js.map +0 -1
- package/lib/commonjs/releaseCatalogCache.spec.js +0 -213
- package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
- package/lib/commonjs/store.notification.spec.js +0 -84
- package/lib/commonjs/store.notification.spec.js.map +0 -1
- package/lib/commonjs/store.spec.js +0 -182
- package/lib/commonjs/store.spec.js.map +0 -1
- package/lib/commonjs/updateError.spec.js +0 -149
- package/lib/commonjs/updateError.spec.js.map +0 -1
- package/lib/commonjs/wrap.progress.spec.js +0 -195
- package/lib/commonjs/wrap.progress.spec.js.map +0 -1
- package/lib/commonjs/wrap.spec.js +0 -232
- package/lib/commonjs/wrap.spec.js.map +0 -1
- package/lib/module/appReady.spec.js +0 -359
- package/lib/module/appReady.spec.js.map +0 -1
- package/lib/module/appReady.test-utils.js +0 -19
- package/lib/module/appReady.test-utils.js.map +0 -1
- package/lib/module/baseURL.js +0 -11
- package/lib/module/baseURL.js.map +0 -1
- package/lib/module/catalogCacheNative.spec.js +0 -31
- package/lib/module/catalogCacheNative.spec.js.map +0 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -935
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
- package/lib/module/entryImportGraph.spec.js +0 -50
- package/lib/module/entryImportGraph.spec.js.map +0 -1
- package/lib/module/httpClient.spec.js +0 -171
- package/lib/module/httpClient.spec.js.map +0 -1
- package/lib/module/index.spec.js +0 -394
- package/lib/module/index.spec.js.map +0 -1
- package/lib/module/iosInterop.spec.js +0 -18
- package/lib/module/iosInterop.spec.js.map +0 -1
- package/lib/module/native.spec.js +0 -909
- package/lib/module/native.spec.js.map +0 -1
- package/lib/module/pluginHost.spec.js +0 -291
- package/lib/module/pluginHost.spec.js.map +0 -1
- package/lib/module/plugins/insights/eventId.js +0 -122
- package/lib/module/plugins/insights/eventId.js.map +0 -1
- package/lib/module/plugins/insights/eventId.spec.js +0 -47
- package/lib/module/plugins/insights/eventId.spec.js.map +0 -1
- package/lib/module/plugins/insights/index.js +0 -268
- package/lib/module/plugins/insights/index.js.map +0 -1
- package/lib/module/plugins/insights/insights.spec.js +0 -639
- package/lib/module/plugins/insights/insights.spec.js.map +0 -1
- package/lib/module/plugins/insights/sender.js +0 -123
- package/lib/module/plugins/insights/sender.js.map +0 -1
- package/lib/module/plugins/insights/sender.spec.js +0 -265
- package/lib/module/plugins/insights/sender.spec.js.map +0 -1
- package/lib/module/plugins/insights/state.js +0 -139
- package/lib/module/plugins/insights/state.js.map +0 -1
- package/lib/module/releaseCatalogCache.spec.js +0 -213
- package/lib/module/releaseCatalogCache.spec.js.map +0 -1
- package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
- package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
- package/lib/module/store.notification.spec.js +0 -85
- package/lib/module/store.notification.spec.js.map +0 -1
- package/lib/module/store.spec.js +0 -181
- package/lib/module/store.spec.js.map +0 -1
- package/lib/module/updateError.spec.js +0 -149
- package/lib/module/updateError.spec.js.map +0 -1
- package/lib/module/wrap.progress.spec.js +0 -196
- package/lib/module/wrap.progress.spec.js.map +0 -1
- package/lib/module/wrap.spec.js +0 -233
- package/lib/module/wrap.spec.js.map +0 -1
- package/lib/typescript/commonjs/appReady.test-utils.d.ts +0 -9
- package/lib/typescript/commonjs/appReady.test-utils.d.ts.map +0 -1
- package/lib/typescript/commonjs/baseURL.d.ts +0 -4
- package/lib/typescript/commonjs/baseURL.d.ts.map +0 -1
- package/lib/typescript/commonjs/plugins/insights/eventId.d.ts +0 -18
- package/lib/typescript/commonjs/plugins/insights/eventId.d.ts.map +0 -1
- package/lib/typescript/commonjs/plugins/insights/index.d.ts +0 -40
- package/lib/typescript/commonjs/plugins/insights/index.d.ts.map +0 -1
- package/lib/typescript/commonjs/plugins/insights/sender.d.ts +0 -74
- package/lib/typescript/commonjs/plugins/insights/sender.d.ts.map +0 -1
- package/lib/typescript/commonjs/plugins/insights/state.d.ts +0 -46
- package/lib/typescript/commonjs/plugins/insights/state.d.ts.map +0 -1
- package/lib/typescript/module/appReady.test-utils.d.ts +0 -9
- package/lib/typescript/module/appReady.test-utils.d.ts.map +0 -1
- package/lib/typescript/module/baseURL.d.ts +0 -4
- package/lib/typescript/module/baseURL.d.ts.map +0 -1
- package/lib/typescript/module/plugins/insights/eventId.d.ts +0 -18
- package/lib/typescript/module/plugins/insights/eventId.d.ts.map +0 -1
- package/lib/typescript/module/plugins/insights/index.d.ts +0 -40
- package/lib/typescript/module/plugins/insights/index.d.ts.map +0 -1
- package/lib/typescript/module/plugins/insights/sender.d.ts +0 -74
- package/lib/typescript/module/plugins/insights/sender.d.ts.map +0 -1
- package/lib/typescript/module/plugins/insights/state.d.ts +0 -46
- package/lib/typescript/module/plugins/insights/state.d.ts.map +0 -1
- package/plugins/insights/package.json +0 -8
- package/src/appReady.spec.ts +0 -393
- package/src/appReady.test-utils.ts +0 -31
- package/src/baseURL.ts +0 -15
- package/src/catalogCacheNative.spec.ts +0 -63
- package/src/checkForUpdate.releaseCatalog.spec.ts +0 -966
- package/src/entryImportGraph.spec.ts +0 -65
- package/src/httpClient.spec.ts +0 -216
- package/src/index.spec.ts +0 -434
- package/src/iosInterop.spec.ts +0 -30
- package/src/native.spec.ts +0 -978
- package/src/pluginHost.spec.ts +0 -297
- package/src/plugins/insights/eventId.spec.ts +0 -60
- package/src/plugins/insights/eventId.ts +0 -171
- package/src/plugins/insights/index.ts +0 -367
- package/src/plugins/insights/insights.spec.ts +0 -732
- package/src/plugins/insights/sender.spec.ts +0 -287
- package/src/plugins/insights/sender.ts +0 -221
- package/src/plugins/insights/state.ts +0 -184
- package/src/releaseCatalogCache.spec.ts +0 -304
- package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
- package/src/store.notification.spec.ts +0 -102
- package/src/store.spec.ts +0 -215
- package/src/updateError.spec.ts +0 -144
- package/src/wrap.progress.spec.ts +0 -232
- package/src/wrap.spec.ts +0 -262
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
package com.hotupdater
|
|
2
|
-
|
|
3
|
-
import org.junit.Assert.assertEquals
|
|
4
|
-
import org.junit.Assert.assertNull
|
|
5
|
-
import org.junit.Assert.assertSame
|
|
6
|
-
import org.junit.Test
|
|
7
|
-
import java.io.IOException
|
|
8
|
-
import java.io.InterruptedIOException
|
|
9
|
-
import java.net.ConnectException
|
|
10
|
-
import java.net.MalformedURLException
|
|
11
|
-
import java.net.NoRouteToHostException
|
|
12
|
-
import java.net.SocketException
|
|
13
|
-
import java.net.SocketTimeoutException
|
|
14
|
-
import java.net.UnknownHostException
|
|
15
|
-
import javax.net.ssl.SSLHandshakeException
|
|
16
|
-
import javax.net.ssl.SSLPeerUnverifiedException
|
|
17
|
-
|
|
18
|
-
class HotUpdaterExceptionTest {
|
|
19
|
-
private val invalidResponse = UpdateFailure.download(UpdateFailureReason.INVALID_RESPONSE)
|
|
20
|
-
private val network = UpdateFailure.download(UpdateFailureReason.NETWORK)
|
|
21
|
-
private val signature = UpdateFailure.download(UpdateFailureReason.SIGNATURE)
|
|
22
|
-
private val storage = UpdateFailure.install(UpdateFailureReason.STORAGE)
|
|
23
|
-
|
|
24
|
-
@Test
|
|
25
|
-
fun `enum values match the JS contract`() {
|
|
26
|
-
assertEquals(listOf("download", "install"), UpdateFailureStage.entries.map { it.value })
|
|
27
|
-
assertEquals(
|
|
28
|
-
listOf(
|
|
29
|
-
"network",
|
|
30
|
-
"http",
|
|
31
|
-
"invalid_response",
|
|
32
|
-
"hash_mismatch",
|
|
33
|
-
"signature",
|
|
34
|
-
"patch",
|
|
35
|
-
"extract",
|
|
36
|
-
"storage",
|
|
37
|
-
"unknown",
|
|
38
|
-
),
|
|
39
|
-
UpdateFailureReason.entries.map { it.value },
|
|
40
|
-
)
|
|
41
|
-
assertEquals(listOf("manifest", "file", "patch", "archive"), UpdateFailureResource.entries.map { it.value })
|
|
42
|
-
assertEquals(
|
|
43
|
-
listOf("timeout", "dns", "tls", "connection", "offline", "cancelled"),
|
|
44
|
-
UpdateFailureTransport.entries.map { it.value },
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
@Test
|
|
49
|
-
fun `userInfo carries each optional key only with its reason`() {
|
|
50
|
-
assertEquals(
|
|
51
|
-
mapOf("stage" to "download", "reason" to "http", "httpStatus" to 404),
|
|
52
|
-
UpdateFailure.http(404).toUserInfo(),
|
|
53
|
-
)
|
|
54
|
-
assertEquals(
|
|
55
|
-
mapOf(
|
|
56
|
-
"stage" to "download",
|
|
57
|
-
"reason" to "http",
|
|
58
|
-
"resource" to "manifest",
|
|
59
|
-
"httpStatus" to 403,
|
|
60
|
-
"originCode" to "AccessDenied",
|
|
61
|
-
),
|
|
62
|
-
UpdateFailure.http(403, originCode = "AccessDenied").copy(resource = UpdateFailureResource.MANIFEST).toUserInfo(),
|
|
63
|
-
)
|
|
64
|
-
assertEquals(mapOf("stage" to "install", "reason" to "storage"), storage.toUserInfo())
|
|
65
|
-
assertEquals(
|
|
66
|
-
mapOf("stage" to "download", "reason" to "network", "resource" to "file", "transport" to "timeout"),
|
|
67
|
-
UpdateFailure(
|
|
68
|
-
UpdateFailureStage.DOWNLOAD,
|
|
69
|
-
UpdateFailureReason.NETWORK,
|
|
70
|
-
httpStatus = 500,
|
|
71
|
-
resource = UpdateFailureResource.FILE,
|
|
72
|
-
transport = UpdateFailureTransport.TIMEOUT,
|
|
73
|
-
originCode = "Ignored",
|
|
74
|
-
).toUserInfo(),
|
|
75
|
-
)
|
|
76
|
-
assertEquals(
|
|
77
|
-
mapOf("stage" to "download", "reason" to "http", "httpStatus" to 502),
|
|
78
|
-
UpdateFailure.http(502).copy(transport = UpdateFailureTransport.DNS).toUserInfo(),
|
|
79
|
-
)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
@Test
|
|
83
|
-
fun `download errors are classified by their cause`() {
|
|
84
|
-
val cases =
|
|
85
|
-
listOf(
|
|
86
|
-
HttpStatusException(403, "Forbidden") to UpdateFailure.http(403),
|
|
87
|
-
HttpStatusException(404, "Not Found", originCode = "NoSuchKey") to UpdateFailure.http(404, originCode = "NoSuchKey"),
|
|
88
|
-
UnknownHostException("updates.example.com") to network.copy(transport = UpdateFailureTransport.DNS),
|
|
89
|
-
SocketTimeoutException("connect timed out") to network.copy(transport = UpdateFailureTransport.TIMEOUT),
|
|
90
|
-
InterruptedIOException("timeout") to network.copy(transport = UpdateFailureTransport.TIMEOUT),
|
|
91
|
-
SSLHandshakeException("Chain validation failed") to network.copy(transport = UpdateFailureTransport.TLS),
|
|
92
|
-
SSLPeerUnverifiedException("Hostname not verified") to network.copy(transport = UpdateFailureTransport.TLS),
|
|
93
|
-
ConnectException("Connection refused") to network.copy(transport = UpdateFailureTransport.CONNECTION),
|
|
94
|
-
NoRouteToHostException("No route to host") to network.copy(transport = UpdateFailureTransport.CONNECTION),
|
|
95
|
-
SocketException("Connection reset") to network.copy(transport = UpdateFailureTransport.CONNECTION),
|
|
96
|
-
IOException("Canceled") to network.copy(transport = UpdateFailureTransport.CANCELLED),
|
|
97
|
-
IOException("unexpected end of stream") to network,
|
|
98
|
-
IncompleteDownloadException(expectedSize = 10, actualSize = 4) to network,
|
|
99
|
-
LocalStorageException(IOException("No space left on device")) to storage,
|
|
100
|
-
MalformedURLException("no protocol: bundle") to invalidResponse,
|
|
101
|
-
IllegalArgumentException("Expected URL scheme 'http' or 'https' but was 'ftp'") to invalidResponse,
|
|
102
|
-
Exception("Response body is null") to UpdateFailure.download(UpdateFailureReason.UNKNOWN),
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
cases.forEach { (cause, failure) ->
|
|
106
|
-
val error = HotUpdaterException.downloadFailed(cause)
|
|
107
|
-
assertEquals("DOWNLOAD_FAILED", error.code)
|
|
108
|
-
assertEquals("$cause", failure, error.failure)
|
|
109
|
-
}
|
|
110
|
-
assertEquals(network, HotUpdaterException.incompleteDownload(expectedSize = 10, actualSize = 4).failure)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
@Test
|
|
114
|
-
fun `a failed host lookup without an active network is offline`() {
|
|
115
|
-
assertEquals(
|
|
116
|
-
network.copy(transport = UpdateFailureTransport.OFFLINE),
|
|
117
|
-
UpdateFailure.ofDownloadError(UnknownHostException("updates.example.com")) { true },
|
|
118
|
-
)
|
|
119
|
-
assertEquals(
|
|
120
|
-
network.copy(transport = UpdateFailureTransport.TIMEOUT),
|
|
121
|
-
UpdateFailure.ofDownloadError(SocketTimeoutException("timeout")) { error("Only a failed host lookup asks") },
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
@Test
|
|
126
|
-
fun `verification errors report hash mismatch storage or signature`() {
|
|
127
|
-
val cases =
|
|
128
|
-
listOf(
|
|
129
|
-
SignatureVerificationException.FileHashMismatch() to UpdateFailure.download(UpdateFailureReason.HASH_MISMATCH),
|
|
130
|
-
SignatureVerificationException.FileReadFailed() to storage,
|
|
131
|
-
SignatureVerificationException.PublicKeyNotConfigured() to signature,
|
|
132
|
-
SignatureVerificationException.InvalidPublicKeyFormat() to signature,
|
|
133
|
-
SignatureVerificationException.MissingFileHash() to signature,
|
|
134
|
-
SignatureVerificationException.InvalidSignatureFormat() to signature,
|
|
135
|
-
SignatureVerificationException.SignatureVerificationFailed() to signature,
|
|
136
|
-
SignatureVerificationException.UnsignedNotAllowed() to signature,
|
|
137
|
-
SignatureVerificationException.SecurityFrameworkError(IllegalStateException("provider")) to signature,
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
cases.forEach { (cause, failure) ->
|
|
141
|
-
val error = HotUpdaterException.signatureVerificationFailed(cause)
|
|
142
|
-
assertEquals("SIGNATURE_VERIFICATION_FAILED", error.code)
|
|
143
|
-
assertEquals("$cause", failure, error.failure)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
@Test
|
|
148
|
-
fun `parameter validation keeps its codes and reports an invalid response`() {
|
|
149
|
-
val cases =
|
|
150
|
-
listOf(
|
|
151
|
-
Triple(HotUpdaterException.missingBundleId(), "MISSING_BUNDLE_ID", "Missing or empty 'bundleId'") to invalidResponse,
|
|
152
|
-
Triple(HotUpdaterException.invalidManifestParams(), "INVALID_MANIFEST", "Manifest URL, hash, and assets are required") to
|
|
153
|
-
invalidResponse.copy(resource = UpdateFailureResource.MANIFEST),
|
|
154
|
-
Triple(
|
|
155
|
-
HotUpdaterException.invalidFileUrl("archiveUrl", "not a url", UpdateFailureResource.ARCHIVE),
|
|
156
|
-
"INVALID_FILE_URL",
|
|
157
|
-
"Invalid 'archiveUrl' provided: not a url",
|
|
158
|
-
) to invalidResponse.copy(resource = UpdateFailureResource.ARCHIVE),
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
cases.forEach { (rejection, failure) ->
|
|
162
|
-
val (error, code, message) = rejection
|
|
163
|
-
assertEquals(code, error.code)
|
|
164
|
-
assertEquals(message, error.message)
|
|
165
|
-
assertEquals(failure, error.failure)
|
|
166
|
-
}
|
|
167
|
-
assertEquals(invalidResponse, HotUpdaterException.invalidBundle().failure)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
@Test
|
|
171
|
-
fun `storage failures report install storage`() {
|
|
172
|
-
val cases =
|
|
173
|
-
listOf(
|
|
174
|
-
HotUpdaterException.directoryCreationFailed() to "DIRECTORY_CREATION_FAILED",
|
|
175
|
-
HotUpdaterException.moveOperationFailed() to "MOVE_OPERATION_FAILED",
|
|
176
|
-
HotUpdaterException.insufficientDiskSpace(required = 10, available = 1) to "INSUFFICIENT_DISK_SPACE",
|
|
177
|
-
HotUpdaterException.storageFailed("Failed to persist bundle metadata") to "UNKNOWN_ERROR",
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
cases.forEach { (error, code) ->
|
|
181
|
-
assertEquals(code, error.code)
|
|
182
|
-
assertEquals(storage, error.failure)
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
@Test
|
|
187
|
-
fun `unexpected errors keep the UNKNOWN_ERROR code and message`() {
|
|
188
|
-
val cause = IllegalStateException("boom")
|
|
189
|
-
val error = HotUpdaterException.unexpected(cause, UpdateFailure.ofUnexpectedError(cause, UpdateFailureStage.INSTALL))
|
|
190
|
-
|
|
191
|
-
assertEquals("UNKNOWN_ERROR", error.code)
|
|
192
|
-
assertEquals("boom", error.message)
|
|
193
|
-
assertSame(cause, error.cause)
|
|
194
|
-
assertEquals(UpdateFailure.install(UpdateFailureReason.UNKNOWN), error.failure)
|
|
195
|
-
assertEquals(storage, UpdateFailure.ofUnexpectedError(IOException("read failed"), UpdateFailureStage.DOWNLOAD))
|
|
196
|
-
assertEquals(invalidResponse, UpdateFailure.ofUnexpectedError(MalformedURLException(), UpdateFailureStage.DOWNLOAD))
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
@Test
|
|
200
|
-
fun `withResource names the resource only when none is named`() {
|
|
201
|
-
val cause = IOException("reset")
|
|
202
|
-
val error = HotUpdaterException.downloadFailed(cause)
|
|
203
|
-
|
|
204
|
-
val named = error.withResource(UpdateFailureResource.FILE)
|
|
205
|
-
assertEquals("DOWNLOAD_FAILED", named.code)
|
|
206
|
-
assertEquals(error.message, named.message)
|
|
207
|
-
assertSame(cause, named.cause)
|
|
208
|
-
assertEquals(error.failure?.copy(resource = UpdateFailureResource.FILE), named.failure)
|
|
209
|
-
|
|
210
|
-
assertSame(named, named.withResource(UpdateFailureResource.MANIFEST))
|
|
211
|
-
assertSame(error, error.withResource(null))
|
|
212
|
-
val crashed = HotUpdaterException.bundleInCrashedHistory("crashed-bundle")
|
|
213
|
-
assertSame(crashed, crashed.withResource(UpdateFailureResource.FILE))
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
@Test
|
|
217
|
-
fun `a bundle in the crashed history is not an update failure`() {
|
|
218
|
-
assertNull(HotUpdaterException.bundleInCrashedHistory("crashed-bundle").failure)
|
|
219
|
-
}
|
|
220
|
-
}
|
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
package com.hotupdater
|
|
2
|
-
|
|
3
|
-
import org.junit.Assert.assertEquals
|
|
4
|
-
import org.junit.Assert.assertNull
|
|
5
|
-
import org.junit.Assert.assertTrue
|
|
6
|
-
import org.junit.Rule
|
|
7
|
-
import org.junit.Test
|
|
8
|
-
import org.junit.rules.TemporaryFolder
|
|
9
|
-
import java.io.File
|
|
10
|
-
|
|
11
|
-
class HotUpdaterImplTest {
|
|
12
|
-
@get:Rule
|
|
13
|
-
val temporaryFolder = TemporaryFolder()
|
|
14
|
-
|
|
15
|
-
@Test
|
|
16
|
-
fun `getBundleId returns built in while native launch selection is missing`() {
|
|
17
|
-
val impl =
|
|
18
|
-
createImpl(
|
|
19
|
-
storageBundleId = "staged-bundle",
|
|
20
|
-
storageManifest = mapOf("bundleId" to "staged-bundle"),
|
|
21
|
-
storageBaseURL = "file:///bundle-store/staged-bundle",
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
assertNull(impl.getBundleId())
|
|
25
|
-
assertTrue(impl.getManifest().isEmpty())
|
|
26
|
-
assertNull(impl.getBaseURL())
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@Test
|
|
30
|
-
fun `getBundleId returns current launched bundle over staged metadata`() {
|
|
31
|
-
val impl = createImpl(storageBundleId = "staged-bundle")
|
|
32
|
-
|
|
33
|
-
setCurrentLaunchSelection(
|
|
34
|
-
impl,
|
|
35
|
-
LaunchSelection(
|
|
36
|
-
bundleUrl = "file:///bundle-store/launched-bundle/index.android.bundle",
|
|
37
|
-
launchedBundleId = "launched-bundle",
|
|
38
|
-
shouldRollbackOnCrash = false,
|
|
39
|
-
),
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
assertEquals("launched-bundle", impl.getBundleId())
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
@Test
|
|
46
|
-
fun `getBundleId returns null for built in launch even when staged metadata exists`() {
|
|
47
|
-
val impl = createImpl(storageBundleId = "staged-bundle")
|
|
48
|
-
|
|
49
|
-
setCurrentLaunchSelection(
|
|
50
|
-
impl,
|
|
51
|
-
LaunchSelection(
|
|
52
|
-
bundleUrl = "assets://index.android.bundle",
|
|
53
|
-
launchedBundleId = null,
|
|
54
|
-
shouldRollbackOnCrash = false,
|
|
55
|
-
),
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
assertNull(impl.getBundleId())
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
@Test
|
|
62
|
-
fun `getManifest and getBaseURL return current launched bundle over staged metadata`() {
|
|
63
|
-
val impl =
|
|
64
|
-
createImpl(
|
|
65
|
-
storageBundleId = "staged-bundle",
|
|
66
|
-
storageManifest = mapOf("bundleId" to "staged-bundle"),
|
|
67
|
-
storageBaseURL = "file:///bundle-store/staged-bundle",
|
|
68
|
-
launchedBundleManifests =
|
|
69
|
-
mapOf(
|
|
70
|
-
"launched-bundle" to mapOf("bundleId" to "launched-bundle"),
|
|
71
|
-
),
|
|
72
|
-
launchedBundleBaseURLs =
|
|
73
|
-
mapOf(
|
|
74
|
-
"launched-bundle" to "file:///bundle-store/launched-bundle",
|
|
75
|
-
),
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
setCurrentLaunchSelection(
|
|
79
|
-
impl,
|
|
80
|
-
LaunchSelection(
|
|
81
|
-
bundleUrl = "file:///bundle-store/launched-bundle/index.android.bundle",
|
|
82
|
-
launchedBundleId = "launched-bundle",
|
|
83
|
-
shouldRollbackOnCrash = false,
|
|
84
|
-
),
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
assertEquals(
|
|
88
|
-
mapOf("bundleId" to "launched-bundle"),
|
|
89
|
-
impl.getManifest(),
|
|
90
|
-
)
|
|
91
|
-
assertEquals("file:///bundle-store/launched-bundle", impl.getBaseURL())
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
@Test
|
|
95
|
-
fun `getManifest and getBaseURL return built in values for built in launch`() {
|
|
96
|
-
val impl =
|
|
97
|
-
createImpl(
|
|
98
|
-
storageBundleId = "staged-bundle",
|
|
99
|
-
storageManifest = mapOf("bundleId" to "staged-bundle"),
|
|
100
|
-
storageBaseURL = "file:///bundle-store/staged-bundle",
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
setCurrentLaunchSelection(
|
|
104
|
-
impl,
|
|
105
|
-
LaunchSelection(
|
|
106
|
-
bundleUrl = "assets://index.android.bundle",
|
|
107
|
-
launchedBundleId = null,
|
|
108
|
-
shouldRollbackOnCrash = false,
|
|
109
|
-
),
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
assertTrue(impl.getManifest().isEmpty())
|
|
113
|
-
assertNull(impl.getBaseURL())
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
@Test
|
|
117
|
-
fun `notifyAppReady reads the report without completing the current launch`() {
|
|
118
|
-
val storage = FakeBundleStorageService(bundleId = null)
|
|
119
|
-
val impl = allocateWithoutConstructor<HotUpdaterImpl>()
|
|
120
|
-
setField(impl, "bundleStorage", storage)
|
|
121
|
-
setCurrentLaunchSelection(
|
|
122
|
-
impl,
|
|
123
|
-
LaunchSelection(
|
|
124
|
-
bundleUrl = "file:///bundle-store/launched-bundle/index.android.bundle",
|
|
125
|
-
launchedBundleId = "launched-bundle",
|
|
126
|
-
shouldRollbackOnCrash = false,
|
|
127
|
-
),
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
assertEquals(mapOf("status" to "UNCHANGED"), impl.notifyAppReady())
|
|
131
|
-
assertNull(storage.lastCompletedBundleId)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
@Test
|
|
135
|
-
fun `install id and storage items come from their no backup stores`() {
|
|
136
|
-
val identityFile = File(temporaryFolder.root, "hot-updater/install-identity.json")
|
|
137
|
-
val storageFile = File(temporaryFolder.root, "hot-updater/storage.json")
|
|
138
|
-
val impl = allocateWithoutConstructor<HotUpdaterImpl>()
|
|
139
|
-
setField(impl, "installIdentity", InstallIdentityService(identityFile))
|
|
140
|
-
setField(impl, "keyValueStorage", KeyValueStorageService(storageFile))
|
|
141
|
-
|
|
142
|
-
val installId = impl.getInstallId()
|
|
143
|
-
assertEquals(installId, InstallationIdentity.loadFromFile(identityFile)?.installId)
|
|
144
|
-
|
|
145
|
-
impl.setStorageItem("plugins/insights/queue", "[]")
|
|
146
|
-
assertEquals("[]", impl.getStorageItem("plugins/insights/queue"))
|
|
147
|
-
assertEquals("[]", KeyValueStorageService(storageFile).getItem("plugins/insights/queue"))
|
|
148
|
-
|
|
149
|
-
impl.setStorageItem("plugins/insights/queue", null)
|
|
150
|
-
assertNull(impl.getStorageItem("plugins/insights/queue"))
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
private fun createImpl(
|
|
154
|
-
storageBundleId: String?,
|
|
155
|
-
storageManifest: Map<String, Any?> = emptyMap(),
|
|
156
|
-
storageBaseURL: String = "",
|
|
157
|
-
launchedBundleManifests: Map<String, Map<String, Any?>> = emptyMap(),
|
|
158
|
-
launchedBundleBaseURLs: Map<String, String> = emptyMap(),
|
|
159
|
-
): HotUpdaterImpl =
|
|
160
|
-
allocateWithoutConstructor<HotUpdaterImpl>().also { impl ->
|
|
161
|
-
setField(
|
|
162
|
-
impl,
|
|
163
|
-
"bundleStorage",
|
|
164
|
-
FakeBundleStorageService(
|
|
165
|
-
bundleId = storageBundleId,
|
|
166
|
-
manifest = storageManifest,
|
|
167
|
-
baseURL = storageBaseURL,
|
|
168
|
-
launchedBundleManifests = launchedBundleManifests,
|
|
169
|
-
launchedBundleBaseURLs = launchedBundleBaseURLs,
|
|
170
|
-
),
|
|
171
|
-
)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
private fun setCurrentLaunchSelection(
|
|
175
|
-
impl: HotUpdaterImpl,
|
|
176
|
-
selection: LaunchSelection,
|
|
177
|
-
) {
|
|
178
|
-
setField(impl, "currentLaunchSelection", selection)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private fun setField(
|
|
182
|
-
target: Any,
|
|
183
|
-
fieldName: String,
|
|
184
|
-
value: Any?,
|
|
185
|
-
) {
|
|
186
|
-
val field = HotUpdaterImpl::class.java.getDeclaredField(fieldName)
|
|
187
|
-
field.isAccessible = true
|
|
188
|
-
field.set(target, value)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
private inline fun <reified T> allocateWithoutConstructor(): T {
|
|
192
|
-
val field = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe")
|
|
193
|
-
field.isAccessible = true
|
|
194
|
-
val unsafe = field.get(null)
|
|
195
|
-
val allocateInstance = unsafe.javaClass.getMethod("allocateInstance", Class::class.java)
|
|
196
|
-
@Suppress("UNCHECKED_CAST")
|
|
197
|
-
return allocateInstance.invoke(unsafe, T::class.java) as T
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
private class FakeBundleStorageService(
|
|
201
|
-
private val bundleId: String?,
|
|
202
|
-
private val manifest: Map<String, Any?> = emptyMap(),
|
|
203
|
-
private val baseURL: String = "",
|
|
204
|
-
private val launchedBundleManifests: Map<String, Map<String, Any?>> =
|
|
205
|
-
emptyMap(),
|
|
206
|
-
private val launchedBundleBaseURLs: Map<String, String> = emptyMap(),
|
|
207
|
-
) : BundleStorageService {
|
|
208
|
-
var lastCompletedBundleId: String? = null
|
|
209
|
-
|
|
210
|
-
override fun setBundleURL(localPath: String?): Boolean = true
|
|
211
|
-
|
|
212
|
-
override fun getCachedBundleURL(): String? = null
|
|
213
|
-
|
|
214
|
-
override fun getFallbackBundleURL(): String = "assets://index.android.bundle"
|
|
215
|
-
|
|
216
|
-
override fun prepareLaunch(pendingRecovery: PendingCrashRecovery?): LaunchSelection =
|
|
217
|
-
LaunchSelection(
|
|
218
|
-
bundleUrl = "assets://index.android.bundle",
|
|
219
|
-
launchedBundleId = null,
|
|
220
|
-
shouldRollbackOnCrash = false,
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
override suspend fun updateBundle(
|
|
224
|
-
bundleId: String,
|
|
225
|
-
manifestUrl: String,
|
|
226
|
-
manifestFileHash: String,
|
|
227
|
-
assets: Map<String, ChangedAssetDescriptor>,
|
|
228
|
-
archiveUrl: String?,
|
|
229
|
-
progressCallback: (UpdateProgressPayload) -> Unit,
|
|
230
|
-
) = UpdateBundleResult(BundleDelivery.MANIFEST, patchFallback = false)
|
|
231
|
-
|
|
232
|
-
override fun markLaunchCompleted(currentBundleId: String?) {
|
|
233
|
-
lastCompletedBundleId = currentBundleId
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
override fun notifyAppReady(): Map<String, Any?> = mapOf("status" to "UNCHANGED")
|
|
237
|
-
|
|
238
|
-
override fun getCrashHistory(): CrashedHistory = CrashedHistory()
|
|
239
|
-
|
|
240
|
-
override fun clearCrashHistory(): Boolean = true
|
|
241
|
-
|
|
242
|
-
override fun getBaseURL(): String = baseURL
|
|
243
|
-
|
|
244
|
-
override fun getBaseURLForBundle(bundleId: String?): String =
|
|
245
|
-
if (bundleId == null) {
|
|
246
|
-
""
|
|
247
|
-
} else {
|
|
248
|
-
launchedBundleBaseURLs[bundleId] ?: ""
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
override fun getBundleId(): String? = bundleId
|
|
252
|
-
|
|
253
|
-
override fun getManifest(): Map<String, Any?> = manifest
|
|
254
|
-
|
|
255
|
-
override fun getManifestForBundle(bundleId: String?): Map<String, Any?> =
|
|
256
|
-
if (bundleId == null) {
|
|
257
|
-
emptyMap()
|
|
258
|
-
} else {
|
|
259
|
-
launchedBundleManifests[bundleId] ?: emptyMap()
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
override suspend fun resetChannel(): Boolean = true
|
|
263
|
-
}
|
|
264
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
package com.hotupdater
|
|
2
|
-
|
|
3
|
-
import org.junit.Assert.assertEquals
|
|
4
|
-
import org.junit.Assert.assertFalse
|
|
5
|
-
import org.junit.Assert.assertNotNull
|
|
6
|
-
import org.junit.Assert.assertNull
|
|
7
|
-
import org.junit.Assert.assertTrue
|
|
8
|
-
import org.junit.Test
|
|
9
|
-
import java.util.concurrent.CountDownLatch
|
|
10
|
-
import java.util.concurrent.TimeUnit
|
|
11
|
-
import java.util.concurrent.atomic.AtomicInteger
|
|
12
|
-
|
|
13
|
-
class HotUpdaterRecoveryStateTest {
|
|
14
|
-
@Test
|
|
15
|
-
fun `content queued after a fatal error cannot verify the failed bundle`() {
|
|
16
|
-
val state = HotUpdaterRecoveryState()
|
|
17
|
-
state.start("crashed-bundle", true)
|
|
18
|
-
|
|
19
|
-
val request = requireNotNull(state.requestRecovery())
|
|
20
|
-
assertNull(state.completeLaunch())
|
|
21
|
-
state.finishRecoveryRequest(request, true)
|
|
22
|
-
|
|
23
|
-
assertNull(state.completeLaunch())
|
|
24
|
-
assertEquals(HotUpdaterRecoveryState.Launch("crashed-bundle", true), state.snapshot())
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
@Test
|
|
28
|
-
fun `a fatal error after first content preserves the verified bundle`() {
|
|
29
|
-
val state = HotUpdaterRecoveryState()
|
|
30
|
-
state.start("verified-bundle", true)
|
|
31
|
-
|
|
32
|
-
assertNotNull(state.completeLaunch())
|
|
33
|
-
|
|
34
|
-
assertNull(state.requestRecovery())
|
|
35
|
-
assertNull(state.completeLaunch())
|
|
36
|
-
assertEquals(HotUpdaterRecoveryState.Launch("verified-bundle", false), state.snapshot())
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
@Test
|
|
40
|
-
fun `concurrent fatal errors schedule only one restart while the request is in flight`() {
|
|
41
|
-
val state = HotUpdaterRecoveryState()
|
|
42
|
-
state.start("crashed-bundle", true)
|
|
43
|
-
val begin = CountDownLatch(1)
|
|
44
|
-
val finished = CountDownLatch(2)
|
|
45
|
-
val restartRequests = AtomicInteger()
|
|
46
|
-
val handledErrors = AtomicInteger()
|
|
47
|
-
val workers =
|
|
48
|
-
List(2) {
|
|
49
|
-
Thread {
|
|
50
|
-
begin.await()
|
|
51
|
-
val request = state.requestRecovery()
|
|
52
|
-
if (request != null) {
|
|
53
|
-
handledErrors.incrementAndGet()
|
|
54
|
-
if (request.shouldRestart) restartRequests.incrementAndGet()
|
|
55
|
-
}
|
|
56
|
-
finished.countDown()
|
|
57
|
-
}.apply { start() }
|
|
58
|
-
}
|
|
59
|
-
begin.countDown()
|
|
60
|
-
assertTrue(finished.await(5, TimeUnit.SECONDS))
|
|
61
|
-
workers.forEach { it.join() }
|
|
62
|
-
|
|
63
|
-
assertEquals(2, handledErrors.get())
|
|
64
|
-
assertEquals(1, restartRequests.get())
|
|
65
|
-
assertNull(state.completeLaunch())
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
@Test
|
|
69
|
-
fun `a failed restart keeps the crash unverified and permits another restart request`() {
|
|
70
|
-
val state = HotUpdaterRecoveryState()
|
|
71
|
-
state.start("crashed-bundle", true)
|
|
72
|
-
val request = requireNotNull(state.requestRecovery())
|
|
73
|
-
|
|
74
|
-
state.finishRecoveryRequest(request, false)
|
|
75
|
-
|
|
76
|
-
assertNull(state.completeLaunch())
|
|
77
|
-
val retry = requireNotNull(state.requestRecovery())
|
|
78
|
-
assertTrue(retry.shouldRestart)
|
|
79
|
-
state.finishRecoveryRequest(retry, true)
|
|
80
|
-
assertFalse(requireNotNull(state.requestRecovery()).shouldRestart)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
@Test
|
|
84
|
-
fun `a previous launch restart completion cannot change the next launch`() {
|
|
85
|
-
val state = HotUpdaterRecoveryState()
|
|
86
|
-
state.start("crashed-bundle", true)
|
|
87
|
-
val request = requireNotNull(state.requestRecovery())
|
|
88
|
-
state.start("safe-bundle", false)
|
|
89
|
-
|
|
90
|
-
state.finishRecoveryRequest(request, true)
|
|
91
|
-
|
|
92
|
-
assertEquals("safe-bundle", state.completeLaunch()?.bundleId)
|
|
93
|
-
state.stop()
|
|
94
|
-
assertNull(state.snapshot())
|
|
95
|
-
assertNull(state.requestRecovery())
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
package com.hotupdater
|
|
2
|
-
|
|
3
|
-
import org.json.JSONObject
|
|
4
|
-
import org.junit.Assert.assertEquals
|
|
5
|
-
import org.junit.Assert.assertNotEquals
|
|
6
|
-
import org.junit.Rule
|
|
7
|
-
import org.junit.Test
|
|
8
|
-
import org.junit.rules.TemporaryFolder
|
|
9
|
-
import java.io.File
|
|
10
|
-
import java.util.UUID
|
|
11
|
-
|
|
12
|
-
class InstallIdentityServiceTest {
|
|
13
|
-
@get:Rule
|
|
14
|
-
val temporaryFolder = TemporaryFolder()
|
|
15
|
-
|
|
16
|
-
@Test
|
|
17
|
-
fun `creates a random install id once and keeps it across instances`() {
|
|
18
|
-
val identityFile = File(temporaryFolder.root, "hot-updater/install-identity.json")
|
|
19
|
-
val service = InstallIdentityService(identityFile)
|
|
20
|
-
|
|
21
|
-
val installId = service.getInstallId()
|
|
22
|
-
|
|
23
|
-
assertEquals(installId, UUID.fromString(installId).toString())
|
|
24
|
-
assertEquals(installId, service.getInstallId())
|
|
25
|
-
assertEquals(installId, InstallIdentityService(identityFile).getInstallId())
|
|
26
|
-
assertEquals(
|
|
27
|
-
listOf("installId"),
|
|
28
|
-
JSONObject(identityFile.readText()).keys().asSequence().toList(),
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
@Test
|
|
33
|
-
fun `lives under noBackupFilesDir and never reads the old bundle store identity`() {
|
|
34
|
-
val noBackupDirectory = temporaryFolder.newFolder("no_backup")
|
|
35
|
-
val oldIdentityJson = "{\"installId\":\"old-install-id\",\"userId\":\"user-1\",\"username\":\"alice\"}"
|
|
36
|
-
val oldIdentityFile =
|
|
37
|
-
File(temporaryFolder.newFolder("files"), "bundle-store/identity.json").apply {
|
|
38
|
-
parentFile?.mkdirs()
|
|
39
|
-
writeText(oldIdentityJson)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
val installId = InstallIdentityService.create(NoBackupContext(noBackupDirectory)).getInstallId()
|
|
43
|
-
|
|
44
|
-
assertNotEquals("old-install-id", installId)
|
|
45
|
-
assertEquals(
|
|
46
|
-
InstallationIdentity(installId),
|
|
47
|
-
InstallationIdentity.loadFromFile(File(noBackupDirectory, "hot-updater/install-identity.json")),
|
|
48
|
-
)
|
|
49
|
-
assertEquals(oldIdentityJson, oldIdentityFile.readText())
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
@Test
|
|
53
|
-
fun `replaces an unreadable identity with a new id`() {
|
|
54
|
-
val identityFile = File(temporaryFolder.root, "install-identity.json").apply { writeText("{") }
|
|
55
|
-
|
|
56
|
-
val installId = InstallIdentityService(identityFile).getInstallId()
|
|
57
|
-
|
|
58
|
-
assertEquals(InstallationIdentity(installId), InstallationIdentity.loadFromFile(identityFile))
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
@Test
|
|
62
|
-
fun `keeps the install id for the process after its file becomes unreadable`() {
|
|
63
|
-
val identityFile = File(temporaryFolder.root, "install-identity.json")
|
|
64
|
-
val service = InstallIdentityService(identityFile)
|
|
65
|
-
val installId = service.getInstallId()
|
|
66
|
-
|
|
67
|
-
identityFile.writeText("{")
|
|
68
|
-
|
|
69
|
-
assertEquals(installId, service.getInstallId())
|
|
70
|
-
}
|
|
71
|
-
}
|