@react-native-firebase/app 26.3.3 → 27.0.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.
- package/CHANGELOG.md +75 -0
- package/README.md +10 -27
- package/RNFBApp.podspec +13 -5
- package/android/build.gradle +3 -0
- package/android/src/main/java/io/invertase/firebase/common/RNFBHandleCollisionException.kt +29 -0
- package/android/src/main/java/io/invertase/firebase/common/RNFBHandleMap.kt +146 -0
- package/android/src/main/java/io/invertase/firebase/common/TaskExecutorService.kt +156 -0
- package/android/src/main/java/io/invertase/firebase/common/UniversalFirebaseModule.kt +44 -0
- package/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.kt +100 -0
- package/android/src/reactnative/java/io/invertase/firebase/app/NativeRNFBTurboApp.kt +250 -0
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseApp.kt +64 -0
- package/android/src/reactnative/java/io/invertase/firebase/app/{ReactNativeFirebaseAppInitProvider.java → ReactNativeFirebaseAppInitProvider.kt} +3 -4
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.kt +47 -0
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppRegistrar.kt +33 -0
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseVersion.java +1 -1
- package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.kt +185 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEvent.kt +35 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.kt +662 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseJSON.kt +103 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.kt +96 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.kt +140 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.kt +109 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/SharedUtils.kt +451 -0
- package/android/src/reactnative/java/io/invertase/firebase/interfaces/{ContextProvider.java → ContextProvider.kt} +8 -8
- package/android/src/reactnative/java/io/invertase/firebase/interfaces/{NativeError.java → NativeError.kt} +8 -8
- package/android/src/reactnative/java/io/invertase/firebase/interfaces/{NativeEvent.java → NativeEvent.kt} +6 -6
- package/android/src/reactnative/java/io/invertase/firebase/utils/NativeRNFBTurboUtils.kt +219 -0
- package/android/src/test/java/expo/core/ModuleRegistry.java +9 -0
- package/android/src/test/java/io/invertase/firebase/app/NativeRNFBTurboAppTest.java +208 -60
- package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppComponentsJavaCompatibilityTest.java +70 -0
- package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppJavaCompatibilityTest.java +60 -0
- package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackageTest.java +165 -0
- package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppTest.kt +167 -0
- package/android/src/test/java/io/invertase/firebase/common/RCTConvertFirebaseJavaCompatibilityTest.java +112 -0
- package/android/src/test/java/io/invertase/firebase/common/RCTConvertFirebaseTest.kt +317 -0
- package/android/src/test/java/io/invertase/firebase/common/RNFBHandleMapTest.kt +309 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterJavaCompatibilityTest.java +176 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterTest.java +144 -13
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventTest.kt +61 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONJavaCompatibilityTest.java +85 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONTest.kt +296 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseMetaJavaCompatibilityTest.java +67 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseMetaTest.kt +246 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseModuleTest.kt +254 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebasePreferencesJavaCompatibilityTest.java +87 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebasePreferencesTest.kt +260 -0
- package/android/src/test/java/io/invertase/firebase/common/SharedUtilsJavaCompatibilityTest.java +255 -0
- package/android/src/test/java/io/invertase/firebase/common/SharedUtilsTest.kt +674 -0
- package/android/src/test/java/io/invertase/firebase/common/TaskExecutorServiceTest.kt +290 -0
- package/android/src/test/java/io/invertase/firebase/common/UniversalFirebaseModuleJavaCompatibilityTest.java +178 -0
- package/android/src/test/java/io/invertase/firebase/common/UniversalFirebasePreferencesJavaCompatibilityTest.java +99 -0
- package/android/src/test/java/io/invertase/firebase/common/UniversalFirebasePreferencesTest.kt +217 -0
- package/android/src/test/java/io/invertase/firebase/commoncompat/ReactNativeFirebaseModuleJavaCompatibilityTest.java +176 -0
- package/android/src/test/java/io/invertase/firebase/commoncompat/ReactNativeFirebaseModuleKotlinCompatibilityTest.kt +52 -0
- package/android/src/test/java/io/invertase/firebase/interfaces/InterfacesJavaCompatibilityTest.java +144 -0
- package/android/src/test/java/io/invertase/firebase/interfaces/NativeEventTest.kt +56 -0
- package/android/src/test/java/io/invertase/firebase/utils/NativeRNFBTurboUtilsTest.java +517 -23
- package/dist/module/FirebaseApp.js +10 -0
- package/dist/module/FirebaseApp.js.map +1 -1
- package/dist/module/common/Base64.js +13 -21
- package/dist/module/common/Base64.js.map +1 -1
- package/dist/module/common/deeps.js +3 -0
- package/dist/module/common/deeps.js.map +1 -1
- package/dist/module/common/index.js +17 -9
- package/dist/module/common/index.js.map +1 -1
- package/dist/module/internal/FirebaseModule.js.map +1 -1
- package/dist/module/internal/RNFBNativeEventEmitter.js +12 -1
- package/dist/module/internal/RNFBNativeEventEmitter.js.map +1 -1
- package/dist/module/internal/SharedEventEmitter.js +3 -1
- package/dist/module/internal/SharedEventEmitter.js.map +1 -1
- package/dist/module/internal/asyncStorage.js +7 -11
- package/dist/module/internal/asyncStorage.js.map +1 -1
- package/dist/module/internal/index.js +1 -0
- package/dist/module/internal/index.js.map +1 -1
- package/dist/module/internal/logger.js +44 -40
- package/dist/module/internal/logger.js.map +1 -1
- package/dist/module/internal/nativeModuleAndroidIos.js +2 -29
- package/dist/module/internal/nativeModuleAndroidIos.js.map +1 -1
- package/dist/module/internal/nativeModuleDebug.js +74 -0
- package/dist/module/internal/nativeModuleDebug.js.map +1 -0
- package/dist/module/internal/nativeModuleWeb.js +9 -29
- package/dist/module/internal/nativeModuleWeb.js.map +1 -1
- package/dist/module/internal/registry/app.js +8 -2
- package/dist/module/internal/registry/app.js.map +1 -1
- package/dist/module/internal/registry/modular.js +2 -2
- package/dist/module/internal/registry/modular.js.map +1 -1
- package/dist/module/internal/registry/nativeModule.js +3 -3
- package/dist/module/internal/registry/nativeModule.js.map +1 -1
- package/dist/module/internal/web/RNFBAppModule.js +16 -15
- package/dist/module/internal/web/RNFBAppModule.js.map +1 -1
- package/dist/module/internal/web/memidb/lib/FakeEventTarget.js +3 -1
- package/dist/module/internal/web/memidb/lib/FakeEventTarget.js.map +1 -1
- package/dist/module/types/app.js.map +1 -1
- package/dist/module/types/internal.js +2 -0
- package/dist/module/version.js +1 -1
- package/dist/typescript/lib/FirebaseApp.d.ts +7 -0
- package/dist/typescript/lib/FirebaseApp.d.ts.map +1 -1
- package/dist/typescript/lib/common/Base64.d.ts.map +1 -1
- package/dist/typescript/lib/common/deeps.d.ts.map +1 -1
- package/dist/typescript/lib/common/index.d.ts.map +1 -1
- package/dist/typescript/lib/internal/FirebaseModule.d.ts +2 -3
- package/dist/typescript/lib/internal/FirebaseModule.d.ts.map +1 -1
- package/dist/typescript/lib/internal/NativeModules.d.ts +4 -1
- package/dist/typescript/lib/internal/NativeModules.d.ts.map +1 -1
- package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts +2 -2
- package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts.map +1 -1
- package/dist/typescript/lib/internal/SharedEventEmitter.d.ts +3 -3
- package/dist/typescript/lib/internal/SharedEventEmitter.d.ts.map +1 -1
- package/dist/typescript/lib/internal/asyncStorage.d.ts.map +1 -1
- package/dist/typescript/lib/internal/index.d.ts +1 -0
- package/dist/typescript/lib/internal/index.d.ts.map +1 -1
- package/dist/typescript/lib/internal/logger.d.ts.map +1 -1
- package/dist/typescript/lib/internal/nativeModuleAndroidIos.d.ts.map +1 -1
- package/dist/typescript/lib/internal/nativeModuleDebug.d.ts +2 -0
- package/dist/typescript/lib/internal/nativeModuleDebug.d.ts.map +1 -0
- package/dist/typescript/lib/internal/nativeModuleWeb.d.ts.map +1 -1
- package/dist/typescript/lib/internal/registry/app.d.ts.map +1 -1
- package/dist/typescript/lib/internal/web/RNFBAppModule.d.ts +7 -7
- package/dist/typescript/lib/internal/web/RNFBAppModule.d.ts.map +1 -1
- package/dist/typescript/lib/types/app.d.ts +10 -3
- package/dist/typescript/lib/types/app.d.ts.map +1 -1
- package/dist/typescript/lib/types/internal.d.ts +12 -0
- package/dist/typescript/lib/types/internal.d.ts.map +1 -1
- package/dist/typescript/lib/version.d.ts +1 -1
- package/firebase-schema.json +5 -1
- package/firebase_spm.rb +552 -96
- package/ios/RNFBApp/RCTConvert+FIRApp.h +2 -5
- package/ios/RNFBApp/RCTConvert+FIRApp.m +11 -6
- package/ios/RNFBApp/RCTConvert+FIROptions.h +2 -5
- package/ios/RNFBApp/RCTConvert+FIROptions.m +11 -10
- package/ios/RNFBApp/RCTConvertFIRApp.swift +86 -0
- package/ios/RNFBApp/RCTConvertFIROptions.swift +259 -0
- package/ios/RNFBApp/RNFBAppCustomAuthDomains.swift +63 -0
- package/ios/RNFBApp/RNFBAppInitializeOptionsMapper.swift +113 -0
- package/ios/RNFBApp/RNFBAppLogLevelMapper.swift +53 -0
- package/ios/RNFBApp/RNFBAppModule+CustomAuthDomain.m +35 -0
- package/ios/RNFBApp/RNFBAppModule.h +6 -2
- package/ios/RNFBApp/RNFBAppModule.mm +67 -204
- package/ios/RNFBApp/RNFBAppModuleFirebase.swift +439 -0
- package/ios/RNFBApp/RNFBAppModuleImplementation.h +68 -0
- package/ios/RNFBApp/RNFBAppModuleImplementation.m +195 -0
- package/ios/RNFBApp/RNFBHandleMap.h +73 -0
- package/ios/RNFBApp/RNFBHandleMap.m +87 -0
- package/ios/RNFBApp/RNFBHandleMapStorage.swift +90 -0
- package/ios/RNFBApp/RNFBInitializeAppArguments.swift +82 -0
- package/ios/RNFBApp/RNFBJSON.m +22 -38
- package/ios/RNFBApp/RNFBJSONImplementation.swift +71 -0
- package/ios/RNFBApp/RNFBMeta.m +24 -26
- package/ios/RNFBApp/RNFBMetaStorage.swift +76 -0
- package/ios/RNFBApp/RNFBNullSentinelDecoding.swift +135 -0
- package/ios/RNFBApp/RNFBNullSentinelInterceptor.h +30 -3
- package/ios/RNFBApp/RNFBNullSentinelInterceptor.m +28 -5
- package/ios/RNFBApp/RNFBPreferences.m +22 -20
- package/ios/RNFBApp/RNFBPreferencesStorage.swift +101 -0
- package/ios/RNFBApp/RNFBRCTEventEmitter.m +36 -93
- package/ios/RNFBApp/RNFBRCTEventEmitterCore.swift +161 -0
- package/ios/RNFBApp/RNFBSharedUtils.h +2 -5
- package/ios/RNFBApp/RNFBSharedUtils.m +109 -197
- package/ios/RNFBApp/RNFBSharedUtilsConfig.swift +72 -0
- package/ios/RNFBApp/RNFBSharedUtilsFIRApp.swift +271 -0
- package/ios/RNFBApp/RNFBSharedUtilsFormatting.swift +59 -0
- package/ios/RNFBApp/RNFBSharedUtilsPromiseRejection.swift +88 -0
- package/ios/RNFBApp/RNFBUtilsHelpers.swift +116 -0
- package/ios/RNFBApp/RNFBUtilsModule+Foundation.m +44 -0
- package/ios/RNFBApp/RNFBUtilsModule+PhotoAssets.m +47 -0
- package/ios/RNFBApp/RNFBUtilsModule.h +11 -1
- package/ios/RNFBApp/RNFBUtilsModule.mm +10 -85
- package/ios/RNFBApp/RNFBUtilsModuleImplementation.h +32 -0
- package/ios/RNFBApp/RNFBUtilsModuleImplementation.m +56 -0
- package/ios/RNFBApp/RNFBVersion.m +1 -1
- package/ios/RNFBApp.xcodeproj/project.pbxproj +106 -0
- package/ios/RNFBAppUnitTests/HostStubs/FirebaseCore/FIRApp.m +171 -0
- package/ios/RNFBAppUnitTests/HostStubs/FirebaseCore/FirebaseCore.h +78 -0
- package/ios/RNFBAppUnitTests/HostStubs/FirebaseInstallations/FIRInstallations.m +7 -0
- package/ios/RNFBAppUnitTests/HostStubs/FirebaseInstallations/FirebaseInstallations.h +8 -0
- package/ios/RNFBAppUnitTests/HostStubs/RNFBAppModuleHostStub.m +55 -0
- package/ios/RNFBAppUnitTests/HostStubs/RNFBAppTurboModules.h +89 -0
- package/ios/RNFBAppUnitTests/HostStubs/RNFBAppTurboModules.mm +30 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridge.h +19 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridge.m +32 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridgeModule.h +19 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTConvert.h +28 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTConvert.m +23 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTEventEmitter.h +9 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTInvalidating.h +24 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTUtils.h +9 -0
- package/ios/RNFBAppUnitTests/HostStubs/module.modulemap +13 -0
- package/ios/RNFBAppUnitTests/RCTConvertFIRAppFacadeTests.m +61 -0
- package/ios/RNFBAppUnitTests/RCTConvertFIRAppTests.swift +73 -0
- package/ios/RNFBAppUnitTests/RCTConvertFIROptionsFacadeTests.m +57 -0
- package/ios/RNFBAppUnitTests/RCTConvertFIROptionsTests.swift +243 -0
- package/ios/RNFBAppUnitTests/RNFBAppCustomAuthDomainsFacadeTests.m +73 -0
- package/ios/RNFBAppUnitTests/RNFBAppCustomAuthDomainsTests.swift +70 -0
- package/ios/RNFBAppUnitTests/RNFBAppInitializeOptionsMapperTests.swift +190 -0
- package/ios/RNFBAppUnitTests/RNFBAppLogLevelFacadeTests.m +76 -0
- package/ios/RNFBAppUnitTests/RNFBAppLogLevelMapperTests.swift +59 -0
- package/ios/RNFBAppUnitTests/RNFBAppModuleFirebaseFacadeTests.m +215 -0
- package/ios/RNFBAppUnitTests/RNFBAppModuleFirebaseTests.swift +430 -0
- package/ios/RNFBAppUnitTests/RNFBAppModuleLifecycleTests.m +206 -0
- package/ios/RNFBAppUnitTests/RNFBAppModuleShellTests.mm +367 -0
- package/ios/RNFBAppUnitTests/RNFBAppUnitTests.xcodeproj/project.pbxproj +590 -0
- package/ios/RNFBAppUnitTests/RNFBAppUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBAppUnitTests.xcscheme +69 -0
- package/ios/RNFBAppUnitTests/RNFBFirebaseAppClientTests.swift +165 -0
- package/ios/RNFBAppUnitTests/RNFBHandleMapStorageTests.swift +121 -0
- package/ios/RNFBAppUnitTests/RNFBHandleMapTests.m +238 -0
- package/ios/RNFBAppUnitTests/RNFBInitializeAppArgumentsTests.swift +256 -0
- package/ios/RNFBAppUnitTests/RNFBJSONImplementationTests.swift +126 -0
- package/ios/RNFBAppUnitTests/RNFBJSONTests.m +172 -0
- package/ios/RNFBAppUnitTests/RNFBMetaStorageTests.swift +106 -0
- package/ios/RNFBAppUnitTests/RNFBMetaTests.m +65 -0
- package/ios/RNFBAppUnitTests/RNFBNullSentinelDecodingTests.swift +286 -0
- package/ios/RNFBAppUnitTests/RNFBNullSentinelInterceptorTests.m +312 -0
- package/ios/RNFBAppUnitTests/RNFBPreferencesStorageTests.swift +105 -0
- package/ios/RNFBAppUnitTests/RNFBPreferencesTests.m +110 -0
- package/ios/RNFBAppUnitTests/RNFBRCTEventEmitterCoreTests.swift +163 -0
- package/ios/RNFBAppUnitTests/RNFBRCTEventEmitterFacadeTests.m +141 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsConfigFacadeTests.m +100 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsConfigTests.swift +234 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsFIRAppFacadeTests.m +145 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsFIRAppTests.swift +173 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsFormattingFacadeTests.m +38 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsFormattingTests.swift +82 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsNullSentinelTests.m +35 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsPromiseRejectionFacadeTests.m +107 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsPromiseRejectionTests.swift +133 -0
- package/ios/RNFBAppUnitTests/RNFBUtilsHelpersTests.swift +144 -0
- package/ios/RNFBAppUnitTests/RNFBUtilsModuleFacadeTests.m +72 -0
- package/ios/RNFBAppUnitTests/RNFBUtilsModuleShellTests.mm +282 -0
- package/ios/RNFBFirebase/Package.swift +53 -0
- package/ios/RNFBFirebase/Sources/RNFBFirebase/RNFBFirebase.swift +285 -0
- package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/Public/.gitkeep +0 -0
- package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/RNFBFirebaseBridge.m +169 -0
- package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/include/RNFBFirebaseBridge.h +52 -0
- package/ios/RNFBFirebaseUnitTests/RNFBAppDynamicFacadeTests.swift +240 -0
- package/{android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppRegistrar.java → ios/RNFBFirebaseUnitTests/RNFBFirebaseClientImport.swift} +22 -17
- package/ios/RNFBFirebaseUnitTests/RNFBFirebaseUnitTests.xcodeproj/project.pbxproj +588 -0
- package/ios/RNFBFirebaseUnitTests/RNFBFirebaseUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBFirebaseUnitTests.xcscheme +83 -0
- package/ios_config.sh +50 -22
- package/lib/FirebaseApp.ts +10 -0
- package/lib/common/Base64.ts +10 -16
- package/lib/common/deeps.ts +3 -0
- package/lib/common/index.ts +20 -9
- package/lib/internal/FirebaseModule.ts +6 -3
- package/lib/internal/NativeModules.ts +4 -1
- package/lib/internal/RNFBNativeEventEmitter.ts +14 -5
- package/lib/internal/SharedEventEmitter.ts +5 -1
- package/lib/internal/asyncStorage.ts +7 -11
- package/lib/internal/index.ts +1 -0
- package/lib/internal/logger.ts +53 -43
- package/lib/internal/nativeModuleAndroidIos.ts +2 -40
- package/lib/internal/nativeModuleDebug.ts +97 -0
- package/lib/internal/nativeModuleWeb.ts +10 -40
- package/lib/internal/react-native-vendor-emitter.d.ts +11 -0
- package/lib/internal/registry/app.ts +8 -2
- package/lib/internal/registry/modular.ts +2 -2
- package/lib/internal/registry/nativeModule.ts +3 -3
- package/lib/internal/web/RNFBAppModule.ts +16 -15
- package/lib/internal/web/memidb/lib/FakeEventTarget.js +3 -1
- package/lib/types/app.ts +10 -3
- package/lib/types/internal.ts +19 -0
- package/lib/version.ts +1 -1
- package/package.json +9 -8
- package/plugin/build/ios/appDelegate.js +19 -9
- package/plugin/build/ios/podfile.js +16 -16
- package/plugin/build/pluginConfig.d.ts +4 -2
- package/plugin/src/ios/appDelegate.ts +45 -15
- package/plugin/src/ios/podfile.ts +20 -23
- package/plugin/src/pluginConfig.ts +4 -2
- package/android/src/main/java/io/invertase/firebase/common/TaskExecutorService.java +0 -124
- package/android/src/main/java/io/invertase/firebase/common/UniversalFirebaseModule.java +0 -62
- package/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.java +0 -91
- package/android/src/reactnative/java/io/invertase/firebase/app/NativeRNFBTurboApp.java +0 -222
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseApp.java +0 -46
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.java +0 -49
- package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.java +0 -191
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEvent.java +0 -53
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.java +0 -606
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseJSON.java +0 -107
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.java +0 -101
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.java +0 -146
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.java +0 -96
- package/android/src/reactnative/java/io/invertase/firebase/common/SharedUtils.java +0 -441
- package/android/src/reactnative/java/io/invertase/firebase/utils/NativeRNFBTurboUtils.java +0 -223
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#import <XCTest/XCTest.h>
|
|
18
|
+
|
|
19
|
+
#import "RNFBHandleMapStorage-Swift.inc"
|
|
20
|
+
#import "RNFBMeta.h"
|
|
21
|
+
|
|
22
|
+
@interface RNFBMetaTests : XCTestCase
|
|
23
|
+
@end
|
|
24
|
+
|
|
25
|
+
@implementation RNFBMetaTests
|
|
26
|
+
|
|
27
|
+
- (void)testPublicClassSelectorsAndReturnEncodingsAreUnchanged {
|
|
28
|
+
NSArray<NSString *> *selectorNames = @[
|
|
29
|
+
@"contains:",
|
|
30
|
+
@"getBooleanValue:defaultValue:",
|
|
31
|
+
@"getStringValue:defaultValue:",
|
|
32
|
+
@"getAll",
|
|
33
|
+
];
|
|
34
|
+
for (NSString *selectorName in selectorNames) {
|
|
35
|
+
XCTAssertTrue([RNFBMeta respondsToSelector:NSSelectorFromString(selectorName)]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
NSMethodSignature *contains = [RNFBMeta methodSignatureForSelector:@selector(contains:)];
|
|
39
|
+
XCTAssertEqual(strcmp(contains.methodReturnType, @encode(BOOL)), 0);
|
|
40
|
+
|
|
41
|
+
NSMethodSignature *boolean = [RNFBMeta methodSignatureForSelector:@selector(getBooleanValue:
|
|
42
|
+
defaultValue:)];
|
|
43
|
+
XCTAssertEqual(strcmp(boolean.methodReturnType, @encode(BOOL)), 0);
|
|
44
|
+
XCTAssertEqual(strcmp([boolean getArgumentTypeAtIndex:3], @encode(BOOL)), 0);
|
|
45
|
+
|
|
46
|
+
NSMethodSignature *string = [RNFBMeta methodSignatureForSelector:@selector(getStringValue:
|
|
47
|
+
defaultValue:)];
|
|
48
|
+
XCTAssertEqual(strcmp(string.methodReturnType, @encode(id)), 0);
|
|
49
|
+
|
|
50
|
+
NSMethodSignature *getAll = [RNFBMeta methodSignatureForSelector:@selector(getAll)];
|
|
51
|
+
XCTAssertEqual(strcmp(getAll.methodReturnType, @encode(id)), 0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
- (void)testFacadeForwardsToSharedMainBundleStorage {
|
|
55
|
+
// Smoke: class methods are callable and getAll returns a dictionary (may be empty).
|
|
56
|
+
XCTAssertNotNil([RNFBMeta getAll]);
|
|
57
|
+
XCTAssertTrue([[RNFBMeta getAll] isKindOfClass:[NSDictionary class]]);
|
|
58
|
+
XCTAssertFalse([RNFBMeta contains:@"__rnfb_meta_unit_test_missing_key__"]);
|
|
59
|
+
XCTAssertTrue([RNFBMeta getBooleanValue:@"__rnfb_meta_unit_test_missing_key__" defaultValue:YES]);
|
|
60
|
+
XCTAssertEqualObjects([RNFBMeta getStringValue:@"__rnfb_meta_unit_test_missing_key__"
|
|
61
|
+
defaultValue:@"fallback"],
|
|
62
|
+
@"fallback");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@end
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import Foundation
|
|
18
|
+
import XCTest
|
|
19
|
+
|
|
20
|
+
final class RNFBNullSentinelDecodingTests: XCTestCase {
|
|
21
|
+
private func decode(_ value: Any?) -> Any? {
|
|
22
|
+
RNFBNullSentinelDecoder.decodeNullSentinels(value)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private func sentinel(_ flag: Any = true) -> NSDictionary {
|
|
26
|
+
["__rnfbNull": flag] as NSDictionary
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private func serverTimestamp() -> NSDictionary {
|
|
30
|
+
[".sv": "timestamp"] as NSDictionary
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func testNilAndNonContainersPreserveIdentity() {
|
|
34
|
+
XCTAssertNil(decode(nil))
|
|
35
|
+
|
|
36
|
+
let number = NSNumber(value: 42)
|
|
37
|
+
let string = "hello" as NSString
|
|
38
|
+
let date = NSDate()
|
|
39
|
+
let data = NSData()
|
|
40
|
+
let null = NSNull()
|
|
41
|
+
|
|
42
|
+
XCTAssertTrue(decode(number) as AnyObject === number)
|
|
43
|
+
XCTAssertTrue(decode(string) as AnyObject === string)
|
|
44
|
+
XCTAssertTrue(decode(date) as AnyObject === date)
|
|
45
|
+
XCTAssertTrue(decode(data) as AnyObject === data)
|
|
46
|
+
XCTAssertTrue(decode(null) as AnyObject === null)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func testDecodeAndDecodeNullSentinelsSelectorsAreEquivalent() {
|
|
50
|
+
let input: NSDictionary = ["a": sentinel(), "b": "keep"]
|
|
51
|
+
let viaCanonical = RNFBNullSentinelDecoder.decodeNullSentinels(input) as! NSDictionary
|
|
52
|
+
let viaAlias = RNFBNullSentinelDecoder.decode(input) as! NSDictionary
|
|
53
|
+
XCTAssertEqual(viaCanonical, viaAlias)
|
|
54
|
+
XCTAssertTrue(viaCanonical["a"] is NSNull)
|
|
55
|
+
XCTAssertEqual(viaCanonical["b"] as? String, "keep")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func testRootSentinelBecomesNSNull() {
|
|
59
|
+
XCTAssertTrue(decode(sentinel()) is NSNull)
|
|
60
|
+
XCTAssertTrue(decode(sentinel(NSNumber(value: 1))) is NSNull)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
func testRootSentinelRequiresExactlyOneKeyAndTruthyFlag() {
|
|
64
|
+
XCTAssertFalse(decode(sentinel(false)) is NSNull)
|
|
65
|
+
XCTAssertEqual(decode(sentinel(false)) as? NSDictionary, ["__rnfbNull": false] as NSDictionary)
|
|
66
|
+
|
|
67
|
+
let extraKey = ["__rnfbNull": true, "other": 1] as NSDictionary
|
|
68
|
+
XCTAssertEqual(decode(extraKey) as? NSDictionary, extraKey)
|
|
69
|
+
|
|
70
|
+
let empty = decode(NSDictionary()) as! NSDictionary
|
|
71
|
+
XCTAssertEqual(empty.count, 0)
|
|
72
|
+
XCTAssertTrue(empty is NSMutableDictionary)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
func testStringFlagIsNotASentinel() {
|
|
76
|
+
for flag in ["true", "YES", "yes", "1"] as [NSString] {
|
|
77
|
+
let input = sentinel(flag)
|
|
78
|
+
XCTAssertEqual(decode(input) as? NSDictionary, input)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
func testNonBoolRespondingFlagIsNotASentinel() {
|
|
83
|
+
let exotic = sentinel(NSDate())
|
|
84
|
+
let decoded = decode(exotic) as! NSDictionary
|
|
85
|
+
XCTAssertEqual(decoded.count, 1)
|
|
86
|
+
XCTAssertTrue(decoded["__rnfbNull"] is NSDate)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
func testRootSentinelFlagNonNumberUnchangedWithoutCrash() {
|
|
90
|
+
let nullFlag = ["__rnfbNull": NSNull()] as NSDictionary
|
|
91
|
+
let dictFlag = ["__rnfbNull": ["a": 1] as NSDictionary] as NSDictionary
|
|
92
|
+
let arrayFlag = ["__rnfbNull": [1] as NSArray] as NSDictionary
|
|
93
|
+
|
|
94
|
+
XCTAssertEqual(decode(nullFlag) as? NSDictionary, nullFlag)
|
|
95
|
+
XCTAssertEqual(decode(dictFlag) as? NSDictionary, dictFlag)
|
|
96
|
+
XCTAssertEqual(decode(arrayFlag) as? NSDictionary, arrayFlag)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func testRootServerValueUnchanged() {
|
|
100
|
+
let input = serverTimestamp()
|
|
101
|
+
XCTAssertEqual(decode(input) as? NSDictionary, input)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
func testNestedDictionarySentinelsBecomeNSNull() {
|
|
105
|
+
let input: NSDictionary = [
|
|
106
|
+
"a": sentinel(),
|
|
107
|
+
"b": "keep",
|
|
108
|
+
"nested": [
|
|
109
|
+
"inner": sentinel(),
|
|
110
|
+
"n": 1,
|
|
111
|
+
"falseFlag": sentinel(false),
|
|
112
|
+
"empty": NSDictionary(),
|
|
113
|
+
] as NSDictionary,
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
let result = decode(input) as! NSDictionary
|
|
117
|
+
XCTAssertTrue(result["a"] is NSNull)
|
|
118
|
+
XCTAssertEqual(result["b"] as? String, "keep")
|
|
119
|
+
let nested = result["nested"] as! NSDictionary
|
|
120
|
+
XCTAssertTrue(nested["inner"] is NSNull)
|
|
121
|
+
XCTAssertEqual(nested["n"] as? Int, 1)
|
|
122
|
+
XCTAssertEqual(nested["falseFlag"] as? NSDictionary, ["__rnfbNull": false] as NSDictionary)
|
|
123
|
+
XCTAssertEqual((nested["empty"] as? NSDictionary)?.count, 0)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
func testNestedSentinelWithExtraKeysUnchanged() {
|
|
127
|
+
let input: NSDictionary = ["child": ["__rnfbNull": true, "other": "x"] as NSDictionary]
|
|
128
|
+
XCTAssertEqual(decode(input) as? NSDictionary, input)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
func testNestedSentinelFlagNonNumberUnchanged() {
|
|
132
|
+
let input: NSDictionary = [
|
|
133
|
+
"nullFlag": ["__rnfbNull": NSNull()] as NSDictionary,
|
|
134
|
+
"dictFlag": ["__rnfbNull": ["a": 1] as NSDictionary] as NSDictionary,
|
|
135
|
+
"arrayFlag": ["__rnfbNull": [1] as NSArray] as NSDictionary,
|
|
136
|
+
"stringFlag": ["__rnfbNull": "true"] as NSDictionary,
|
|
137
|
+
]
|
|
138
|
+
XCTAssertEqual(decode(input) as? NSDictionary, input)
|
|
139
|
+
|
|
140
|
+
let arrayInput: NSArray = [
|
|
141
|
+
["__rnfbNull": NSNull()] as NSDictionary,
|
|
142
|
+
["__rnfbNull": ["a": 1] as NSDictionary] as NSDictionary,
|
|
143
|
+
["__rnfbNull": [1] as NSArray] as NSDictionary,
|
|
144
|
+
["__rnfbNull": "true"] as NSDictionary,
|
|
145
|
+
]
|
|
146
|
+
XCTAssertEqual(decode(arrayInput) as? NSArray, arrayInput)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
func testValidSentinelSiblingDecodedWhileNonNumberFlagPassesThrough() {
|
|
150
|
+
let input: NSDictionary = [
|
|
151
|
+
"valid": sentinel(),
|
|
152
|
+
"guarded": ["__rnfbNull": NSNull()] as NSDictionary,
|
|
153
|
+
"list": [sentinel(), ["__rnfbNull": [1] as NSArray] as NSDictionary] as NSArray,
|
|
154
|
+
]
|
|
155
|
+
let result = decode(input) as! NSDictionary
|
|
156
|
+
XCTAssertTrue(result["valid"] is NSNull)
|
|
157
|
+
XCTAssertEqual(result["guarded"] as? NSDictionary, ["__rnfbNull": NSNull()] as NSDictionary)
|
|
158
|
+
let list = result["list"] as! NSArray
|
|
159
|
+
XCTAssertTrue(list[0] is NSNull)
|
|
160
|
+
XCTAssertEqual(list[1] as? NSDictionary, ["__rnfbNull": [1] as NSArray] as NSDictionary)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
func testArrayWalkPreservesNSNullAndReplacesSentinels() {
|
|
164
|
+
let input: NSArray = [
|
|
165
|
+
sentinel(),
|
|
166
|
+
NSNull(),
|
|
167
|
+
"x",
|
|
168
|
+
3,
|
|
169
|
+
serverTimestamp(),
|
|
170
|
+
[sentinel(), 2] as NSArray,
|
|
171
|
+
]
|
|
172
|
+
|
|
173
|
+
let result = decode(input) as! NSArray
|
|
174
|
+
XCTAssertEqual(result.count, 6)
|
|
175
|
+
XCTAssertTrue(result[0] is NSNull)
|
|
176
|
+
XCTAssertTrue(result[1] is NSNull)
|
|
177
|
+
XCTAssertEqual(result[2] as? String, "x")
|
|
178
|
+
XCTAssertEqual(result[3] as? Int, 3)
|
|
179
|
+
XCTAssertEqual(result[4] as? NSDictionary, serverTimestamp())
|
|
180
|
+
let nested = result[5] as! NSArray
|
|
181
|
+
XCTAssertTrue(nested[0] is NSNull)
|
|
182
|
+
XCTAssertEqual(nested[1] as? Int, 2)
|
|
183
|
+
XCTAssertTrue(result is NSMutableArray)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
func testRootEmptyArrayReturnsEmptyMutableArray() {
|
|
187
|
+
let decoded = decode([] as NSArray) as! NSArray
|
|
188
|
+
XCTAssertEqual(decoded.count, 0)
|
|
189
|
+
XCTAssertTrue(decoded is NSMutableArray)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
func testNestedArraysSentinelsDecodedAtEveryLevel() {
|
|
193
|
+
let input: NSArray = [[sentinel(), [sentinel(), "leaf"] as NSArray] as NSArray, [] as NSArray]
|
|
194
|
+
let result = decode(input) as! NSArray
|
|
195
|
+
let first = result[0] as! NSArray
|
|
196
|
+
XCTAssertTrue(first[0] is NSNull)
|
|
197
|
+
XCTAssertTrue((first[1] as! NSArray)[0] is NSNull)
|
|
198
|
+
XCTAssertEqual((first[1] as! NSArray)[1] as? String, "leaf")
|
|
199
|
+
XCTAssertEqual((result[1] as! NSArray).count, 0)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
func testArrayInsideDictionaryAndMixedMatrixDecoded() {
|
|
203
|
+
let input: NSDictionary = [
|
|
204
|
+
"list": [sentinel(), "x", [sentinel()] as NSArray] as NSArray,
|
|
205
|
+
"items": [
|
|
206
|
+
["id": 1, "note": sentinel()] as NSDictionary,
|
|
207
|
+
["id": 2, "tags": [sentinel()] as NSArray] as NSDictionary,
|
|
208
|
+
] as NSArray,
|
|
209
|
+
]
|
|
210
|
+
let result = decode(input) as! NSDictionary
|
|
211
|
+
let list = result["list"] as! NSArray
|
|
212
|
+
XCTAssertTrue(list[0] is NSNull)
|
|
213
|
+
XCTAssertEqual(list[1] as? String, "x")
|
|
214
|
+
XCTAssertTrue((list[2] as! NSArray)[0] is NSNull)
|
|
215
|
+
|
|
216
|
+
let items = result["items"] as! NSArray
|
|
217
|
+
XCTAssertTrue((items[0] as! NSDictionary)["note"] is NSNull)
|
|
218
|
+
XCTAssertTrue(((items[1] as! NSDictionary)["tags"] as! NSArray)[0] is NSNull)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
func testChildAndParentTypeMatrix() {
|
|
222
|
+
let dictInput: NSDictionary = [
|
|
223
|
+
"dictChild": ["k": 1] as NSDictionary,
|
|
224
|
+
"arrayChild": ["leaf", [2] as NSArray, sentinel(), NSNull()] as NSArray,
|
|
225
|
+
"primitiveChild": "text",
|
|
226
|
+
"nullChild": NSNull(),
|
|
227
|
+
]
|
|
228
|
+
let arrayRoot: NSArray = [
|
|
229
|
+
["inner": sentinel()] as NSDictionary,
|
|
230
|
+
["nested"] as NSArray,
|
|
231
|
+
3,
|
|
232
|
+
sentinel(),
|
|
233
|
+
NSNull(),
|
|
234
|
+
]
|
|
235
|
+
|
|
236
|
+
let decodedDict = decode(dictInput) as! NSDictionary
|
|
237
|
+
XCTAssertEqual(decodedDict["dictChild"] as? NSDictionary, ["k": 1] as NSDictionary)
|
|
238
|
+
let arrayChild = decodedDict["arrayChild"] as! NSArray
|
|
239
|
+
XCTAssertEqual(arrayChild[0] as? String, "leaf")
|
|
240
|
+
XCTAssertTrue(arrayChild[2] is NSNull)
|
|
241
|
+
XCTAssertTrue(decodedDict["nullChild"] is NSNull)
|
|
242
|
+
|
|
243
|
+
let decodedArray = decode(arrayRoot) as! NSArray
|
|
244
|
+
XCTAssertTrue((decodedArray[0] as! NSDictionary)["inner"] is NSNull)
|
|
245
|
+
XCTAssertTrue(decodedArray[3] is NSNull)
|
|
246
|
+
XCTAssertTrue(decodedArray[4] is NSNull)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
func testContainersReturnNewMutableCopiesEvenWithoutSentinels() {
|
|
250
|
+
let dict = ["k": "v"] as NSDictionary
|
|
251
|
+
let array = ["a", 1] as NSArray
|
|
252
|
+
|
|
253
|
+
let decodedDict = decode(dict)
|
|
254
|
+
let decodedArray = decode(array)
|
|
255
|
+
|
|
256
|
+
XCTAssertFalse(decodedDict as AnyObject === dict)
|
|
257
|
+
XCTAssertFalse(decodedArray as AnyObject === array)
|
|
258
|
+
XCTAssertTrue(decodedDict is NSMutableDictionary)
|
|
259
|
+
XCTAssertTrue(decodedArray is NSMutableArray)
|
|
260
|
+
XCTAssertEqual(decodedDict as? NSDictionary, dict)
|
|
261
|
+
XCTAssertEqual(decodedArray as? NSArray, array)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
func testCountOneNumberFalseIsNotSentinel() {
|
|
265
|
+
let input: NSDictionary = [
|
|
266
|
+
"a": ["__rnfbNull": false] as NSDictionary,
|
|
267
|
+
"b": ["__rnfbNull": NSNumber(value: 0)] as NSDictionary,
|
|
268
|
+
]
|
|
269
|
+
XCTAssertEqual(decode(input) as? NSDictionary, input)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
func testDeepNestingDoesNotOverflow() {
|
|
273
|
+
let depth = 1000
|
|
274
|
+
var leaf: Any = ["value": sentinel()] as NSDictionary
|
|
275
|
+
for _ in 0..<depth {
|
|
276
|
+
leaf = ["child": leaf] as NSDictionary
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
var cursor = decode(leaf) as! NSDictionary
|
|
280
|
+
for _ in 0..<depth {
|
|
281
|
+
XCTAssertTrue(cursor is NSMutableDictionary)
|
|
282
|
+
cursor = cursor["child"] as! NSDictionary
|
|
283
|
+
}
|
|
284
|
+
XCTAssertTrue(cursor["value"] is NSNull)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this library except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#import <XCTest/XCTest.h>
|
|
19
|
+
#import <objc/runtime.h>
|
|
20
|
+
|
|
21
|
+
#import "RNFBNullSentinelInterceptor.h"
|
|
22
|
+
|
|
23
|
+
/** Set by the original (pre-swizzle) IMPs of every runtime-created stand-in class. */
|
|
24
|
+
static id gLastJsonSeenByOriginalIMP = nil;
|
|
25
|
+
|
|
26
|
+
static NSString *const kSpecSelector = @"JS_NativeRNFBTurboFunctions_SpecHttpsCallableData:";
|
|
27
|
+
/** Prefix matches JS_NativeRNFBTurbo* but lacks _Spec: must not be wrapped. */
|
|
28
|
+
static NSString *const kNotASpecSelector = @"JS_NativeRNFBTurboFunctions_NotASpecMethod:";
|
|
29
|
+
static NSString *const kUnrelatedSelector = @"unrelatedConverter:";
|
|
30
|
+
static NSString *const kCxxConvertSpecSelector = @"JS_NativeRNFBTurboApp_SpecData:";
|
|
31
|
+
static NSString *const kCxxConvertClassName = @"RCTCxxConvert";
|
|
32
|
+
|
|
33
|
+
@interface RNFBNullSentinelInterceptorTests : XCTestCase
|
|
34
|
+
@end
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Each test that needs a class builds its own uniquely named one at runtime and disposes of it in
|
|
38
|
+
* tearDown, so no test observes IMP wrapping or a global RCTCxxConvert registered by another test.
|
|
39
|
+
* That makes the suite independent of XCTest execution order and safe to run singly.
|
|
40
|
+
*/
|
|
41
|
+
@implementation RNFBNullSentinelInterceptorTests {
|
|
42
|
+
// Class pointers are stored as NSValue so ARC never retains/releases a class after disposal.
|
|
43
|
+
// Per-test stand-ins: scheduled blocks may capture these, so tearDown spares them on a drain
|
|
44
|
+
// timeout.
|
|
45
|
+
NSMutableArray<NSValue *> *_standInClasses;
|
|
46
|
+
// Fake global RCTCxxConvert: nothing queued captures it, so tearDown always disposes it.
|
|
47
|
+
NSMutableArray<NSValue *> *_globalClasses;
|
|
48
|
+
// Class pointer -> the IMPs of the block-backed methods this test added to it.
|
|
49
|
+
NSMutableDictionary<NSValue *, NSArray<NSValue *> *> *_originalIMPsByClass;
|
|
50
|
+
// Heap-allocated dispatch_once_t storage (see -newOnceToken), freed in tearDown.
|
|
51
|
+
NSMutableArray<NSValue *> *_onceTokens;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
- (void)setUp {
|
|
55
|
+
[super setUp];
|
|
56
|
+
gLastJsonSeenByOriginalIMP = nil;
|
|
57
|
+
_standInClasses = [NSMutableArray array];
|
|
58
|
+
_globalClasses = [NSMutableArray array];
|
|
59
|
+
_originalIMPsByClass = [NSMutableDictionary dictionary];
|
|
60
|
+
_onceTokens = [NSMutableArray array];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
- (void)tearDown {
|
|
64
|
+
// schedule...WithOnceToken: dispatches a block to the main queue that captures the once token
|
|
65
|
+
// pointer and the stand-in class. Drain the main queue (FIFO, so every block queued by the test
|
|
66
|
+
// has run once the drain block does) before disposing those or freeing the token. If the drain
|
|
67
|
+
// times out, leak them instead of risking a use-after-free in a late block. The global
|
|
68
|
+
// RCTCxxConvert is never captured by a queued block, so it is always disposed; otherwise a
|
|
69
|
+
// leaked registration would fail every later RCTCxxConvert test.
|
|
70
|
+
BOOL drained = [self drainMainQueue];
|
|
71
|
+
[self disposeClasses:_globalClasses];
|
|
72
|
+
if (drained) {
|
|
73
|
+
[self disposeClasses:_standInClasses];
|
|
74
|
+
for (NSValue *tokenValue in _onceTokens) {
|
|
75
|
+
free(tokenValue.pointerValue);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
[_standInClasses removeAllObjects];
|
|
79
|
+
[_globalClasses removeAllObjects];
|
|
80
|
+
[_originalIMPsByClass removeAllObjects];
|
|
81
|
+
[_onceTokens removeAllObjects];
|
|
82
|
+
gLastJsonSeenByOriginalIMP = nil;
|
|
83
|
+
[super tearDown];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Disposes of the given classes and removes their block-backed IMPs. Each block IMP must be
|
|
88
|
+
* removed exactly once (a second imp_removeBlock crashes), so gather the unique set per class: the
|
|
89
|
+
* original stand-in IMPs plus any wrapper IMPs the interceptor swizzled in.
|
|
90
|
+
*/
|
|
91
|
+
- (void)disposeClasses:(NSArray<NSValue *> *)classValues {
|
|
92
|
+
for (NSValue *classValue in classValues) {
|
|
93
|
+
__unsafe_unretained Class cls = (__bridge Class)classValue.pointerValue;
|
|
94
|
+
NSMutableSet<NSValue *> *blockIMPs =
|
|
95
|
+
[NSMutableSet setWithArray:_originalIMPsByClass[classValue] ?: @[]];
|
|
96
|
+
unsigned int count = 0;
|
|
97
|
+
Method *methods = class_copyMethodList(object_getClass(cls), &count);
|
|
98
|
+
for (unsigned int i = 0; i < count; i++) {
|
|
99
|
+
[blockIMPs
|
|
100
|
+
addObject:[NSValue valueWithPointer:(const void *)method_getImplementation(methods[i])]];
|
|
101
|
+
}
|
|
102
|
+
free(methods);
|
|
103
|
+
objc_disposeClassPair(cls);
|
|
104
|
+
for (NSValue *value in blockIMPs) {
|
|
105
|
+
imp_removeBlock((IMP)value.pointerValue);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Creates and registers a class named `name` whose class methods (one per selector name) record
|
|
112
|
+
* the received json in gLastJsonSeenByOriginalIMP and return it. Disposed in tearDown. `global`
|
|
113
|
+
* marks a class registered under a well-known global name (RCTCxxConvert).
|
|
114
|
+
*/
|
|
115
|
+
- (Class)registerClassNamed:(NSString *)name
|
|
116
|
+
selectorNames:(NSArray<NSString *> *)selectorNames
|
|
117
|
+
global:(BOOL)global {
|
|
118
|
+
Class cls = objc_allocateClassPair([NSObject class], name.UTF8String, 0);
|
|
119
|
+
XCTAssertTrue(cls != Nil, @"could not allocate class %@", name);
|
|
120
|
+
if (cls == Nil) {
|
|
121
|
+
return Nil;
|
|
122
|
+
}
|
|
123
|
+
Class meta = object_getClass(cls);
|
|
124
|
+
NSMutableArray<NSValue *> *imps = [NSMutableArray array];
|
|
125
|
+
for (NSString *selectorName in selectorNames) {
|
|
126
|
+
IMP imp = imp_implementationWithBlock(^id(__unused id receiver, id json) {
|
|
127
|
+
gLastJsonSeenByOriginalIMP = json;
|
|
128
|
+
return json;
|
|
129
|
+
});
|
|
130
|
+
BOOL added = class_addMethod(meta, NSSelectorFromString(selectorName), imp, "@@:@");
|
|
131
|
+
XCTAssertTrue(added, @"could not add class method %@ to %@", selectorName, name);
|
|
132
|
+
[imps addObject:[NSValue valueWithPointer:(const void *)imp]];
|
|
133
|
+
}
|
|
134
|
+
objc_registerClassPair(cls);
|
|
135
|
+
NSValue *classValue = [NSValue valueWithPointer:(__bridge const void *)cls];
|
|
136
|
+
_originalIMPsByClass[classValue] = imps;
|
|
137
|
+
[global ? _globalClasses : _standInClasses addObject:classValue];
|
|
138
|
+
return cls;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Stand-in for RCTCxxConvert codegen categories, uniquely named per call. */
|
|
142
|
+
- (Class)makeStandInConvertClass {
|
|
143
|
+
static NSUInteger counter = 0; // XCTest runs test methods serially on one thread.
|
|
144
|
+
NSString *name =
|
|
145
|
+
[NSString stringWithFormat:@"RNFBNullSentinelStandInConvert_%lu", (unsigned long)++counter];
|
|
146
|
+
return [self registerClassNamed:name
|
|
147
|
+
selectorNames:@[ kSpecSelector, kNotASpecSelector, kUnrelatedSelector ]
|
|
148
|
+
global:NO];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
- (NSDictionary *)nullSentinel {
|
|
152
|
+
return @{@"__rnfbNull" : @YES};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
- (NSDictionary *)unrelatedOneKeyDictionary {
|
|
156
|
+
return @{@".sv" : @"timestamp"};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
- (IMP)classMethodIMP:(Class)cls selector:(SEL)selector {
|
|
160
|
+
Method method = class_getClassMethod(cls, selector);
|
|
161
|
+
XCTAssertTrue(method != NULL, @"missing class method %@", NSStringFromSelector(selector));
|
|
162
|
+
return method_getImplementation(method);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Invokes the class method's current IMP, so swizzled wrappers are exercised. */
|
|
166
|
+
- (id)callConvert:(Class)cls selectorName:(NSString *)selectorName json:(id)json {
|
|
167
|
+
typedef id (*ConvertFunc)(id, SEL, id);
|
|
168
|
+
SEL selector = NSSelectorFromString(selectorName);
|
|
169
|
+
ConvertFunc convert = (ConvertFunc)[self classMethodIMP:cls selector:selector];
|
|
170
|
+
return convert(cls, selector, json);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Heap-allocated once token. A stack token would dangle if a dispatched block ran after the test
|
|
175
|
+
* method returned. Freed in tearDown, after the main queue is drained.
|
|
176
|
+
*/
|
|
177
|
+
- (dispatch_once_t *)newOnceToken {
|
|
178
|
+
dispatch_once_t *token = calloc(1, sizeof(dispatch_once_t));
|
|
179
|
+
[_onceTokens addObject:[NSValue valueWithPointer:token]];
|
|
180
|
+
return token;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Returns YES once every main-queue block queued before this call has run. */
|
|
184
|
+
- (BOOL)drainMainQueue {
|
|
185
|
+
XCTestExpectation *drained = [self expectationWithDescription:@"main queue drained"];
|
|
186
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
187
|
+
[drained fulfill];
|
|
188
|
+
});
|
|
189
|
+
XCTWaiterResult result = [XCTWaiter waitForExpectations:@[ drained ] timeout:2.0];
|
|
190
|
+
XCTAssertEqual(result, XCTWaiterResultCompleted, @"main queue did not drain in time");
|
|
191
|
+
return result == XCTWaiterResultCompleted;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
- (void)testSwizzleTurboModuleConversions_nullSentinel_handsNSNullToOriginalIMP {
|
|
195
|
+
Class standIn = [self makeStandInConvertClass];
|
|
196
|
+
SEL selector = NSSelectorFromString(kSpecSelector);
|
|
197
|
+
IMP before = [self classMethodIMP:standIn selector:selector];
|
|
198
|
+
|
|
199
|
+
[RNFBNullSentinelInterceptor swizzleTurboModuleConversions:standIn];
|
|
200
|
+
|
|
201
|
+
IMP after = [self classMethodIMP:standIn selector:selector];
|
|
202
|
+
XCTAssertNotEqual(before, after);
|
|
203
|
+
|
|
204
|
+
gLastJsonSeenByOriginalIMP = nil;
|
|
205
|
+
id result = [self callConvert:standIn selectorName:kSpecSelector json:[self nullSentinel]];
|
|
206
|
+
XCTAssertEqualObjects(gLastJsonSeenByOriginalIMP, [NSNull null]);
|
|
207
|
+
XCTAssertEqualObjects(result, [NSNull null]);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
- (void)testSwizzleTurboModuleConversions_unrelatedOneKeyDictionary_unchanged {
|
|
211
|
+
Class standIn = [self makeStandInConvertClass];
|
|
212
|
+
[RNFBNullSentinelInterceptor swizzleTurboModuleConversions:standIn];
|
|
213
|
+
|
|
214
|
+
gLastJsonSeenByOriginalIMP = nil;
|
|
215
|
+
NSDictionary *payload = [self unrelatedOneKeyDictionary];
|
|
216
|
+
id result = [self callConvert:standIn selectorName:kSpecSelector json:payload];
|
|
217
|
+
|
|
218
|
+
XCTAssertEqualObjects(gLastJsonSeenByOriginalIMP, payload);
|
|
219
|
+
XCTAssertEqualObjects(result, payload);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
- (void)testScheduleSwizzle_doesNotSwizzleUntilMainQueueRuns {
|
|
223
|
+
Class standIn = [self makeStandInConvertClass];
|
|
224
|
+
SEL selector = NSSelectorFromString(kSpecSelector);
|
|
225
|
+
IMP before = [self classMethodIMP:standIn selector:selector];
|
|
226
|
+
|
|
227
|
+
dispatch_once_t *onceToken = [self newOnceToken];
|
|
228
|
+
[RNFBNullSentinelInterceptor scheduleSwizzleOnMainQueueWithOnceToken:onceToken
|
|
229
|
+
turboConvertClass:standIn];
|
|
230
|
+
|
|
231
|
+
IMP afterSchedule = [self classMethodIMP:standIn selector:selector];
|
|
232
|
+
XCTAssertEqual(before, afterSchedule, @"swizzle must not run synchronously from schedule");
|
|
233
|
+
|
|
234
|
+
[self drainMainQueue];
|
|
235
|
+
|
|
236
|
+
IMP afterDrain = [self classMethodIMP:standIn selector:selector];
|
|
237
|
+
XCTAssertNotEqual(before, afterDrain, @"swizzle must run once the main queue drains");
|
|
238
|
+
|
|
239
|
+
gLastJsonSeenByOriginalIMP = nil;
|
|
240
|
+
[self callConvert:standIn selectorName:kSpecSelector json:[self nullSentinel]];
|
|
241
|
+
XCTAssertEqualObjects(gLastJsonSeenByOriginalIMP, [NSNull null]);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
- (void)testScheduleSwizzle_secondCallDoesNotDoubleWrap {
|
|
245
|
+
Class standIn = [self makeStandInConvertClass];
|
|
246
|
+
SEL selector = NSSelectorFromString(kSpecSelector);
|
|
247
|
+
|
|
248
|
+
dispatch_once_t *onceToken = [self newOnceToken];
|
|
249
|
+
[RNFBNullSentinelInterceptor scheduleSwizzleOnMainQueueWithOnceToken:onceToken
|
|
250
|
+
turboConvertClass:standIn];
|
|
251
|
+
[self drainMainQueue];
|
|
252
|
+
IMP afterFirst = [self classMethodIMP:standIn selector:selector];
|
|
253
|
+
|
|
254
|
+
[RNFBNullSentinelInterceptor scheduleSwizzleOnMainQueueWithOnceToken:onceToken
|
|
255
|
+
turboConvertClass:standIn];
|
|
256
|
+
[self drainMainQueue];
|
|
257
|
+
IMP afterSecond = [self classMethodIMP:standIn selector:selector];
|
|
258
|
+
|
|
259
|
+
XCTAssertEqual(afterFirst, afterSecond, @"dispatch_once must prevent a second IMP wrap");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Nil arm of `if (cxxConvertClass)`.
|
|
263
|
+
// Precondition: the unit host has no React runtime (RNFBAppUnitTests.xcodeproj links no React), so
|
|
264
|
+
// RCTCxxConvert is absent. The original missing-case test made the same assumption. The only test
|
|
265
|
+
// that registers a global RCTCxxConvert (the "present" test) disposes of it in tearDown, so the
|
|
266
|
+
// class is also absent here regardless of which test ran first.
|
|
267
|
+
- (void)testSwizzleRCTConvertMethods_missingRCTCxxConvert_isNoOp {
|
|
268
|
+
XCTAssertNil(NSClassFromString(kCxxConvertClassName));
|
|
269
|
+
XCTAssertNoThrow([RNFBNullSentinelInterceptor swizzleRCTConvertMethods]);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Non-nil arm of `if (cxxConvertClass)`. Registers a global RCTCxxConvert stand-in (disposed in
|
|
273
|
+
// tearDown). Same no-React-in-the-host precondition as the missing-class test above.
|
|
274
|
+
- (void)testSwizzleRCTConvertMethods_whenRCTCxxConvertPresent_swizzlesSpecMethods {
|
|
275
|
+
XCTAssertNil(NSClassFromString(kCxxConvertClassName),
|
|
276
|
+
@"no RCTCxxConvert may exist before this test registers its stand-in");
|
|
277
|
+
Class convertClass = [self registerClassNamed:kCxxConvertClassName
|
|
278
|
+
selectorNames:@[ kCxxConvertSpecSelector ]
|
|
279
|
+
global:YES];
|
|
280
|
+
XCTAssertNotNil(convertClass);
|
|
281
|
+
if (convertClass == Nil) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
SEL selector = NSSelectorFromString(kCxxConvertSpecSelector);
|
|
286
|
+
IMP before = [self classMethodIMP:convertClass selector:selector];
|
|
287
|
+
[RNFBNullSentinelInterceptor swizzleRCTConvertMethods];
|
|
288
|
+
IMP after = [self classMethodIMP:convertClass selector:selector];
|
|
289
|
+
XCTAssertNotEqual(before, after);
|
|
290
|
+
|
|
291
|
+
gLastJsonSeenByOriginalIMP = nil;
|
|
292
|
+
id result = [self callConvert:convertClass
|
|
293
|
+
selectorName:kCxxConvertSpecSelector
|
|
294
|
+
json:[self nullSentinel]];
|
|
295
|
+
XCTAssertEqualObjects(gLastJsonSeenByOriginalIMP, [NSNull null]);
|
|
296
|
+
XCTAssertEqualObjects(result, [NSNull null]);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
- (void)testSwizzleTurboModuleConversions_prefixWithoutSpec_leavesIMPUnchanged {
|
|
300
|
+
Class standIn = [self makeStandInConvertClass];
|
|
301
|
+
SEL notASpec = NSSelectorFromString(kNotASpecSelector);
|
|
302
|
+
SEL unrelated = NSSelectorFromString(kUnrelatedSelector);
|
|
303
|
+
IMP notASpecBefore = [self classMethodIMP:standIn selector:notASpec];
|
|
304
|
+
IMP unrelatedBefore = [self classMethodIMP:standIn selector:unrelated];
|
|
305
|
+
|
|
306
|
+
[RNFBNullSentinelInterceptor swizzleTurboModuleConversions:standIn];
|
|
307
|
+
|
|
308
|
+
XCTAssertEqual(notASpecBefore, [self classMethodIMP:standIn selector:notASpec]);
|
|
309
|
+
XCTAssertEqual(unrelatedBefore, [self classMethodIMP:standIn selector:unrelated]);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
@end
|