@hot-updater/react-native 1.0.0-rc.3 → 1.0.0-rc.30

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.
Files changed (300) hide show
  1. package/android/proguard-rules.pro +17 -2
  2. package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
  3. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +954 -837
  4. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +15 -11
  5. package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
  6. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
  7. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +43 -31
  8. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +37 -54
  9. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
  10. package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
  11. package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
  12. package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
  13. package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
  14. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
  15. package/android/src/main/java/com/hotupdater/PreviousProcessExit.kt +89 -0
  16. package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
  17. package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
  18. package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
  19. package/android/src/newarch/HotUpdater.kt +7 -9
  20. package/android/src/newarch/HotUpdaterModule.kt +109 -84
  21. package/android/src/oldarch/HotUpdater.kt +7 -9
  22. package/android/src/oldarch/HotUpdaterModule.kt +98 -74
  23. package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
  24. package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
  25. package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
  26. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +950 -1112
  27. package/ios/HotUpdater/Internal/BundleMetadata.swift +8 -53
  28. package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
  29. package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
  30. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
  31. package/ios/HotUpdater/Internal/HotUpdater.mm +13 -45
  32. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +190 -73
  33. package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
  34. package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
  35. package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
  36. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
  37. package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
  38. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
  39. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
  40. package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
  41. package/lib/commonjs/appReady.js +111 -0
  42. package/lib/commonjs/appReady.js.map +1 -0
  43. package/lib/commonjs/checkForUpdate.js +124 -69
  44. package/lib/commonjs/checkForUpdate.js.map +1 -1
  45. package/lib/commonjs/clientPlugin.js +13 -0
  46. package/lib/commonjs/clientPlugin.js.map +1 -0
  47. package/lib/commonjs/error.js +4 -13
  48. package/lib/commonjs/error.js.map +1 -1
  49. package/lib/commonjs/fetchJSON.js +16 -27
  50. package/lib/commonjs/fetchJSON.js.map +1 -1
  51. package/lib/commonjs/fetchUpdateResponse.js +54 -0
  52. package/lib/commonjs/fetchUpdateResponse.js.map +1 -0
  53. package/lib/commonjs/httpClient.js +63 -94
  54. package/lib/commonjs/httpClient.js.map +1 -1
  55. package/lib/commonjs/index.js +71 -18
  56. package/lib/commonjs/index.js.map +1 -1
  57. package/lib/commonjs/native.js +78 -60
  58. package/lib/commonjs/native.js.map +1 -1
  59. package/lib/commonjs/pluginHost.js +44 -0
  60. package/lib/commonjs/pluginHost.js.map +1 -0
  61. package/lib/commonjs/releaseCatalogCache.js +36 -87
  62. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  63. package/lib/commonjs/sdkVersion.js +1 -1
  64. package/lib/commonjs/sdkVersion.js.map +1 -1
  65. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  66. package/lib/commonjs/store.js +3 -10
  67. package/lib/commonjs/store.js.map +1 -1
  68. package/lib/commonjs/updateError.js +132 -0
  69. package/lib/commonjs/updateError.js.map +1 -0
  70. package/lib/commonjs/wrap.js +43 -26
  71. package/lib/commonjs/wrap.js.map +1 -1
  72. package/lib/module/appReady.js +103 -0
  73. package/lib/module/appReady.js.map +1 -0
  74. package/lib/module/checkForUpdate.js +117 -64
  75. package/lib/module/checkForUpdate.js.map +1 -1
  76. package/lib/module/clientPlugin.js +4 -0
  77. package/lib/module/clientPlugin.js.map +1 -0
  78. package/lib/module/error.js +4 -13
  79. package/lib/module/error.js.map +1 -1
  80. package/lib/module/fetchJSON.js +14 -26
  81. package/lib/module/fetchJSON.js.map +1 -1
  82. package/lib/module/fetchUpdateResponse.js +49 -0
  83. package/lib/module/fetchUpdateResponse.js.map +1 -0
  84. package/lib/module/httpClient.js +62 -93
  85. package/lib/module/httpClient.js.map +1 -1
  86. package/lib/module/index.js +58 -20
  87. package/lib/module/index.js.map +1 -1
  88. package/lib/module/native.js +71 -53
  89. package/lib/module/native.js.map +1 -1
  90. package/lib/module/pluginHost.js +35 -0
  91. package/lib/module/pluginHost.js.map +1 -0
  92. package/lib/module/releaseCatalogCache.js +32 -82
  93. package/lib/module/releaseCatalogCache.js.map +1 -1
  94. package/lib/module/sdkVersion.js +1 -1
  95. package/lib/module/sdkVersion.js.map +1 -1
  96. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  97. package/lib/module/store.js +3 -10
  98. package/lib/module/store.js.map +1 -1
  99. package/lib/module/updateError.js +126 -0
  100. package/lib/module/updateError.js.map +1 -0
  101. package/lib/module/wrap.js +43 -26
  102. package/lib/module/wrap.js.map +1 -1
  103. package/lib/typescript/commonjs/appReady.d.ts +23 -0
  104. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/checkForUpdate.d.ts +16 -4
  106. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
  108. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  109. package/lib/typescript/commonjs/error.d.ts +4 -13
  110. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
  112. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
  114. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/httpClient.d.ts +4 -33
  116. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/index.d.ts +4 -8
  118. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/native.d.ts +31 -22
  120. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/pluginHost.d.ts +4 -0
  122. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  123. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -14
  124. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  126. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +42 -47
  128. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/store.d.ts +1 -3
  130. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/types.d.ts +1 -1
  132. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  134. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  136. package/lib/typescript/commonjs/wrap.types.d.ts +6 -7
  137. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  138. package/lib/typescript/module/appReady.d.ts +23 -0
  139. package/lib/typescript/module/appReady.d.ts.map +1 -0
  140. package/lib/typescript/module/checkForUpdate.d.ts +16 -4
  141. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  142. package/lib/typescript/module/clientPlugin.d.ts +2 -0
  143. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  144. package/lib/typescript/module/error.d.ts +4 -13
  145. package/lib/typescript/module/error.d.ts.map +1 -1
  146. package/lib/typescript/module/fetchJSON.d.ts +5 -5
  147. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  148. package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
  149. package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
  150. package/lib/typescript/module/httpClient.d.ts +4 -33
  151. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  152. package/lib/typescript/module/index.d.ts +4 -8
  153. package/lib/typescript/module/index.d.ts.map +1 -1
  154. package/lib/typescript/module/native.d.ts +31 -22
  155. package/lib/typescript/module/native.d.ts.map +1 -1
  156. package/lib/typescript/module/pluginHost.d.ts +4 -0
  157. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  158. package/lib/typescript/module/releaseCatalogCache.d.ts +8 -14
  159. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  160. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  161. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  162. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +42 -47
  163. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  164. package/lib/typescript/module/store.d.ts +1 -3
  165. package/lib/typescript/module/store.d.ts.map +1 -1
  166. package/lib/typescript/module/types.d.ts +1 -1
  167. package/lib/typescript/module/types.d.ts.map +1 -1
  168. package/lib/typescript/module/updateError.d.ts +26 -0
  169. package/lib/typescript/module/updateError.d.ts.map +1 -0
  170. package/lib/typescript/module/wrap.d.ts.map +1 -1
  171. package/lib/typescript/module/wrap.types.d.ts +6 -7
  172. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  173. package/package.json +7 -4
  174. package/src/appReady.ts +149 -0
  175. package/src/checkForUpdate.ts +151 -88
  176. package/src/clientPlugin.ts +18 -0
  177. package/src/error.ts +4 -14
  178. package/src/fetchJSON.ts +14 -30
  179. package/src/fetchUpdateResponse.ts +54 -0
  180. package/src/httpClient.ts +88 -159
  181. package/src/index.ts +90 -24
  182. package/src/native.ts +106 -102
  183. package/src/pluginHost.ts +45 -0
  184. package/src/releaseCatalogCache.ts +43 -172
  185. package/src/sdkVersion.ts +1 -1
  186. package/src/specs/NativeHotUpdater.ts +41 -48
  187. package/src/store.ts +2 -23
  188. package/src/types.ts +1 -1
  189. package/src/updateError.ts +153 -0
  190. package/src/wrap.tsx +57 -26
  191. package/src/wrap.types.ts +6 -7
  192. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  193. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  194. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  195. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  196. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  197. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  198. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
  199. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
  200. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  201. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
  202. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  203. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  204. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  205. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  206. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  207. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  208. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  209. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  210. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  211. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  212. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  213. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  214. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  215. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -615
  216. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  217. package/lib/commonjs/httpClient.spec.js +0 -102
  218. package/lib/commonjs/httpClient.spec.js.map +0 -1
  219. package/lib/commonjs/index.spec.js +0 -302
  220. package/lib/commonjs/index.spec.js.map +0 -1
  221. package/lib/commonjs/iosInterop.spec.js +0 -18
  222. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  223. package/lib/commonjs/native.spec.js +0 -883
  224. package/lib/commonjs/native.spec.js.map +0 -1
  225. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
  226. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  227. package/lib/commonjs/notifyAppReadyInsights.js +0 -133
  228. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  229. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
  230. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  231. package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
  232. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  233. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
  234. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  235. package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
  236. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  237. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  238. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  239. package/lib/commonjs/store.notification.spec.js +0 -76
  240. package/lib/commonjs/store.notification.spec.js.map +0 -1
  241. package/lib/commonjs/store.spec.js +0 -316
  242. package/lib/commonjs/store.spec.js.map +0 -1
  243. package/lib/commonjs/wrap.spec.js +0 -329
  244. package/lib/commonjs/wrap.spec.js.map +0 -1
  245. package/lib/module/catalogCacheNative.spec.js +0 -31
  246. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  247. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -614
  248. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  249. package/lib/module/httpClient.spec.js +0 -102
  250. package/lib/module/httpClient.spec.js.map +0 -1
  251. package/lib/module/index.spec.js +0 -301
  252. package/lib/module/index.spec.js.map +0 -1
  253. package/lib/module/iosInterop.spec.js +0 -18
  254. package/lib/module/iosInterop.spec.js.map +0 -1
  255. package/lib/module/native.spec.js +0 -882
  256. package/lib/module/native.spec.js.map +0 -1
  257. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
  258. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  259. package/lib/module/notifyAppReadyInsights.js +0 -128
  260. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  261. package/lib/module/notifyAppReadyInsights.spec.js +0 -245
  262. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  263. package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
  264. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  265. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
  266. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  267. package/lib/module/releaseCatalogCache.spec.js +0 -195
  268. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  269. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  270. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  271. package/lib/module/store.notification.spec.js +0 -77
  272. package/lib/module/store.notification.spec.js.map +0 -1
  273. package/lib/module/store.spec.js +0 -315
  274. package/lib/module/store.spec.js.map +0 -1
  275. package/lib/module/wrap.spec.js +0 -330
  276. package/lib/module/wrap.spec.js.map +0 -1
  277. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
  278. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  279. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  280. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  281. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
  282. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  283. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  284. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  285. package/src/catalogCacheNative.spec.ts +0 -63
  286. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -611
  287. package/src/httpClient.spec.ts +0 -132
  288. package/src/index.spec.ts +0 -337
  289. package/src/iosInterop.spec.ts +0 -30
  290. package/src/native.spec.ts +0 -945
  291. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
  292. package/src/notifyAppReadyInsights.spec.ts +0 -243
  293. package/src/notifyAppReadyInsights.test-utils.ts +0 -31
  294. package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
  295. package/src/notifyAppReadyInsights.ts +0 -194
  296. package/src/releaseCatalogCache.spec.ts +0 -282
  297. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  298. package/src/store.notification.spec.ts +0 -92
  299. package/src/store.spec.ts +0 -372
  300. package/src/wrap.spec.ts +0 -406
@@ -1,273 +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.Test
7
-
8
- class HotUpdaterImplTest {
9
- @Test
10
- fun `getBundleId returns built in while native launch selection is missing`() {
11
- val impl =
12
- createImpl(
13
- storageBundleId = "staged-bundle",
14
- storageManifest = mapOf("bundleId" to "staged-bundle"),
15
- storageBaseURL = "file:///bundle-store/staged-bundle",
16
- )
17
-
18
- assertNull(impl.getBundleId())
19
- assertTrue(impl.getManifest().isEmpty())
20
- assertNull(impl.getBaseURL())
21
- }
22
-
23
- @Test
24
- fun `getBundleId returns current launched bundle over staged metadata`() {
25
- val impl = createImpl(storageBundleId = "staged-bundle")
26
-
27
- setCurrentLaunchSelection(
28
- impl,
29
- LaunchSelection(
30
- bundleUrl = "file:///bundle-store/launched-bundle/index.android.bundle",
31
- launchedBundleId = "launched-bundle",
32
- shouldRollbackOnCrash = false,
33
- ),
34
- )
35
-
36
- assertEquals("launched-bundle", impl.getBundleId())
37
- }
38
-
39
- @Test
40
- fun `getBundleId returns null for built in launch even when staged metadata exists`() {
41
- val impl = createImpl(storageBundleId = "staged-bundle")
42
-
43
- setCurrentLaunchSelection(
44
- impl,
45
- LaunchSelection(
46
- bundleUrl = "assets://index.android.bundle",
47
- launchedBundleId = null,
48
- shouldRollbackOnCrash = false,
49
- ),
50
- )
51
-
52
- assertNull(impl.getBundleId())
53
- }
54
-
55
- @Test
56
- fun `getManifest and getBaseURL return current launched bundle over staged metadata`() {
57
- val impl =
58
- createImpl(
59
- storageBundleId = "staged-bundle",
60
- storageManifest = mapOf("bundleId" to "staged-bundle"),
61
- storageBaseURL = "file:///bundle-store/staged-bundle",
62
- launchedBundleManifests =
63
- mapOf(
64
- "launched-bundle" to mapOf("bundleId" to "launched-bundle"),
65
- ),
66
- launchedBundleBaseURLs =
67
- mapOf(
68
- "launched-bundle" to "file:///bundle-store/launched-bundle",
69
- ),
70
- )
71
-
72
- setCurrentLaunchSelection(
73
- impl,
74
- LaunchSelection(
75
- bundleUrl = "file:///bundle-store/launched-bundle/index.android.bundle",
76
- launchedBundleId = "launched-bundle",
77
- shouldRollbackOnCrash = false,
78
- ),
79
- )
80
-
81
- assertEquals(
82
- mapOf("bundleId" to "launched-bundle"),
83
- impl.getManifest(),
84
- )
85
- assertEquals("file:///bundle-store/launched-bundle", impl.getBaseURL())
86
- }
87
-
88
- @Test
89
- fun `getManifest and getBaseURL return built in values for built in launch`() {
90
- val impl =
91
- createImpl(
92
- storageBundleId = "staged-bundle",
93
- storageManifest = mapOf("bundleId" to "staged-bundle"),
94
- storageBaseURL = "file:///bundle-store/staged-bundle",
95
- )
96
-
97
- setCurrentLaunchSelection(
98
- impl,
99
- LaunchSelection(
100
- bundleUrl = "assets://index.android.bundle",
101
- launchedBundleId = null,
102
- shouldRollbackOnCrash = false,
103
- ),
104
- )
105
-
106
- assertTrue(impl.getManifest().isEmpty())
107
- assertNull(impl.getBaseURL())
108
- }
109
-
110
- @Test
111
- fun `notifyAppReady reads the report without completing the current launch`() {
112
- val storage = FakeBundleStorageService(bundleId = null)
113
- val impl = allocateWithoutConstructor<HotUpdaterImpl>()
114
- setField(impl, "bundleStorage", storage)
115
- setCurrentLaunchSelection(
116
- impl,
117
- LaunchSelection(
118
- bundleUrl = "file:///bundle-store/launched-bundle/index.android.bundle",
119
- launchedBundleId = "launched-bundle",
120
- shouldRollbackOnCrash = false,
121
- ),
122
- )
123
-
124
- assertEquals(mapOf("status" to "UNCHANGED"), impl.notifyAppReady())
125
- assertNull(storage.lastCompletedBundleId)
126
- }
127
-
128
- @Test
129
- fun `getInstallId and setUser delegate to bundle storage`() {
130
- val storage = FakeBundleStorageService(bundleId = null)
131
- val impl = allocateWithoutConstructor<HotUpdaterImpl>()
132
- setField(impl, "bundleStorage", storage)
133
-
134
- assertEquals("test-install-id", impl.getInstallId())
135
-
136
- impl.setUser("user-123", "alice")
137
- assertEquals("user-123", storage.lastUserId)
138
- assertEquals("alice", storage.lastUsername)
139
-
140
- impl.setUser(null, null)
141
- assertNull(storage.lastUserId)
142
- assertNull(storage.lastUsername)
143
- }
144
-
145
- private fun createImpl(
146
- storageBundleId: String?,
147
- storageManifest: Map<String, Any?> = emptyMap(),
148
- storageBaseURL: String = "",
149
- launchedBundleManifests: Map<String, Map<String, Any?>> = emptyMap(),
150
- launchedBundleBaseURLs: Map<String, String> = emptyMap(),
151
- ): HotUpdaterImpl =
152
- allocateWithoutConstructor<HotUpdaterImpl>().also { impl ->
153
- setField(
154
- impl,
155
- "bundleStorage",
156
- FakeBundleStorageService(
157
- bundleId = storageBundleId,
158
- manifest = storageManifest,
159
- baseURL = storageBaseURL,
160
- launchedBundleManifests = launchedBundleManifests,
161
- launchedBundleBaseURLs = launchedBundleBaseURLs,
162
- ),
163
- )
164
- }
165
-
166
- private fun setCurrentLaunchSelection(
167
- impl: HotUpdaterImpl,
168
- selection: LaunchSelection,
169
- ) {
170
- setField(impl, "currentLaunchSelection", selection)
171
- }
172
-
173
- private fun setField(
174
- target: Any,
175
- fieldName: String,
176
- value: Any?,
177
- ) {
178
- val field = HotUpdaterImpl::class.java.getDeclaredField(fieldName)
179
- field.isAccessible = true
180
- field.set(target, value)
181
- }
182
-
183
- private inline fun <reified T> allocateWithoutConstructor(): T {
184
- val field = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe")
185
- field.isAccessible = true
186
- val unsafe = field.get(null)
187
- val allocateInstance = unsafe.javaClass.getMethod("allocateInstance", Class::class.java)
188
- @Suppress("UNCHECKED_CAST")
189
- return allocateInstance.invoke(unsafe, T::class.java) as T
190
- }
191
-
192
- private class FakeBundleStorageService(
193
- private val bundleId: String?,
194
- private val manifest: Map<String, Any?> = emptyMap(),
195
- private val baseURL: String = "",
196
- private val launchedBundleManifests: Map<String, Map<String, Any?>> =
197
- emptyMap(),
198
- private val launchedBundleBaseURLs: Map<String, String> = emptyMap(),
199
- ) : BundleStorageService {
200
- var lastCompletedBundleId: String? = null
201
- var lastUserId: String? = null
202
- var lastUsername: String? = null
203
-
204
- override fun setBundleURL(localPath: String?): Boolean = true
205
-
206
- override fun getCachedBundleURL(): String? = null
207
-
208
- override fun getFallbackBundleURL(): String = "assets://index.android.bundle"
209
-
210
- override fun prepareLaunch(pendingRecovery: PendingCrashRecovery?): LaunchSelection =
211
- LaunchSelection(
212
- bundleUrl = "assets://index.android.bundle",
213
- launchedBundleId = null,
214
- shouldRollbackOnCrash = false,
215
- )
216
-
217
- override suspend fun updateBundle(
218
- bundleId: String,
219
- fileUrl: String?,
220
- fileHash: String?,
221
- manifestUrl: String?,
222
- manifestFileHash: String?,
223
- changedAssets: Map<String, ChangedAssetDescriptor>?,
224
- progressCallback: (UpdateProgressPayload) -> Unit,
225
- ) = Unit
226
-
227
- override fun markLaunchCompleted(currentBundleId: String?) {
228
- lastCompletedBundleId = currentBundleId
229
- }
230
-
231
- override fun notifyAppReady(): Map<String, Any?> = mapOf("status" to "UNCHANGED")
232
-
233
- override fun getInstallId(): String = "test-install-id"
234
-
235
- override fun getUserId(): String? = lastUserId
236
-
237
- override fun getUsername(): String? = lastUsername
238
-
239
- override fun setUser(
240
- userId: String?,
241
- username: String?,
242
- ) {
243
- lastUserId = userId
244
- lastUsername = username
245
- }
246
-
247
- override fun getCrashHistory(): CrashedHistory = CrashedHistory()
248
-
249
- override fun clearCrashHistory(): Boolean = true
250
-
251
- override fun getBaseURL(): String = baseURL
252
-
253
- override fun getBaseURLForBundle(bundleId: String?): String =
254
- if (bundleId == null) {
255
- ""
256
- } else {
257
- launchedBundleBaseURLs[bundleId] ?: ""
258
- }
259
-
260
- override fun getBundleId(): String? = bundleId
261
-
262
- override fun getManifest(): Map<String, Any?> = manifest
263
-
264
- override fun getManifestForBundle(bundleId: String?): Map<String, Any?> =
265
- if (bundleId == null) {
266
- emptyMap()
267
- } else {
268
- launchedBundleManifests[bundleId] ?: emptyMap()
269
- }
270
-
271
- override suspend fun resetChannel(): Boolean = true
272
- }
273
- }
@@ -1,20 +0,0 @@
1
- package com.hotupdater
2
-
3
- import org.junit.Assert.assertEquals
4
- import org.junit.Assert.assertNull
5
- import org.junit.Test
6
-
7
- class NativeConfigUtilsTest {
8
- @Test
9
- fun `coerces typed manifest metadata to strings`() {
10
- assertEquals("123", NativeConfigUtils.coerceManifestMetaDataString(123))
11
- assertEquals("true", NativeConfigUtils.coerceManifestMetaDataString(true))
12
- assertEquals("-16711936", NativeConfigUtils.coerceManifestMetaDataString(-16711936))
13
- }
14
-
15
- @Test
16
- fun `ignores missing and empty manifest metadata`() {
17
- assertNull(NativeConfigUtils.coerceManifestMetaDataString(null))
18
- assertNull(NativeConfigUtils.coerceManifestMetaDataString(""))
19
- }
20
- }
@@ -1,111 +0,0 @@
1
- package com.hotupdater
2
-
3
- import kotlinx.coroutines.runBlocking
4
- import org.junit.Assert.assertArrayEquals
5
- import org.junit.Assert.assertEquals
6
- import org.junit.Assert.assertNull
7
- import org.junit.Assert.assertTrue
8
- import org.junit.Rule
9
- import org.junit.Test
10
- import org.junit.rules.TemporaryFolder
11
- import java.io.File
12
- import java.net.InetAddress
13
- import java.net.ServerSocket
14
- import java.net.Socket
15
- import java.net.URL
16
- import kotlin.concurrent.thread
17
-
18
- class OkHttpDownloadServiceTest {
19
- @get:Rule
20
- val temporaryFolder = TemporaryFolder()
21
-
22
- @Test
23
- fun `downloadFile succeeds when content length is unknown`() =
24
- runBlocking {
25
- val payload = "bundle-content-without-content-length".repeat(512).toByteArray()
26
- val server = ChunkedResponseServer(payload)
27
-
28
- try {
29
- val destinationDir = temporaryFolder.newFolder("downloads")
30
- val destination = File(destinationDir, "bundle.android.bundle")
31
- var reportedSize: Long? = null
32
-
33
- val result =
34
- OkHttpDownloadService().downloadFile(
35
- fileUrl = URL("http://127.0.0.1:${server.port}/bundle"),
36
- destination = destination,
37
- fileSizeCallback = { size -> reportedSize = size },
38
- progressCallback = {},
39
- )
40
-
41
- assertTrue(result is DownloadResult.Success)
42
- assertEquals(payload.size.toLong(), destination.length())
43
- assertArrayEquals(payload, destination.readBytes())
44
- assertNull(reportedSize)
45
- } finally {
46
- server.close()
47
- }
48
- }
49
-
50
- private class ChunkedResponseServer(
51
- private val payload: ByteArray,
52
- ) : AutoCloseable {
53
- private val serverSocket = ServerSocket(0, 1, InetAddress.getByName("127.0.0.1"))
54
- private val worker =
55
- thread(start = true, isDaemon = true) {
56
- serverSocket.use { socket ->
57
- socket.accept().use(::respond)
58
- }
59
- }
60
-
61
- val port: Int = serverSocket.localPort
62
-
63
- private fun respond(client: Socket) {
64
- drainRequestHeaders(client)
65
-
66
- client.getOutputStream().use { output ->
67
- output.write(
68
- (
69
- "HTTP/1.1 200 OK\r\n" +
70
- "Content-Type: application/octet-stream\r\n" +
71
- "Transfer-Encoding: chunked\r\n" +
72
- "Connection: close\r\n" +
73
- "\r\n"
74
- ).toByteArray(),
75
- )
76
- output.write(payload.size.toString(16).toByteArray())
77
- output.write("\r\n".toByteArray())
78
- output.write(payload)
79
- output.write("\r\n0\r\n\r\n".toByteArray())
80
- output.flush()
81
- }
82
- }
83
-
84
- private fun drainRequestHeaders(client: Socket) {
85
- val input = client.getInputStream()
86
- var matched = 0
87
- val terminator = byteArrayOf('\r'.code.toByte(), '\n'.code.toByte(), '\r'.code.toByte(), '\n'.code.toByte())
88
-
89
- while (matched < terminator.size) {
90
- val next = input.read()
91
- if (next == -1) {
92
- break
93
- }
94
-
95
- matched =
96
- if (next.toByte() == terminator[matched]) {
97
- matched + 1
98
- } else if (next.toByte() == terminator[0]) {
99
- 1
100
- } else {
101
- 0
102
- }
103
- }
104
- }
105
-
106
- override fun close() {
107
- serverSocket.close()
108
- worker.join(1_000)
109
- }
110
- }
111
- }
@@ -1,41 +0,0 @@
1
- package com.hotupdater
2
-
3
- import org.junit.Assert.assertEquals
4
- import org.junit.Assert.assertNull
5
- import org.junit.Rule
6
- import org.junit.Test
7
- import org.junit.rules.TemporaryFolder
8
- import java.io.File
9
-
10
- class RelativePathResolverTest {
11
- @get:Rule
12
- val temporaryFolder = TemporaryFolder()
13
-
14
- @Test
15
- fun `resolves a standard archive directory entry ending in a slash`() {
16
- val root = temporaryFolder.root
17
-
18
- val resolved = RelativePathResolver.resolveInside(root, "drawable-mdpi/")
19
-
20
- assertEquals(
21
- File(root, "drawable-mdpi").canonicalFile,
22
- resolved?.canonicalFile,
23
- )
24
- }
25
-
26
- @Test
27
- fun `rejects unsafe archive directory paths ending in a slash`() {
28
- val root = temporaryFolder.root
29
- val unsafePaths =
30
- listOf(
31
- "/",
32
- "../",
33
- "drawable-mdpi/../",
34
- "drawable-mdpi//",
35
- )
36
-
37
- unsafePaths.forEach { path ->
38
- assertNull(path, RelativePathResolver.resolveInside(root, path))
39
- }
40
- }
41
- }
@@ -1,118 +0,0 @@
1
- package com.hotupdater
2
-
3
- import org.junit.Assert.assertEquals
4
- import org.junit.Assert.assertFalse
5
- import org.junit.Assert.assertNull
6
- import org.junit.Assert.assertTrue
7
- import org.junit.Rule
8
- import org.junit.Test
9
- import org.junit.rules.TemporaryFolder
10
- import java.io.File
11
- import java.nio.file.Files
12
- import java.nio.file.StandardCopyOption
13
-
14
- class ReleaseCatalogCacheServiceTest {
15
- @get:Rule
16
- val temporaryFolder = TemporaryFolder()
17
-
18
- @Test
19
- fun `persists checksum verified entries without credential filenames`() {
20
- val root = temporaryFolder.newFolder("catalog-cache")
21
- val first = createService(root)
22
- val partition = "https://updates.example.com|x-api-key=raw-secret"
23
-
24
- assertTrue(first.set(partition, "validated-catalog"))
25
- assertEquals("validated-catalog", createService(root).get(partition))
26
-
27
- val entry = root.listFiles()!!.single()
28
- assertTrue(entry.name.matches(Regex("[0-9a-f]{64}\\.catalog")))
29
- assertFalse(entry.name.contains("raw-secret"))
30
- assertFalse(entry.readText().contains("raw-secret"))
31
-
32
- entry.appendText("corrupt")
33
- assertNull(createService(root).get(partition))
34
- assertFalse(entry.exists())
35
- }
36
-
37
- @Test
38
- fun `failed atomic replacement preserves the previous entry`() {
39
- val root = temporaryFolder.newFolder("interrupted-write")
40
- val partition = "partition"
41
- assertTrue(createService(root).set(partition, "previous"))
42
-
43
- val interrupted =
44
- ReleaseCatalogCacheService(
45
- rootDirectory = root,
46
- atomicReader = ::readForTest,
47
- atomicWriter = { _, _ -> false },
48
- atomicRemover = ::removeForTest,
49
- )
50
- assertFalse(interrupted.set(partition, "replacement"))
51
- assertEquals("previous", createService(root).get(partition))
52
- }
53
-
54
- @Test
55
- fun `evicts least recently used entries and rejects oversized payloads`() {
56
- val root = temporaryFolder.newFolder("bounded-cache")
57
- val service =
58
- ReleaseCatalogCacheService(
59
- rootDirectory = root,
60
- maxEntries = 2,
61
- maxEntryBytes = 16,
62
- maxTotalBytes = 200,
63
- atomicReader = ::readForTest,
64
- atomicWriter = ::writeForTest,
65
- atomicRemover = ::removeForTest,
66
- )
67
-
68
- assertTrue(service.set("one", "first"))
69
- Thread.sleep(5)
70
- assertTrue(service.set("two", "second"))
71
- Thread.sleep(5)
72
- assertTrue(service.set("three", "third"))
73
-
74
- assertNull(service.get("one"))
75
- assertEquals("second", service.get("two"))
76
- assertEquals("third", service.get("three"))
77
- assertFalse(service.set("two", "x".repeat(17)))
78
- assertEquals("second", service.get("two"))
79
- }
80
-
81
- private fun createService(root: File): ReleaseCatalogCacheService =
82
- ReleaseCatalogCacheService(
83
- rootDirectory = root,
84
- atomicReader = ::readForTest,
85
- atomicWriter = ::writeForTest,
86
- atomicRemover = ::removeForTest,
87
- )
88
-
89
- private fun readForTest(file: File): ByteArray? = if (file.isFile) file.readBytes() else null
90
-
91
- private fun removeForTest(file: File): Boolean = !file.exists() || file.delete()
92
-
93
- private fun writeForTest(
94
- destination: File,
95
- bytes: ByteArray,
96
- ): Boolean =
97
- try {
98
- val temporary = File.createTempFile("entry", ".tmp", destination.parentFile)
99
- temporary.writeBytes(bytes)
100
- try {
101
- Files.move(
102
- temporary.toPath(),
103
- destination.toPath(),
104
- StandardCopyOption.ATOMIC_MOVE,
105
- StandardCopyOption.REPLACE_EXISTING,
106
- )
107
- } catch (_: Exception) {
108
- Files.move(
109
- temporary.toPath(),
110
- destination.toPath(),
111
- StandardCopyOption.REPLACE_EXISTING,
112
- )
113
- }
114
- true
115
- } catch (_: Exception) {
116
- false
117
- }
118
- }
@@ -1,68 +0,0 @@
1
- package com.hotupdater
2
-
3
- import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream
4
- import org.junit.Assert.assertArrayEquals
5
- import org.junit.Assert.assertEquals
6
- import org.junit.Assert.assertNull
7
- import org.junit.Assert.assertThrows
8
- import org.junit.Test
9
- import java.io.ByteArrayInputStream
10
- import java.io.ByteArrayOutputStream
11
- import org.apache.commons.compress.archivers.tar.TarArchiveEntry as CommonsTarArchiveEntry
12
-
13
- class TarArchiveInputStreamTest {
14
- @Test
15
- fun `reads a long basename from a POSIX PAX header`() {
16
- val path = "raw/${"long-name-".repeat(14)}asset.bmp"
17
- val contents = "pax asset".toByteArray()
18
- val archive = createPaxArchive(path, contents)
19
-
20
- TarArchiveInputStream(ByteArrayInputStream(archive)).use { tar ->
21
- val entry = tar.getNextEntry()
22
-
23
- assertEquals(path, entry?.name)
24
- assertArrayEquals(contents, tar.readBytes())
25
- assertNull(tar.getNextEntry())
26
- }
27
- }
28
-
29
- @Test
30
- fun `rejects path traversal from a POSIX PAX header`() {
31
- val path = "../${"long-name-".repeat(14)}asset.bmp"
32
- val archive =
33
- createPaxArchive(
34
- path = path,
35
- contents = "malicious".toByteArray(),
36
- headerPath = "safe.bmp",
37
- )
38
-
39
- val error =
40
- assertThrows(SecurityException::class.java) {
41
- TarArchiveInputStream(ByteArrayInputStream(archive)).use { tar ->
42
- tar.getNextEntry()
43
- }
44
- }
45
-
46
- assertEquals("Path traversal detected: $path", error.message)
47
- }
48
-
49
- private fun createPaxArchive(
50
- path: String,
51
- contents: ByteArray,
52
- headerPath: String = path,
53
- ): ByteArray {
54
- val output = ByteArrayOutputStream()
55
- TarArchiveOutputStream(output).use { tar ->
56
- tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX)
57
- val entry =
58
- CommonsTarArchiveEntry(headerPath).apply {
59
- size = contents.size.toLong()
60
- if (path != headerPath) addPaxHeader("path", path)
61
- }
62
- tar.putArchiveEntry(entry)
63
- tar.write(contents)
64
- tar.closeArchiveEntry()
65
- }
66
- return output.toByteArray()
67
- }
68
- }