in firestore/testapp/Assets/Firebase/Sample/Firestore/UIHandlerAutomated.cs [1205:1387]
Task TestTransactionDispose() {
return Async(() => {
// Verify that the Transaction is disposed if the callback returns a null Task.
// TODO(b/197348363) Re-enable this test on Android once the bug where returning null from
// the callback specified to `RunTransactionAsync()` causes the test to hang is fixed.
#if !UNITY_ANDROID
{
DocumentReference doc = TestDocument();
Transaction capturedTransaction = null;
Task txnTask = db.RunTransactionAsync(transaction => {
capturedTransaction = transaction;
return null;
});
AssertTaskFaults(txnTask);
AssertTransactionMethodsThrow(capturedTransaction, doc);
}
#endif
// Verify that the Transaction is disposed if the callback throws an exception.
{
DocumentReference doc = TestDocument();
Transaction capturedTransaction = null;
Task txnTask = db.RunTransactionAsync(transaction => {
capturedTransaction = transaction;
throw new InvalidOperationException("forced exception");
});
AssertTaskFaults(txnTask);
AssertTransactionMethodsThrow(capturedTransaction, doc);
}
// Verify that the Transaction is disposed if the callback returns a Task that succeeds.
{
DocumentReference doc = TestDocument();
Transaction capturedTransaction = null;
Task txnTask = db.RunTransactionAsync(transaction => {
return transaction.GetSnapshotAsync(doc).ContinueWith(task => {
// Call a method on Transaction to ensure that it does not throw an exception.
transaction.Set(doc, new Dictionary<string, object> { { "answer", 42 } });
capturedTransaction = transaction;
});
});
AssertTaskSucceeds(txnTask);
AssertTransactionMethodsThrow(capturedTransaction, doc);
}
// Verify that the Transaction is disposed if the callback returns a Task that faults.
{
DocumentReference doc = TestDocument();
Transaction capturedTransaction = null;
Task txnTask = db.RunTransactionAsync(transaction => {
capturedTransaction = transaction;
var taskCompletionSource = new TaskCompletionSource<object>();
taskCompletionSource.SetException(new InvalidOperationException("forced exception"));
return taskCompletionSource.Task;
});
AssertTaskFaults(txnTask);
AssertTransactionMethodsThrow(capturedTransaction, doc);
}
// Verify that the Transaction is disposed when the Firestore instance terminated.
{
FirebaseApp customApp = FirebaseApp.Create(db.App.Options, "transaction-terminate");
FirebaseFirestore customDb = FirebaseFirestore.GetInstance(customApp);
DocumentReference doc = customDb.Document(TestDocument().Path);
var barrier = new BarrierCompat(2);
Transaction capturedTransaction = null;
customDb.RunTransactionAsync(transaction => {
return Task.Factory.StartNew<object>(() => {
capturedTransaction = transaction;
barrier.SignalAndWait();
barrier.SignalAndWait();
return null;
});
});
try {
barrier.SignalAndWait();
AssertTaskSucceeds(customDb.TerminateAsync());
// TODO(b/201415845) Remove the following two assertions once the commented call to
// `AssertTaskFaults` below is uncommented. With that code commented the C# compiler
// complains about these two variables being "unused".
AssertNotNull("dummy call to prevent unused variable warning", doc);
AssertNotNull("dummy call to prevent unused variable warning", capturedTransaction);
// TODO(b/201415845) Uncomment this check once the crash on iOS and hang on Android
// that it causes is fixed.
// AssertTaskFaults(typeof(InvalidOperationException),
// capturedTransaction.GetSnapshotAsync(doc));
} finally {
barrier.SignalAndWait();
}
customApp.Dispose();
// TODO(b/171568274): Add an assertion that the Task returned from RunTransactionAsync()
// either completes or faults once the inconsistent behavior is fixed.
}
// Verify that the Transaction is disposed when the Firestore instance is disposed.
{
FirebaseApp customApp = FirebaseApp.Create(db.App.Options, "transaction-dispose1");
FirebaseFirestore customDb = FirebaseFirestore.GetInstance(customApp);
DocumentReference doc = customDb.Document(TestDocument().Path);
var barrier = new BarrierCompat(2);
Transaction capturedTransaction = null;
Task capturedTask = null;
customDb.RunTransactionAsync(transaction => {
capturedTask = Task.Factory.StartNew<object>(() => {
capturedTransaction = transaction;
barrier.SignalAndWait();
barrier.SignalAndWait();
return null;
});
return capturedTask;
});
try {
barrier.SignalAndWait();
customApp.Dispose();
AssertTaskIsPending(capturedTask);
AssertTransactionMethodsThrow(capturedTransaction, doc);
AssertTaskIsPending(capturedTask);
} finally {
barrier.SignalAndWait();
}
AssertTaskSucceeds(capturedTask);
// TODO(b/171568274): Add an assertion that the Task returned from RunTransactionAsync()
// either completes or faults once the inconsistent behavior is fixed.
}
// Verify that the Transaction is disposed when the Firestore instance is disposed
// directly from the transaction callback.
{
FirebaseApp customApp = FirebaseApp.Create(db.App.Options, "transaction-dispose2");
FirebaseFirestore customDb = FirebaseFirestore.GetInstance(customApp);
DocumentReference doc = customDb.Document(TestDocument().Path);
var barrier = new BarrierCompat(2);
Transaction capturedTransaction = null;
customDb.RunTransactionAsync(transaction => {
capturedTransaction = transaction;
customApp.Dispose();
barrier.SignalAndWait();
barrier.SignalAndWait();
var taskCompletionSource = new TaskCompletionSource<object>();
taskCompletionSource.SetResult(null);
return taskCompletionSource.Task;
});
try {
barrier.SignalAndWait();
AssertTransactionMethodsThrow(capturedTransaction, doc);
} finally {
barrier.SignalAndWait();
}
// TODO(b/171568274): Add an assertion that the Task returned from RunTransactionAsync()
// either completes or faults once the inconsistent behavior is fixed.
}
// Verify that the Transaction is disposed when the Firestore instance is disposed
// from the task returned from the transaction callback.
{
FirebaseApp customApp = FirebaseApp.Create(db.App.Options, "transaction-dispose3");
FirebaseFirestore customDb = FirebaseFirestore.GetInstance(customApp);
DocumentReference doc = customDb.Document(TestDocument().Path);
var barrier = new BarrierCompat(2);
Transaction capturedTransaction = null;
Task capturedTask = null;
customDb.RunTransactionAsync(transaction => {
capturedTask = Task.Factory.StartNew<object>(() => {
capturedTransaction = transaction;
customApp.Dispose();
barrier.SignalAndWait();
barrier.SignalAndWait();
return null;
});
return capturedTask;
});
try {
barrier.SignalAndWait();
AssertTaskIsPending(capturedTask);
AssertTransactionMethodsThrow(capturedTransaction, doc);
} finally {
barrier.SignalAndWait();
}
// TODO(b/171568274): Add an assertion that the Task returned from RunTransactionAsync()
// either completes or faults once the inconsistent behavior is fixed.
}
});
}