@react-native-firebase/app 26.4.0 → 27.1.0

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 (242) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +10 -27
  3. package/RNFBApp.podspec +13 -5
  4. package/android/build.gradle +3 -0
  5. package/android/src/main/java/io/invertase/firebase/common/RNFBHandleCollisionException.kt +29 -0
  6. package/android/src/main/java/io/invertase/firebase/common/RNFBHandleMap.kt +146 -0
  7. package/android/src/main/java/io/invertase/firebase/common/TaskExecutorService.kt +156 -0
  8. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebaseModule.kt +44 -0
  9. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.kt +100 -0
  10. package/android/src/reactnative/java/io/invertase/firebase/app/NativeRNFBTurboApp.kt +250 -0
  11. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseApp.kt +64 -0
  12. package/android/src/reactnative/java/io/invertase/firebase/app/{ReactNativeFirebaseAppInitProvider.java → ReactNativeFirebaseAppInitProvider.kt} +3 -4
  13. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.kt +47 -0
  14. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppRegistrar.kt +33 -0
  15. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseVersion.java +1 -1
  16. package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.kt +185 -0
  17. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEvent.kt +35 -0
  18. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.kt +662 -0
  19. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseJSON.kt +103 -0
  20. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.kt +96 -0
  21. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.kt +140 -0
  22. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.kt +109 -0
  23. package/android/src/reactnative/java/io/invertase/firebase/common/SharedUtils.kt +451 -0
  24. package/android/src/reactnative/java/io/invertase/firebase/interfaces/{ContextProvider.java → ContextProvider.kt} +8 -8
  25. package/android/src/reactnative/java/io/invertase/firebase/interfaces/{NativeError.java → NativeError.kt} +8 -8
  26. package/android/src/reactnative/java/io/invertase/firebase/interfaces/{NativeEvent.java → NativeEvent.kt} +6 -6
  27. package/android/src/reactnative/java/io/invertase/firebase/utils/NativeRNFBTurboUtils.kt +219 -0
  28. package/android/src/test/java/expo/core/ModuleRegistry.java +9 -0
  29. package/android/src/test/java/io/invertase/firebase/app/NativeRNFBTurboAppTest.java +182 -59
  30. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppComponentsJavaCompatibilityTest.java +70 -0
  31. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppJavaCompatibilityTest.java +60 -0
  32. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackageTest.java +165 -0
  33. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppTest.kt +167 -0
  34. package/android/src/test/java/io/invertase/firebase/common/RCTConvertFirebaseJavaCompatibilityTest.java +112 -0
  35. package/android/src/test/java/io/invertase/firebase/common/RCTConvertFirebaseTest.kt +317 -0
  36. package/android/src/test/java/io/invertase/firebase/common/RNFBHandleMapTest.kt +309 -0
  37. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterJavaCompatibilityTest.java +176 -0
  38. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterTest.java +143 -12
  39. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventTest.kt +61 -0
  40. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONJavaCompatibilityTest.java +85 -0
  41. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONTest.kt +296 -0
  42. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseMetaJavaCompatibilityTest.java +67 -0
  43. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseMetaTest.kt +246 -0
  44. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseModuleTest.kt +254 -0
  45. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebasePreferencesJavaCompatibilityTest.java +87 -0
  46. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebasePreferencesTest.kt +260 -0
  47. package/android/src/test/java/io/invertase/firebase/common/SharedUtilsJavaCompatibilityTest.java +255 -0
  48. package/android/src/test/java/io/invertase/firebase/common/SharedUtilsTest.kt +674 -0
  49. package/android/src/test/java/io/invertase/firebase/common/TaskExecutorServiceTest.kt +290 -0
  50. package/android/src/test/java/io/invertase/firebase/common/UniversalFirebaseModuleJavaCompatibilityTest.java +178 -0
  51. package/android/src/test/java/io/invertase/firebase/common/UniversalFirebasePreferencesJavaCompatibilityTest.java +99 -0
  52. package/android/src/test/java/io/invertase/firebase/common/UniversalFirebasePreferencesTest.kt +217 -0
  53. package/android/src/test/java/io/invertase/firebase/commoncompat/ReactNativeFirebaseModuleJavaCompatibilityTest.java +176 -0
  54. package/android/src/test/java/io/invertase/firebase/commoncompat/ReactNativeFirebaseModuleKotlinCompatibilityTest.kt +52 -0
  55. package/android/src/test/java/io/invertase/firebase/interfaces/InterfacesJavaCompatibilityTest.java +144 -0
  56. package/android/src/test/java/io/invertase/firebase/interfaces/NativeEventTest.kt +56 -0
  57. package/android/src/test/java/io/invertase/firebase/utils/NativeRNFBTurboUtilsTest.java +517 -21
  58. package/dist/module/FirebaseApp.js +10 -0
  59. package/dist/module/FirebaseApp.js.map +1 -1
  60. package/dist/module/common/deeps.js +3 -0
  61. package/dist/module/common/deeps.js.map +1 -1
  62. package/dist/module/internal/FirebaseModule.js.map +1 -1
  63. package/dist/module/internal/RNFBNativeEventEmitter.js +12 -1
  64. package/dist/module/internal/RNFBNativeEventEmitter.js.map +1 -1
  65. package/dist/module/internal/SharedEventEmitter.js +3 -1
  66. package/dist/module/internal/SharedEventEmitter.js.map +1 -1
  67. package/dist/module/internal/index.js +1 -0
  68. package/dist/module/internal/index.js.map +1 -1
  69. package/dist/module/internal/registry/app.js +7 -1
  70. package/dist/module/internal/registry/app.js.map +1 -1
  71. package/dist/module/types/app.js.map +1 -1
  72. package/dist/module/types/internal.js +2 -0
  73. package/dist/module/version.js +1 -1
  74. package/dist/typescript/lib/FirebaseApp.d.ts +7 -0
  75. package/dist/typescript/lib/FirebaseApp.d.ts.map +1 -1
  76. package/dist/typescript/lib/common/deeps.d.ts.map +1 -1
  77. package/dist/typescript/lib/internal/FirebaseModule.d.ts +2 -3
  78. package/dist/typescript/lib/internal/FirebaseModule.d.ts.map +1 -1
  79. package/dist/typescript/lib/internal/NativeModules.d.ts +4 -1
  80. package/dist/typescript/lib/internal/NativeModules.d.ts.map +1 -1
  81. package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts +2 -2
  82. package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts.map +1 -1
  83. package/dist/typescript/lib/internal/SharedEventEmitter.d.ts +3 -3
  84. package/dist/typescript/lib/internal/SharedEventEmitter.d.ts.map +1 -1
  85. package/dist/typescript/lib/internal/index.d.ts +1 -0
  86. package/dist/typescript/lib/internal/index.d.ts.map +1 -1
  87. package/dist/typescript/lib/internal/registry/app.d.ts.map +1 -1
  88. package/dist/typescript/lib/types/app.d.ts +10 -3
  89. package/dist/typescript/lib/types/app.d.ts.map +1 -1
  90. package/dist/typescript/lib/types/internal.d.ts +12 -0
  91. package/dist/typescript/lib/types/internal.d.ts.map +1 -1
  92. package/dist/typescript/lib/version.d.ts +1 -1
  93. package/firebase-schema.json +4 -0
  94. package/firebase_spm.rb +552 -96
  95. package/ios/RNFBApp/RCTConvert+FIRApp.h +2 -5
  96. package/ios/RNFBApp/RCTConvert+FIRApp.m +11 -6
  97. package/ios/RNFBApp/RCTConvert+FIROptions.h +2 -5
  98. package/ios/RNFBApp/RCTConvert+FIROptions.m +11 -10
  99. package/ios/RNFBApp/RCTConvertFIRApp.swift +86 -0
  100. package/ios/RNFBApp/RCTConvertFIROptions.swift +259 -0
  101. package/ios/RNFBApp/RNFBAppCustomAuthDomains.swift +63 -0
  102. package/ios/RNFBApp/RNFBAppInitializeOptionsMapper.swift +113 -0
  103. package/ios/RNFBApp/RNFBAppLogLevelMapper.swift +53 -0
  104. package/ios/RNFBApp/RNFBAppModule+CustomAuthDomain.m +35 -0
  105. package/ios/RNFBApp/RNFBAppModule.h +6 -2
  106. package/ios/RNFBApp/RNFBAppModule.mm +65 -228
  107. package/ios/RNFBApp/RNFBAppModuleFirebase.swift +439 -0
  108. package/ios/RNFBApp/RNFBAppModuleImplementation.h +68 -0
  109. package/ios/RNFBApp/RNFBAppModuleImplementation.m +195 -0
  110. package/ios/RNFBApp/RNFBHandleMap.m +27 -56
  111. package/ios/RNFBApp/RNFBHandleMapStorage.swift +90 -0
  112. package/ios/RNFBApp/RNFBInitializeAppArguments.swift +82 -0
  113. package/ios/RNFBApp/RNFBJSON.m +22 -38
  114. package/ios/RNFBApp/RNFBJSONImplementation.swift +71 -0
  115. package/ios/RNFBApp/RNFBMeta.m +24 -26
  116. package/ios/RNFBApp/RNFBMetaStorage.swift +76 -0
  117. package/ios/RNFBApp/RNFBNullSentinelDecoding.swift +135 -0
  118. package/ios/RNFBApp/RNFBNullSentinelInterceptor.h +30 -3
  119. package/ios/RNFBApp/RNFBNullSentinelInterceptor.m +28 -5
  120. package/ios/RNFBApp/RNFBPreferences.m +22 -20
  121. package/ios/RNFBApp/RNFBPreferencesStorage.swift +101 -0
  122. package/ios/RNFBApp/RNFBRCTEventEmitter.m +36 -93
  123. package/ios/RNFBApp/RNFBRCTEventEmitterCore.swift +161 -0
  124. package/ios/RNFBApp/RNFBSharedUtils.h +2 -5
  125. package/ios/RNFBApp/RNFBSharedUtils.m +109 -198
  126. package/ios/RNFBApp/RNFBSharedUtilsConfig.swift +72 -0
  127. package/ios/RNFBApp/RNFBSharedUtilsFIRApp.swift +271 -0
  128. package/ios/RNFBApp/RNFBSharedUtilsFormatting.swift +59 -0
  129. package/ios/RNFBApp/RNFBSharedUtilsPromiseRejection.swift +88 -0
  130. package/ios/RNFBApp/RNFBUtilsHelpers.swift +116 -0
  131. package/ios/RNFBApp/RNFBUtilsModule+Foundation.m +44 -0
  132. package/ios/RNFBApp/RNFBUtilsModule+PhotoAssets.m +47 -0
  133. package/ios/RNFBApp/RNFBUtilsModule.h +11 -1
  134. package/ios/RNFBApp/RNFBUtilsModule.mm +10 -85
  135. package/ios/RNFBApp/RNFBUtilsModuleImplementation.h +32 -0
  136. package/ios/RNFBApp/RNFBUtilsModuleImplementation.m +56 -0
  137. package/ios/RNFBApp/RNFBVersion.m +1 -1
  138. package/ios/RNFBApp.xcodeproj/project.pbxproj +100 -0
  139. package/ios/RNFBAppUnitTests/HostStubs/FirebaseCore/FIRApp.m +171 -0
  140. package/ios/RNFBAppUnitTests/HostStubs/FirebaseCore/FirebaseCore.h +78 -0
  141. package/ios/RNFBAppUnitTests/HostStubs/FirebaseInstallations/FIRInstallations.m +7 -0
  142. package/ios/RNFBAppUnitTests/HostStubs/FirebaseInstallations/FirebaseInstallations.h +8 -0
  143. package/ios/RNFBAppUnitTests/HostStubs/RNFBAppModuleHostStub.m +55 -0
  144. package/ios/RNFBAppUnitTests/HostStubs/RNFBAppTurboModules.h +89 -0
  145. package/ios/RNFBAppUnitTests/HostStubs/RNFBAppTurboModules.mm +30 -0
  146. package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridge.h +19 -0
  147. package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridge.m +32 -0
  148. package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridgeModule.h +19 -0
  149. package/ios/RNFBAppUnitTests/HostStubs/React/RCTConvert.h +28 -0
  150. package/ios/RNFBAppUnitTests/HostStubs/React/RCTConvert.m +23 -0
  151. package/ios/RNFBAppUnitTests/HostStubs/React/RCTEventEmitter.h +9 -0
  152. package/ios/RNFBAppUnitTests/HostStubs/React/RCTInvalidating.h +24 -0
  153. package/ios/RNFBAppUnitTests/HostStubs/React/RCTUtils.h +9 -0
  154. package/ios/RNFBAppUnitTests/HostStubs/module.modulemap +13 -0
  155. package/ios/RNFBAppUnitTests/RCTConvertFIRAppFacadeTests.m +61 -0
  156. package/ios/RNFBAppUnitTests/RCTConvertFIRAppTests.swift +73 -0
  157. package/ios/RNFBAppUnitTests/RCTConvertFIROptionsFacadeTests.m +57 -0
  158. package/ios/RNFBAppUnitTests/RCTConvertFIROptionsTests.swift +243 -0
  159. package/ios/RNFBAppUnitTests/RNFBAppCustomAuthDomainsFacadeTests.m +73 -0
  160. package/ios/RNFBAppUnitTests/RNFBAppCustomAuthDomainsTests.swift +70 -0
  161. package/ios/RNFBAppUnitTests/RNFBAppInitializeOptionsMapperTests.swift +190 -0
  162. package/ios/RNFBAppUnitTests/RNFBAppLogLevelFacadeTests.m +76 -0
  163. package/ios/RNFBAppUnitTests/RNFBAppLogLevelMapperTests.swift +59 -0
  164. package/ios/RNFBAppUnitTests/RNFBAppModuleFirebaseFacadeTests.m +215 -0
  165. package/ios/RNFBAppUnitTests/RNFBAppModuleFirebaseTests.swift +430 -0
  166. package/ios/RNFBAppUnitTests/RNFBAppModuleLifecycleTests.m +206 -0
  167. package/ios/RNFBAppUnitTests/RNFBAppModuleShellTests.mm +367 -0
  168. package/ios/RNFBAppUnitTests/RNFBAppUnitTests.xcodeproj/project.pbxproj +355 -2
  169. package/ios/RNFBAppUnitTests/RNFBFirebaseAppClientTests.swift +165 -0
  170. package/ios/RNFBAppUnitTests/RNFBHandleMapStorageTests.swift +121 -0
  171. package/ios/RNFBAppUnitTests/RNFBInitializeAppArgumentsTests.swift +256 -0
  172. package/ios/RNFBAppUnitTests/RNFBJSONImplementationTests.swift +126 -0
  173. package/ios/RNFBAppUnitTests/RNFBJSONTests.m +172 -0
  174. package/ios/RNFBAppUnitTests/RNFBMetaStorageTests.swift +106 -0
  175. package/ios/RNFBAppUnitTests/RNFBMetaTests.m +65 -0
  176. package/ios/RNFBAppUnitTests/RNFBNullSentinelDecodingTests.swift +286 -0
  177. package/ios/RNFBAppUnitTests/RNFBNullSentinelInterceptorTests.m +312 -0
  178. package/ios/RNFBAppUnitTests/RNFBPreferencesStorageTests.swift +105 -0
  179. package/ios/RNFBAppUnitTests/RNFBPreferencesTests.m +110 -0
  180. package/ios/RNFBAppUnitTests/RNFBRCTEventEmitterCoreTests.swift +163 -0
  181. package/ios/RNFBAppUnitTests/RNFBRCTEventEmitterFacadeTests.m +141 -0
  182. package/ios/RNFBAppUnitTests/RNFBSharedUtilsConfigFacadeTests.m +100 -0
  183. package/ios/RNFBAppUnitTests/RNFBSharedUtilsConfigTests.swift +234 -0
  184. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFIRAppFacadeTests.m +145 -0
  185. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFIRAppTests.swift +173 -0
  186. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFormattingFacadeTests.m +38 -0
  187. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFormattingTests.swift +82 -0
  188. package/ios/RNFBAppUnitTests/RNFBSharedUtilsNullSentinelTests.m +35 -0
  189. package/ios/RNFBAppUnitTests/RNFBSharedUtilsPromiseRejectionFacadeTests.m +107 -0
  190. package/ios/RNFBAppUnitTests/RNFBSharedUtilsPromiseRejectionTests.swift +133 -0
  191. package/ios/RNFBAppUnitTests/RNFBUtilsHelpersTests.swift +144 -0
  192. package/ios/RNFBAppUnitTests/RNFBUtilsModuleFacadeTests.m +72 -0
  193. package/ios/RNFBAppUnitTests/RNFBUtilsModuleShellTests.mm +282 -0
  194. package/ios/RNFBFirebase/Package.swift +53 -0
  195. package/ios/RNFBFirebase/Sources/RNFBFirebase/RNFBFirebase.swift +285 -0
  196. package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/Public/.gitkeep +0 -0
  197. package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/RNFBFirebaseBridge.m +169 -0
  198. package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/include/RNFBFirebaseBridge.h +52 -0
  199. package/ios/RNFBFirebaseUnitTests/RNFBAppDynamicFacadeTests.swift +240 -0
  200. package/{android/src/main/java/io/invertase/firebase/common/RNFBHandleCollisionException.java → ios/RNFBFirebaseUnitTests/RNFBFirebaseClientImport.swift} +23 -14
  201. package/ios/RNFBFirebaseUnitTests/RNFBFirebaseUnitTests.xcodeproj/project.pbxproj +588 -0
  202. package/ios/RNFBFirebaseUnitTests/RNFBFirebaseUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBFirebaseUnitTests.xcscheme +83 -0
  203. package/ios_config.sh +50 -22
  204. package/lib/FirebaseApp.ts +10 -0
  205. package/lib/common/deeps.ts +3 -0
  206. package/lib/internal/FirebaseModule.ts +6 -3
  207. package/lib/internal/NativeModules.ts +4 -1
  208. package/lib/internal/RNFBNativeEventEmitter.ts +14 -5
  209. package/lib/internal/SharedEventEmitter.ts +5 -1
  210. package/lib/internal/index.ts +1 -0
  211. package/lib/internal/react-native-vendor-emitter.d.ts +11 -0
  212. package/lib/internal/registry/app.ts +7 -1
  213. package/lib/types/app.ts +10 -3
  214. package/lib/types/internal.ts +19 -0
  215. package/lib/version.ts +1 -1
  216. package/package.json +8 -8
  217. package/plugin/build/ios/appDelegate.js +2 -2
  218. package/plugin/build/ios/podfile.js +16 -16
  219. package/plugin/build/pluginConfig.d.ts +4 -2
  220. package/plugin/src/ios/appDelegate.ts +2 -2
  221. package/plugin/src/ios/podfile.ts +20 -23
  222. package/plugin/src/pluginConfig.ts +4 -2
  223. package/android/src/main/java/io/invertase/firebase/common/RNFBHandleMap.java +0 -150
  224. package/android/src/main/java/io/invertase/firebase/common/TaskExecutorService.java +0 -148
  225. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebaseModule.java +0 -62
  226. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.java +0 -91
  227. package/android/src/reactnative/java/io/invertase/firebase/app/NativeRNFBTurboApp.java +0 -224
  228. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseApp.java +0 -46
  229. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.java +0 -49
  230. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppRegistrar.java +0 -35
  231. package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.java +0 -192
  232. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEvent.java +0 -53
  233. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.java +0 -606
  234. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseJSON.java +0 -109
  235. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.java +0 -101
  236. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.java +0 -146
  237. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.java +0 -96
  238. package/android/src/reactnative/java/io/invertase/firebase/common/SharedUtils.java +0 -441
  239. package/android/src/reactnative/java/io/invertase/firebase/utils/NativeRNFBTurboUtils.java +0 -223
  240. package/android/src/test/java/io/invertase/firebase/common/RNFBHandleMapTest.java +0 -275
  241. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONTest.java +0 -108
  242. package/android/src/test/java/io/invertase/firebase/common/TaskExecutorServiceTest.java +0 -97
@@ -0,0 +1,317 @@
1
+ package io.invertase.firebase.common
2
+
3
+ /*
4
+ * Copyright (c) 2016-present Invertase Limited & Contributors
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this library except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ import android.content.Context
20
+ import android.util.Log
21
+ import com.facebook.react.bridge.Arguments
22
+ import com.facebook.react.bridge.ReadableArray
23
+ import com.facebook.react.bridge.ReadableMap
24
+ import com.facebook.react.bridge.WritableMap
25
+ import com.google.firebase.FirebaseApp
26
+ import com.google.firebase.FirebaseOptions
27
+ import io.invertase.firebase.app.NativeRNFBTurboApp
28
+ import org.json.JSONObject
29
+ import org.junit.After
30
+ import org.junit.Assert.assertEquals
31
+ import org.junit.Assert.assertFalse
32
+ import org.junit.Assert.assertNull
33
+ import org.junit.Assert.assertSame
34
+ import org.junit.Assert.assertTrue
35
+ import org.junit.Test
36
+ import org.mockito.ArgumentCaptor
37
+ import org.mockito.Mockito.mock
38
+ import org.mockito.Mockito.mockStatic
39
+ import org.mockito.Mockito.never
40
+ import org.mockito.Mockito.verify
41
+ import org.mockito.Mockito.`when`
42
+ import org.robolectric.RobolectricTestRunner
43
+ import java.util.ArrayList
44
+ import java.util.HashMap
45
+
46
+ @org.junit.runner.RunWith(RobolectricTestRunner::class)
47
+ class RCTConvertFirebaseTest {
48
+ private val touchedAuthDomains = mutableMapOf<String, String?>()
49
+
50
+ @After
51
+ fun tearDown() {
52
+ touchedAuthDomains.forEach { (name, original) ->
53
+ if (original == null) {
54
+ NativeRNFBTurboApp.authDomains.remove(name)
55
+ } else {
56
+ NativeRNFBTurboApp.authDomains[name] = original
57
+ }
58
+ }
59
+ }
60
+
61
+ @Test
62
+ fun firebaseAppToMap_returnsExactMutableStructureWithOptionalAuthDomain() {
63
+ val firebaseApp = mock(FirebaseApp::class.java)
64
+ val options = mock(FirebaseOptions::class.java)
65
+ `when`(firebaseApp.name).thenReturn("secondary")
66
+ `when`(firebaseApp.options).thenReturn(options)
67
+ `when`(firebaseApp.isDataCollectionDefaultEnabled).thenReturn(false)
68
+ `when`(options.apiKey).thenReturn("api-key")
69
+ `when`(options.applicationId).thenReturn("app-id")
70
+ `when`(options.projectId).thenReturn("project-id")
71
+ `when`(options.databaseUrl).thenReturn("database-url")
72
+ `when`(options.gaTrackingId).thenReturn("measurement-id")
73
+ `when`(options.gcmSenderId).thenReturn("sender-id")
74
+ `when`(options.storageBucket).thenReturn("storage-bucket")
75
+ rememberAuthDomain("secondary")
76
+ NativeRNFBTurboApp.authDomains["secondary"] = "auth.example"
77
+
78
+ val result = RCTConvertFirebase.firebaseAppToMap(firebaseApp)
79
+
80
+ assertTrue(result is HashMap)
81
+ val appConfig = result["appConfig"] as Map<*, *>
82
+ val convertedOptions = result["options"] as Map<*, *>
83
+ assertTrue(appConfig is HashMap)
84
+ assertTrue(convertedOptions is HashMap)
85
+ assertEquals(
86
+ mapOf("name" to "secondary", "automaticDataCollectionEnabled" to false),
87
+ appConfig,
88
+ )
89
+ assertEquals(
90
+ mapOf(
91
+ "apiKey" to "api-key",
92
+ "appId" to "app-id",
93
+ "projectId" to "project-id",
94
+ "databaseURL" to "database-url",
95
+ "measurementId" to "measurement-id",
96
+ "messagingSenderId" to "sender-id",
97
+ "storageBucket" to "storage-bucket",
98
+ "authDomain" to "auth.example",
99
+ ),
100
+ convertedOptions,
101
+ )
102
+ (result as MutableMap)["later"] = true
103
+ (appConfig as MutableMap<String, Any>)["later"] = true
104
+ (convertedOptions as MutableMap<String, Any>)["later"] = true
105
+ }
106
+
107
+ @Test
108
+ fun firebaseAppToMap_omitsMissingAuthDomainAndWritableConversionUsesSameMap() {
109
+ val firebaseApp = mock(FirebaseApp::class.java)
110
+ val options = mock(FirebaseOptions::class.java)
111
+ `when`(firebaseApp.name).thenReturn("[DEFAULT]")
112
+ `when`(firebaseApp.options).thenReturn(options)
113
+ rememberAuthDomain("[DEFAULT]")
114
+ NativeRNFBTurboApp.authDomains.remove("[DEFAULT]")
115
+
116
+ val converted = RCTConvertFirebase.firebaseAppToMap(firebaseApp)
117
+
118
+ assertFalse((converted["options"] as Map<*, *>).containsKey("authDomain"))
119
+ mockStatic(Arguments::class.java).use { arguments ->
120
+ assertNull(RCTConvertFirebase.firebaseAppToWritableMap(firebaseApp))
121
+ arguments.verify {
122
+ Arguments.makeNativeMap(org.mockito.ArgumentMatchers.anyMap<String, Any?>())
123
+ }
124
+ }
125
+ }
126
+
127
+ @Test
128
+ fun readableMapToFirebaseApp_buildsDefaultAppAndAppliesOptionalConfiguration() {
129
+ val options = completeOptions("measurement-id")
130
+ val appConfig = mock(ReadableMap::class.java)
131
+ val context = mock(Context::class.java)
132
+ val firebaseApp = mock(FirebaseApp::class.java)
133
+ `when`(appConfig.getString("name")).thenReturn("[DEFAULT]")
134
+ `when`(appConfig.hasKey("automaticDataCollectionEnabled")).thenReturn(true)
135
+ `when`(appConfig.getBoolean("automaticDataCollectionEnabled")).thenReturn(false)
136
+ `when`(appConfig.hasKey("automaticResourceManagement")).thenReturn(true)
137
+ `when`(appConfig.getBoolean("automaticResourceManagement")).thenReturn(true)
138
+
139
+ mockStatic(FirebaseApp::class.java).use { firebaseApps ->
140
+ firebaseApps
141
+ .`when`<FirebaseApp> {
142
+ FirebaseApp.initializeApp(
143
+ org.mockito.ArgumentMatchers.eq(context),
144
+ org.mockito.ArgumentMatchers.any(FirebaseOptions::class.java),
145
+ )
146
+ }.thenReturn(firebaseApp)
147
+
148
+ assertSame(firebaseApp, RCTConvertFirebase.readableMapToFirebaseApp(options, appConfig, context))
149
+
150
+ val captor = ArgumentCaptor.forClass(FirebaseOptions::class.java)
151
+ firebaseApps.verify { FirebaseApp.initializeApp(org.mockito.ArgumentMatchers.eq(context), captor.capture()) }
152
+ assertOptions(captor.value, "measurement-id")
153
+ verify(firebaseApp).setDataCollectionDefaultEnabled(false)
154
+ verify(firebaseApp).setAutomaticResourceManagementEnabled(true)
155
+ }
156
+ }
157
+
158
+ @Test
159
+ fun readableMapToFirebaseApp_buildsNamedAppWithoutAbsentOptionalConfiguration() {
160
+ val options = completeOptions(null)
161
+ val appConfig = mock(ReadableMap::class.java)
162
+ val context = mock(Context::class.java)
163
+ val firebaseApp = mock(FirebaseApp::class.java)
164
+ `when`(appConfig.getString("name")).thenReturn("secondary")
165
+
166
+ mockStatic(FirebaseApp::class.java).use { firebaseApps ->
167
+ firebaseApps
168
+ .`when`<FirebaseApp> {
169
+ FirebaseApp.initializeApp(
170
+ org.mockito.ArgumentMatchers.eq(context),
171
+ org.mockito.ArgumentMatchers.any(FirebaseOptions::class.java),
172
+ org.mockito.ArgumentMatchers.eq("secondary"),
173
+ )
174
+ }.thenReturn(firebaseApp)
175
+
176
+ assertSame(firebaseApp, RCTConvertFirebase.readableMapToFirebaseApp(options, appConfig, context))
177
+
178
+ val captor = ArgumentCaptor.forClass(FirebaseOptions::class.java)
179
+ firebaseApps.verify {
180
+ FirebaseApp.initializeApp(
181
+ org.mockito.ArgumentMatchers.eq(context),
182
+ captor.capture(),
183
+ org.mockito.ArgumentMatchers.eq("secondary"),
184
+ )
185
+ }
186
+ assertOptions(captor.value, null)
187
+ assertFalse(
188
+ org.mockito.Mockito
189
+ .mockingDetails(firebaseApp)
190
+ .invocations
191
+ .any { it.method.name == "setDataCollectionDefaultEnabled" },
192
+ )
193
+ verify(firebaseApp, never())
194
+ .setAutomaticResourceManagementEnabled(org.mockito.ArgumentMatchers.anyBoolean())
195
+ }
196
+ }
197
+
198
+ @Test
199
+ fun mapPutValue_writesEveryScalarAndJsonNullAndReturnsTargetIdentity() {
200
+ val map = mock(WritableMap::class.java)
201
+
202
+ assertSame(map, RCTConvertFirebase.mapPutValue("null", null, map))
203
+ assertSame(map, RCTConvertFirebase.mapPutValue("boolean", true, map))
204
+ assertSame(map, RCTConvertFirebase.mapPutValue("long", 4_294_967_296L, map))
205
+ assertSame(map, RCTConvertFirebase.mapPutValue("float", 1.25F, map))
206
+ assertSame(map, RCTConvertFirebase.mapPutValue("double", 2.5, map))
207
+ assertSame(map, RCTConvertFirebase.mapPutValue("integer", 42, map))
208
+ assertSame(map, RCTConvertFirebase.mapPutValue("string", "value", map))
209
+ assertSame(map, RCTConvertFirebase.mapPutValue("jsonNull", JSONObject.NULL, map))
210
+
211
+ verify(map).putNull("null")
212
+ verify(map).putBoolean("boolean", true)
213
+ verify(map).putDouble("long", 4_294_967_296.0)
214
+ verify(map).putDouble("float", 1.25)
215
+ verify(map).putDouble("double", 2.5)
216
+ verify(map).putInt("integer", 42)
217
+ verify(map).putString("string", "value")
218
+ verify(map).putString("jsonNull", "null")
219
+ }
220
+
221
+ @Test
222
+ fun mapPutValue_writesListsAndRecursiveMapsWithoutChangingInputs() {
223
+ val map = mock(WritableMap::class.java)
224
+ val childMap = mock(WritableMap::class.java)
225
+ val list = arrayListOf<Any>("item")
226
+ val nested = linkedMapOf<String, Any?>("boolean" to true, "null" to null)
227
+
228
+ mockStatic(Arguments::class.java).use { arguments ->
229
+ arguments.`when`<WritableMap>(Arguments::createMap).thenReturn(childMap)
230
+
231
+ assertSame(map, RCTConvertFirebase.mapPutValue("list", list, map))
232
+ assertSame(map, RCTConvertFirebase.mapPutValue("map", nested, map))
233
+ arguments.verify { Arguments.makeNativeArray(list) }
234
+ }
235
+
236
+ verify(map).putArray("list", null)
237
+ verify(childMap).putBoolean("boolean", true)
238
+ verify(childMap).putNull("null")
239
+ verify(map).putMap("map", childMap)
240
+ assertEquals(arrayListOf<Any>("item"), list)
241
+ assertEquals(linkedMapOf<String, Any?>("boolean" to true, "null" to null), nested)
242
+ }
243
+
244
+ @Test
245
+ fun mapPutValue_logsUnknownTypeAndWritesNull() {
246
+ val map = mock(WritableMap::class.java)
247
+ val unknown = Any()
248
+
249
+ mockStatic(Log::class.java).use { log ->
250
+ assertSame(map, RCTConvertFirebase.mapPutValue("unknown", unknown, map))
251
+ log.verify {
252
+ Log.d(
253
+ "RCTConvertFirebase",
254
+ "utils:mapPutValue:unknownType:${unknown.javaClass.name}",
255
+ )
256
+ }
257
+ }
258
+ verify(map).putNull("unknown")
259
+ }
260
+
261
+ @Test
262
+ fun readableDelegates_preserveReturnedMutableCollectionIdentity() {
263
+ val readableMap = mock(ReadableMap::class.java)
264
+ val writableMap = mock(WritableMap::class.java)
265
+ val hashMap = HashMap<String, Any?>()
266
+ val readableArray = mock(ReadableArray::class.java)
267
+ val arrayList = ArrayList<Any?>()
268
+ `when`(readableMap.toHashMap()).thenReturn(hashMap)
269
+ `when`(readableArray.toArrayList()).thenReturn(arrayList)
270
+
271
+ mockStatic(Arguments::class.java).use { arguments ->
272
+ arguments.`when`<WritableMap>(Arguments::createMap).thenReturn(writableMap)
273
+ assertSame(writableMap, RCTConvertFirebase.readableMapToWritableMap(readableMap))
274
+ }
275
+ verify(writableMap).merge(readableMap)
276
+ assertSame(hashMap, RCTConvertFirebase.toHashMap(readableMap))
277
+ assertSame(arrayList, RCTConvertFirebase.toArrayList(readableArray))
278
+ hashMap["later"] = true
279
+ arrayList.add("later")
280
+ }
281
+
282
+ private fun completeOptions(measurementId: String?): ReadableMap {
283
+ val options = mock(ReadableMap::class.java)
284
+ val values =
285
+ mapOf(
286
+ "apiKey" to "api-key",
287
+ "appId" to "app-id",
288
+ "projectId" to "project-id",
289
+ "databaseURL" to "database-url",
290
+ "storageBucket" to "storage-bucket",
291
+ "messagingSenderId" to "sender-id",
292
+ )
293
+ values.forEach { (key, value) -> `when`(options.getString(key)).thenReturn(value) }
294
+ `when`(options.hasKey("measurementId")).thenReturn(measurementId != null)
295
+ if (measurementId != null) {
296
+ `when`(options.getString("measurementId")).thenReturn(measurementId)
297
+ }
298
+ return options
299
+ }
300
+
301
+ private fun assertOptions(
302
+ options: FirebaseOptions,
303
+ measurementId: String?,
304
+ ) {
305
+ assertEquals("api-key", options.apiKey)
306
+ assertEquals("app-id", options.applicationId)
307
+ assertEquals("project-id", options.projectId)
308
+ assertEquals("database-url", options.databaseUrl)
309
+ assertEquals(measurementId, options.gaTrackingId)
310
+ assertEquals("storage-bucket", options.storageBucket)
311
+ assertEquals("sender-id", options.gcmSenderId)
312
+ }
313
+
314
+ private fun rememberAuthDomain(name: String) {
315
+ touchedAuthDomains.putIfAbsent(name, NativeRNFBTurboApp.authDomains[name])
316
+ }
317
+ }
@@ -0,0 +1,309 @@
1
+ package io.invertase.firebase.common
2
+
3
+ /*
4
+ * Copyright (c) 2016-present Invertase Limited & Contributors
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this library except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ */
19
+
20
+ import org.junit.Assert.assertEquals
21
+ import org.junit.Assert.assertFalse
22
+ import org.junit.Assert.assertNull
23
+ import org.junit.Assert.assertSame
24
+ import org.junit.Assert.assertTrue
25
+ import org.junit.Assert.fail
26
+ import org.junit.Before
27
+ import org.junit.Test
28
+ import java.util.concurrent.CountDownLatch
29
+ import java.util.concurrent.TimeUnit
30
+ import java.util.concurrent.atomic.AtomicReference
31
+
32
+ /**
33
+ * JVM coverage for [RNFBHandleMap]. Tests must not invoke methods on stored values — the map only
34
+ * moves pointers.
35
+ */
36
+ class RNFBHandleMapTest {
37
+ private lateinit var map: RNFBHandleMap<Int, Any>
38
+
39
+ @Before
40
+ fun setUp() {
41
+ map = RNFBHandleMap()
42
+ }
43
+
44
+ @Test
45
+ fun putGetTake_happyPath() {
46
+ val handle = Any()
47
+ map.put(1, handle)
48
+
49
+ assertSame(handle, map.get(1))
50
+ assertSame(handle, map.take(1))
51
+ assertNull(map.get(1))
52
+ }
53
+
54
+ @Test
55
+ fun take_missingKey_returnsNull() {
56
+ assertNull(map.take(99))
57
+ assertNull(map.get(99))
58
+ }
59
+
60
+ @Test
61
+ fun takeAll_returnsSnapshotAndLeavesMapEmpty() {
62
+ val a = Any()
63
+ val b = Any()
64
+ map.put(1, a)
65
+ map.put(2, b)
66
+
67
+ val snapshot = map.takeAll()
68
+
69
+ assertEquals(2, snapshot.size)
70
+ assertTrue(snapshot.contains(a))
71
+ assertTrue(snapshot.contains(b))
72
+ assertNull(map.get(1))
73
+ assertNull(map.get(2))
74
+ assertTrue(map.takeAll().isEmpty())
75
+ }
76
+
77
+ @Test
78
+ fun put_occupiedId_throwsCollision() {
79
+ val first = Any()
80
+ map.put(1, first)
81
+ try {
82
+ map.put(1, Any())
83
+ fail("expected RNFBHandleCollisionException")
84
+ } catch (exception: RNFBHandleCollisionException) {
85
+ assertTrue(exception.message!!.contains("1"))
86
+ assertSame(first, map.get(1))
87
+ }
88
+ }
89
+
90
+ @Test
91
+ fun put_nullValue_stillTreatsIdAsOccupied() {
92
+ val nullableMap = RNFBHandleMap<Int, Any?>()
93
+ nullableMap.put(1, null)
94
+
95
+ try {
96
+ nullableMap.put(1, Any())
97
+ fail("expected RNFBHandleCollisionException")
98
+ } catch (exception: RNFBHandleCollisionException) {
99
+ assertEquals("Handle id already registered: 1", exception.message)
100
+ assertNull(nullableMap.get(1))
101
+ }
102
+ }
103
+
104
+ @Test
105
+ fun putIfAbsent_whenFree_storesAndReturnsTrue() {
106
+ val handle = Any()
107
+ assertTrue(map.putIfAbsent(1, handle))
108
+ assertSame(handle, map.get(1))
109
+ }
110
+
111
+ @Test
112
+ fun putIfAbsent_whenOccupied_keepsExistingAndReturnsFalse() {
113
+ val first = Any()
114
+ val second = Any()
115
+ map.put(1, first)
116
+ assertFalse(map.putIfAbsent(1, second))
117
+ assertSame(first, map.get(1))
118
+ }
119
+
120
+ @Test
121
+ fun putIfAbsent_nullableExistingValue_isOccupied() {
122
+ val nullableMap = RNFBHandleMap<Int, Any?>()
123
+ nullableMap.put(1, null)
124
+
125
+ assertFalse(nullableMap.putIfAbsent(1, Any()))
126
+ assertNull(nullableMap.get(1))
127
+ }
128
+
129
+ @Test
130
+ fun putIfAbsentOrSame_whenAbsent_storesAndReturnsTrue() {
131
+ val handle = Any()
132
+ assertTrue(map.putIfAbsentOrSame(1, handle))
133
+ assertSame(handle, map.get(1))
134
+ }
135
+
136
+ @Test
137
+ fun putIfAbsentOrSame_whenSameInstance_returnsTrue() {
138
+ val handle = Any()
139
+ map.put(1, handle)
140
+ assertTrue(map.putIfAbsentOrSame(1, handle))
141
+ assertSame(handle, map.get(1))
142
+ }
143
+
144
+ @Test
145
+ fun putIfAbsentOrSame_whenDifferent_returnsFalse() {
146
+ val first = Any()
147
+ val second = Any()
148
+ map.put(1, first)
149
+ assertFalse(map.putIfAbsentOrSame(1, second))
150
+ assertSame(first, map.get(1))
151
+ }
152
+
153
+ @Test
154
+ fun putIfAbsentOrSame_sameNullInstance_returnsTrue() {
155
+ val nullableMap = RNFBHandleMap<Int, Any?>()
156
+ nullableMap.put(1, null)
157
+
158
+ assertTrue(nullableMap.putIfAbsentOrSame(1, null))
159
+ assertNull(nullableMap.get(1))
160
+ }
161
+
162
+ @Test
163
+ fun putIfAbsentOrSame_nullExistingAndDifferentHandle_returnsFalse() {
164
+ val nullableMap = RNFBHandleMap<Int, Any?>()
165
+ nullableMap.put(1, null)
166
+
167
+ assertFalse(nullableMap.putIfAbsentOrSame(1, Any()))
168
+ assertNull(nullableMap.get(1))
169
+ }
170
+
171
+ @Test
172
+ fun putReplacing_whenFree_storesAndReturnsNull() {
173
+ val handle = Any()
174
+ assertNull(map.putReplacing(1, handle))
175
+ assertSame(handle, map.get(1))
176
+ }
177
+
178
+ @Test
179
+ fun putReplacing_whenOccupied_replacesLastWins() {
180
+ val first = Any()
181
+ val second = Any()
182
+ map.put(1, first)
183
+ assertSame(first, map.putReplacing(1, second))
184
+ assertSame(second, map.get(1))
185
+ }
186
+
187
+ @Test
188
+ fun put_afterTake_allowsReuse() {
189
+ val first = Any()
190
+ val second = Any()
191
+ map.put(1, first)
192
+ assertSame(first, map.take(1))
193
+ map.put(1, second)
194
+ assertSame(second, map.get(1))
195
+ }
196
+
197
+ @Test
198
+ fun takeIf_whenPredicateTrue_removesAndReturns() {
199
+ val handle = Any()
200
+ map.put(1, handle)
201
+ assertSame(handle, map.takeIf(1) { it === handle })
202
+ assertNull(map.get(1))
203
+ }
204
+
205
+ @Test
206
+ fun takeIf_whenPredicateFalse_leavesMapping() {
207
+ val handle = Any()
208
+ map.put(1, handle)
209
+ assertNull(map.takeIf(1) { false })
210
+ assertSame(handle, map.get(1))
211
+ }
212
+
213
+ @Test
214
+ fun takeIf_whenMissing_returnsNull() {
215
+ assertNull(map.takeIf(99) { true })
216
+ }
217
+
218
+ @Test
219
+ fun take_sameIdFromTwoThreads_onlyOneReturnsNonNull() {
220
+ val handle = Any()
221
+ map.put(1, handle)
222
+
223
+ val start = CountDownLatch(1)
224
+ val done = CountDownLatch(2)
225
+ val first = AtomicReference<Any>()
226
+ val second = AtomicReference<Any>()
227
+
228
+ val firstThread =
229
+ Thread {
230
+ try {
231
+ start.await()
232
+ first.set(map.take(1))
233
+ } catch (_: InterruptedException) {
234
+ Thread.currentThread().interrupt()
235
+ } finally {
236
+ done.countDown()
237
+ }
238
+ }
239
+ val secondThread =
240
+ Thread {
241
+ try {
242
+ start.await()
243
+ second.set(map.take(1))
244
+ } catch (_: InterruptedException) {
245
+ Thread.currentThread().interrupt()
246
+ } finally {
247
+ done.countDown()
248
+ }
249
+ }
250
+
251
+ firstThread.start()
252
+ secondThread.start()
253
+ start.countDown()
254
+ assertTrue(done.await(5, TimeUnit.SECONDS))
255
+
256
+ val a = first.get()
257
+ val b = second.get()
258
+ val onlyFirst = a === handle && b == null
259
+ val onlySecond = b === handle && a == null
260
+ assertTrue(onlyFirst || onlySecond)
261
+ assertNull(map.get(1))
262
+ }
263
+
264
+ @Test
265
+ fun putReplacing_sameIdFromTwoThreads_lastWins() {
266
+ val first = Any()
267
+ val second = Any()
268
+ val third = Any()
269
+ map.put(1, first)
270
+
271
+ val start = CountDownLatch(1)
272
+ val done = CountDownLatch(2)
273
+ val previousA = AtomicReference<Any>()
274
+ val previousB = AtomicReference<Any>()
275
+
276
+ val firstThread =
277
+ Thread {
278
+ try {
279
+ start.await()
280
+ previousA.set(map.putReplacing(1, second))
281
+ } catch (_: InterruptedException) {
282
+ Thread.currentThread().interrupt()
283
+ } finally {
284
+ done.countDown()
285
+ }
286
+ }
287
+ val secondThread =
288
+ Thread {
289
+ try {
290
+ start.await()
291
+ previousB.set(map.putReplacing(1, third))
292
+ } catch (_: InterruptedException) {
293
+ Thread.currentThread().interrupt()
294
+ } finally {
295
+ done.countDown()
296
+ }
297
+ }
298
+
299
+ firstThread.start()
300
+ secondThread.start()
301
+ start.countDown()
302
+ assertTrue(done.await(5, TimeUnit.SECONDS))
303
+
304
+ val winner = map.get(1)
305
+ assertTrue(winner === second || winner === third)
306
+ val returnedFirst = if (previousA.get() === first) previousA.get() else previousB.get()
307
+ assertSame(first, returnedFirst)
308
+ }
309
+ }