|
6 | 6 | using ObjCRuntime; |
7 | 7 | #endif |
8 | 8 |
|
| 9 | +#if ENABLE_RUNTIME_DRIFT_CASE_ANALYTICS_ONDEVICECONVERSION |
| 10 | +using Firebase.Analytics; |
| 11 | +using Foundation; |
| 12 | +using ObjCRuntime; |
| 13 | +#endif |
| 14 | + |
9 | 15 | #if ENABLE_RUNTIME_DRIFT_CASE_DATABASE_SERVERVALUE_INCREMENT |
10 | 16 | using Firebase.Database; |
11 | 17 | using Foundation; |
@@ -189,6 +195,106 @@ void OnMarshalObjectiveCException(object? sender, MarshalObjectiveCExceptionEven |
189 | 195 | } |
190 | 196 | #endif |
191 | 197 |
|
| 198 | +#if ENABLE_RUNTIME_DRIFT_CASE_ANALYTICS_ONDEVICECONVERSION |
| 199 | + static Task<string> VerifyAnalyticsOnDeviceConversionAsync() |
| 200 | + { |
| 201 | + var expectedSignatures = new (string MethodName, Type[] ParameterTypes, string Selector)[] |
| 202 | + { |
| 203 | + ( |
| 204 | + nameof(Analytics.InitiateOnDeviceConversionMeasurementWithEmailAddress), |
| 205 | + new[] { typeof(string) }, |
| 206 | + "initiateOnDeviceConversionMeasurementWithEmailAddress:"), |
| 207 | + ( |
| 208 | + nameof(Analytics.InitiateOnDeviceConversionMeasurementWithPhoneNumber), |
| 209 | + new[] { typeof(string) }, |
| 210 | + "initiateOnDeviceConversionMeasurementWithPhoneNumber:"), |
| 211 | + ( |
| 212 | + nameof(Analytics.InitiateOnDeviceConversionMeasurementWithHashedEmailAddress), |
| 213 | + new[] { typeof(NSData) }, |
| 214 | + "initiateOnDeviceConversionMeasurementWithHashedEmailAddress:"), |
| 215 | + ( |
| 216 | + nameof(Analytics.InitiateOnDeviceConversionMeasurementWithHashedPhoneNumber), |
| 217 | + new[] { typeof(NSData) }, |
| 218 | + "initiateOnDeviceConversionMeasurementWithHashedPhoneNumber:"), |
| 219 | + }; |
| 220 | + |
| 221 | + foreach (var (methodName, parameterTypes, selector) in expectedSignatures) |
| 222 | + { |
| 223 | + var signature = typeof(Analytics).GetMethod( |
| 224 | + methodName, |
| 225 | + BindingFlags.Static | BindingFlags.Public, |
| 226 | + binder: null, |
| 227 | + types: parameterTypes, |
| 228 | + modifiers: null); |
| 229 | + if (signature is null) |
| 230 | + { |
| 231 | + throw new InvalidOperationException( |
| 232 | + $"Expected managed API '{methodName}({string.Join(", ", parameterTypes.Select(type => type.FullName))})' was not found for selector '{selector}'."); |
| 233 | + } |
| 234 | + } |
| 235 | + |
| 236 | + var marshaledExceptionCaptured = false; |
| 237 | + MarshalObjectiveCExceptionMode? marshaledExceptionMode = null; |
| 238 | + |
| 239 | + void OnMarshalObjectiveCException(object? sender, MarshalObjectiveCExceptionEventArgs args) |
| 240 | + { |
| 241 | + marshaledExceptionCaptured = true; |
| 242 | + marshaledExceptionMode ??= args.ExceptionMode; |
| 243 | + } |
| 244 | + |
| 245 | + Runtime.MarshalObjectiveCException += OnMarshalObjectiveCException; |
| 246 | + try |
| 247 | + { |
| 248 | + using var hashedEmailAddress = NSData.FromArray(new byte[] |
| 249 | + { |
| 250 | + 0x9a, 0x79, 0x2f, 0x3d, 0xa4, 0x18, 0x7e, 0x1b, |
| 251 | + 0x02, 0xb7, 0x83, 0xfd, 0x0b, 0x41, 0x77, 0x57, |
| 252 | + 0x97, 0x7a, 0x2a, 0xaf, 0x62, 0x94, 0x6d, 0x12, |
| 253 | + 0x9d, 0x4c, 0xd2, 0x5a, 0x73, 0xb1, 0x3f, 0x31, |
| 254 | + }); |
| 255 | + using var hashedPhoneNumber = NSData.FromArray(new byte[] |
| 256 | + { |
| 257 | + 0x2d, 0x71, 0x16, 0x42, 0xb7, 0x26, 0xb0, 0x44, |
| 258 | + 0x01, 0x62, 0x7c, 0xa9, 0xfb, 0xac, 0x32, 0xf5, |
| 259 | + 0xc8, 0x53, 0x0f, 0x8d, 0x89, 0xc4, 0x6c, 0x2e, |
| 260 | + 0x42, 0xb8, 0x6e, 0xfd, 0xb0, 0x33, 0x84, 0xa8, |
| 261 | + }); |
| 262 | + |
| 263 | + try |
| 264 | + { |
| 265 | + Analytics.InitiateOnDeviceConversionMeasurementWithEmailAddress("codex@example.com"); |
| 266 | + Analytics.InitiateOnDeviceConversionMeasurementWithPhoneNumber("+15555550100"); |
| 267 | + Analytics.InitiateOnDeviceConversionMeasurementWithHashedEmailAddress(hashedEmailAddress); |
| 268 | + Analytics.InitiateOnDeviceConversionMeasurementWithHashedPhoneNumber(hashedPhoneNumber); |
| 269 | + } |
| 270 | + catch (ObjCException ex) |
| 271 | + { |
| 272 | + throw new InvalidOperationException( |
| 273 | + "Analytics on-device conversion selectors should not throw after the missing bindings are added, " + |
| 274 | + $"but observed {ex.GetType().FullName}. " + |
| 275 | + $"String argument type: {typeof(string).FullName}. Hashed argument type: {typeof(NSData).FullName}. " + |
| 276 | + $"Marshal mode: {FormatDetail(marshaledExceptionMode?.ToString())}.", |
| 277 | + ex); |
| 278 | + } |
| 279 | + |
| 280 | + if (marshaledExceptionCaptured) |
| 281 | + { |
| 282 | + throw new InvalidOperationException( |
| 283 | + "Analytics on-device conversion selectors completed, but Runtime.MarshalObjectiveCException captured an unexpected Objective-C exception. " + |
| 284 | + $"Marshal mode: {FormatDetail(marshaledExceptionMode?.ToString())}."); |
| 285 | + } |
| 286 | + |
| 287 | + return Task.FromResult( |
| 288 | + "Analytics on-device conversion selectors completed without ObjC exception. " + |
| 289 | + $"String argument type: {typeof(string).FullName}. Hashed argument type: {typeof(NSData).FullName}."); |
| 290 | + } |
| 291 | + finally |
| 292 | + { |
| 293 | + Runtime.MarshalObjectiveCException -= OnMarshalObjectiveCException; |
| 294 | + } |
| 295 | + } |
| 296 | +#endif |
| 297 | + |
192 | 298 | #if ENABLE_RUNTIME_DRIFT_CASE_DATABASE_SERVERVALUE_INCREMENT |
193 | 299 | static Task<string> VerifyDatabaseServerValueIncrementAsync() |
194 | 300 | { |
|
0 commit comments