Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions source/Firebase/CloudFirestore/ApiDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,24 @@ interface FieldValue
[Export ("fieldValueForIntegerIncrement:")]
FieldValue FromIntegerIncrement (long l);

// +(FIRVectorValue * _Nonnull)vectorWithArray:(NSArray<NSNumber *> * _Nonnull)array;
[Static]
[Export ("vectorWithArray:")]
VectorValue VectorWithArray (NSNumber [] array);
}

// @interface FIRVectorValue : NSObject
[DisableDefaultCtor]
[BaseType (typeof (NSObject), Name = "FIRVectorValue")]
interface VectorValue
{
// @property (atomic, readonly) NSArray<NSNumber *> * _Nonnull array;
[Export ("array")]
NSNumber [] Array { get; }

// -(instancetype _Nonnull)initWithArray:(NSArray<NSNumber *> * _Nonnull)array;
[Export ("initWithArray:")]
NativeHandle Constructor (NSNumber [] array);
}

// void (^)(id _Nullable result, NSError *_Nullable error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@
using ObjCRuntime;
#endif

#if ENABLE_RUNTIME_DRIFT_CASE_CLOUDFIRESTORE_FIELDVALUE_VECTORWITHARRAY
using Firebase.CloudFirestore;
using Foundation;
using ObjCRuntime;
#endif

#if ENABLE_RUNTIME_DRIFT_CASE_CLOUDFUNCTIONS_USEFUNCTIONSEMULATORORIGIN
using Firebase.CloudFunctions;
using Foundation;
Expand Down Expand Up @@ -871,6 +877,105 @@ void OnMarshalObjectiveCException(object? sender, MarshalObjectiveCExceptionEven
}
#endif

#if ENABLE_RUNTIME_DRIFT_CASE_CLOUDFIRESTORE_FIELDVALUE_VECTORWITHARRAY
static Task<string> VerifyCloudFirestoreFieldValueVectorWithArrayAsync()
{
const string selector = "vectorWithArray:";

var signature = typeof(FieldValue).GetMethod(
nameof(FieldValue.VectorWithArray),
BindingFlags.Static | BindingFlags.Public,
binder: null,
types: new[] { typeof(NSNumber[]) },
modifiers: null);
if (signature is null)
{
throw new InvalidOperationException(
$"Expected managed API '{nameof(FieldValue.VectorWithArray)}({typeof(NSNumber[]).FullName})' was not found.");
}

if (signature.ReturnType != typeof(VectorValue))
{
throw new InvalidOperationException(
$"Managed signature regression: expected '{nameof(FieldValue.VectorWithArray)}' to return '{typeof(VectorValue).FullName}', observed '{signature.ReturnType.FullName}'.");
}

var vectorConstructor = typeof(VectorValue).GetConstructor(
BindingFlags.Instance | BindingFlags.Public,
binder: null,
types: new[] { typeof(NSNumber[]) },
modifiers: null);
if (vectorConstructor is null)
{
throw new InvalidOperationException(
$"Expected managed API '{nameof(VectorValue)}({typeof(NSNumber[]).FullName})' was not found.");
}

using var first = NSNumber.FromInt64(1);
using var second = NSNumber.FromInt64(2);
var values = new[] { first, second };
NSException? marshaledException = null;
MarshalObjectiveCExceptionMode? marshaledExceptionMode = null;
var vectorArrayLength = 0;

void OnMarshalObjectiveCException(object? sender, MarshalObjectiveCExceptionEventArgs args)
{
marshaledException ??= args.Exception;
marshaledExceptionMode ??= args.ExceptionMode;
}

Runtime.MarshalObjectiveCException += OnMarshalObjectiveCException;
try
{
try
{
using var fieldValue = FieldValue.VectorWithArray(values);
if (fieldValue is null)
{
throw new InvalidOperationException(
$"Selector '{selector}' returned null for a valid NSNumber array.");
}

using var vectorValue = new VectorValue(values);
var vectorArray = vectorValue.Array;
vectorArrayLength = vectorArray.Length;
if (vectorArrayLength != values.Length)
{
throw new InvalidOperationException(
$"VectorValue.Array returned {vectorArrayLength} values, expected {values.Length}.");
}
}
catch (ObjCException ex)
{
throw new InvalidOperationException(
$"Selector '{selector}' should not throw after the missing binding is added, but observed {ex.GetType().FullName}. " +
$"Managed vector argument type: {values.GetType().FullName}. " +
$"Vector value type: {typeof(VectorValue).FullName}. " +
$"NSException.Name: {FormatDetail(marshaledException?.Name?.ToString())}. " +
$"NSException.Reason: {FormatDetail(marshaledException?.Reason)}. " +
$"Marshal mode: {FormatDetail(marshaledExceptionMode?.ToString())}.",
ex);
}

if (marshaledException is not null)
{
throw new InvalidOperationException(
$"Selector '{selector}' completed, but Runtime.MarshalObjectiveCException captured unexpected NSException.Name '{marshaledException.Name}'. " +
$"Reason: {FormatDetail(marshaledException.Reason)}. Marshal mode: {FormatDetail(marshaledExceptionMode?.ToString())}.");
}

return Task.FromResult(
$"Selector '{selector}' returned a VectorValue without ObjC exception after the missing binding was added. " +
$"Managed vector argument type: {values.GetType().FullName}. " +
$"Return type: {signature.ReturnType.FullName}. Vector array length: {vectorArrayLength}.");
}
finally
{
Runtime.MarshalObjectiveCException -= OnMarshalObjectiveCException;
}
}
#endif

#if ENABLE_RUNTIME_DRIFT_CASE_CLOUDFUNCTIONS_USEFUNCTIONSEMULATORORIGIN
static Task<string> VerifyCloudFunctionsUseFunctionsEmulatorOriginAsync()
{
Expand Down
11 changes: 11 additions & 0 deletions tests/E2E/Firebase.Foundation/runtime-drift-cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@
}
]
},
{
"id": "cloudfirestore-fieldvalue-vectorwitharray",
"method": "VerifyCloudFirestoreFieldValueVectorWithArrayAsync",
"bindingPackage": "AdamE.Firebase.iOS.CloudFirestore",
"packages": [
{
"id": "AdamE.Firebase.iOS.CloudFirestore",
"version": "12.6.0"
}
]
},
{
"id": "cloudfunctions-usefunctionsemulatororigin",
"method": "VerifyCloudFunctionsUseFunctionsEmulatorOriginAsync",
Expand Down
Loading