Task TestTransactionDispose()

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.
        }
      });
    }