Task TestTerminate()

in firestore/testapp/Assets/Firebase/Sample/Firestore/UIHandlerAutomated.cs [2454:2580]


    Task TestTerminate() {
      return Async(() => {
        var db1 = NonDefaultFirestore("TestTerminate");
        FirebaseApp app = db1.App;
        var doc = db1.Document("ColA/DocA/ColB/DocB");
        var doc2 = db1.Document("ColA/DocA/ColB/DocC");
        var collection = doc.Parent;
        var writeBatch = db1.StartBatch();
        var accumulator = new EventAccumulator<DocumentSnapshot>(MainThreadId, FailTest);
        var registration = doc.Listen(accumulator.Listener);

        // Multiple calls to terminate should go through.
        AssertTaskSucceeds(db1.TerminateAsync());
        AssertTaskSucceeds(db1.TerminateAsync());

        // Can call registration.Stop multiple times even after termination.
        registration.Stop();
        registration.Stop();

        var taskCompletionSource = new TaskCompletionSource<string>();
        taskCompletionSource.SetException(new InvalidOperationException("forced exception"));
        Task sampleUntypedTask = taskCompletionSource.Task;
        Task<string> sampleTypedTask = taskCompletionSource.Task;

        // Verify that the `App` property is still valid after `TerminateAsync()`.
        Assert("App property should not have changed", db1.App == app);

        // Verify that synchronous methods in `FirebaseFirestore` still return values after
        // `TerminateAsync()`.
        AssertNotNull("Collection() returned null", db1.Collection("a"));
        AssertNotNull("CollectionGroup() returned null", db1.CollectionGroup("c"));
        AssertNotNull("Document() returned null", db1.Document("a/b"));
        AssertNotNull("StartBatch() returned null", db1.StartBatch());

        // Verify that adding a listener via `ListenForSnapshotsInSync()` is not notified
        // after `TerminateAsync()`.
        bool snapshotsInSyncListenerInvoked = false;
        Action snapshotsInSyncListener = () => { snapshotsInSyncListenerInvoked = true; };
#if UNITY_ANDROID
        AssertException(typeof(InvalidOperationException),
                        () => db1.ListenForSnapshotsInSync(snapshotsInSyncListener));
#else
        // TODO(b/201438171) `ListenForSnapshotsInSync()` should throw `InvalidOperationException`,
        // like it does on Android and like `CollectionReference.Listen()` and
        // `DocumentReference.Listen()` do.
        db1.ListenForSnapshotsInSync(snapshotsInSyncListener);
#endif

        // Verify that all non-static methods in `FirebaseFirestore` that start a `Task`, other
        // than `ClearPersistenceAsync()` fail.
        AssertTaskFaults(typeof(InvalidOperationException),
                         db1.RunTransactionAsync(transaction => sampleUntypedTask));
        AssertTaskFaults(typeof(InvalidOperationException),
                         db1.RunTransactionAsync<string>(transaction => sampleTypedTask));
        // TODO(b/201098348) Change `AssertException()` to `AssertTaskFaults()` for the 5 bundle
        // loading methods below, since they should be consistent with the rest of the methods.
        AssertException(typeof(InvalidOperationException), () => db1.LoadBundleAsync("BundleData"));
        AssertException(typeof(InvalidOperationException),
                        () => db1.LoadBundleAsync("BundleData", (sender, progress) => {}));
        AssertException(typeof(InvalidOperationException), () => db1.LoadBundleAsync(new byte[0]));
        AssertException(typeof(InvalidOperationException),
                        () => db1.LoadBundleAsync(new byte[0], (sender, progress) => {}));
        AssertException(typeof(InvalidOperationException),
                        () => db1.GetNamedQueryAsync("QueryName"));
        AssertTaskFaults(typeof(InvalidOperationException), db1.DisableNetworkAsync());
        AssertTaskFaults(typeof(InvalidOperationException), db1.EnableNetworkAsync());
        AssertTaskFaults(typeof(InvalidOperationException), db1.WaitForPendingWritesAsync());

        // Verify the behavior of methods in `CollectionReference` after `TerminateAsync()`.
        Assert("collection.Firestore is not correct", collection.Firestore == db1);
        AssertEq(collection.Id, "ColB");
        AssertEq(collection.Path, "ColA/DocA/ColB");
        bool collectionListenerInvoked = false;
        Action<QuerySnapshot> collectionListener = snap => { collectionListenerInvoked = true; };
        AssertException(typeof(InvalidOperationException),
                        () => collection.Listen(collectionListener));
        AssertNotNull("Collection.Document() returned null", collection.Document());
        AssertNotNull("Collection.Document(string) returned null", collection.Document("abc"));
        AssertTaskFaults(typeof(InvalidOperationException), collection.AddAsync(TestData(1)));
        // TODO(b/201438328) Change `AssertException()` to `AssertTaskFaults()` for
        // `GetSnapshotAsync()`, since it should be consistent with other methods.
        AssertException(typeof(InvalidOperationException),
                        () => collection.GetSnapshotAsync(Source.Default));

        // Verify the behavior of methods in `DocumentReference` after `TerminateAsync()`.
        Assert("doc.Firestore is not correct", doc.Firestore == db1);
        AssertEq(doc.Id, "DocB");
        AssertEq(doc.Path, "ColA/DocA/ColB/DocB");
        AssertEq(doc.Parent, collection);
        bool docListenerInvoked = false;
        Action<DocumentSnapshot> docListener = snap => { docListenerInvoked = true; };
        AssertException(typeof(InvalidOperationException), () => doc.Listen(docListener));
        AssertNotNull("DocumentReference.Collection() returned null", doc.Collection("zzz"));
        AssertTaskFaults(typeof(InvalidOperationException), doc.DeleteAsync());
        AssertTaskFaults(typeof(InvalidOperationException), doc.SetAsync(TestData(1)));
        AssertTaskFaults(typeof(InvalidOperationException), doc.UpdateAsync("life", 42));
        // TODO(b/201438328) Change `AssertException()` to `AssertTaskFaults()` for
        // `GetSnapshotAsync()`, since it should be consistent with other methods.
        AssertException(typeof(InvalidOperationException),
                        () => doc.GetSnapshotAsync(Source.Default));

        // Verify the behavior of methods in `WriteBatch` after `TerminateAsync()`.
        writeBatch.Delete(doc);
        writeBatch.Set(doc2, TestData(1), null);
        AssertTaskFaults(typeof(InvalidOperationException), writeBatch.CommitAsync());

        // Verify that the listeners were not notified. Do it here instead of above to allow
        // some time to pass for unexpected notifications to be received.
        Assert("The listener registered with FirebaseFirestore.ListenForSnapshotsInSync() " +
                   "should not be notified after TerminateAsync()",
               !snapshotsInSyncListenerInvoked);
        Assert("The listener registered with CollectionReference.Listen() " +
                   "should not be notified after TerminateAsync()",
               !collectionListenerInvoked);
        Assert("The listener registered with DocumentReference.Listen() " +
                   "should not be notified after TerminateAsync()",
               !docListenerInvoked);

        // Create a new functional instance.
        var db2 = FirebaseFirestore.GetInstance(app);
        Assert("Should create a new instance.", db1 != db2);
        AssertTaskSucceeds(db2.DisableNetworkAsync());
        AssertTaskSucceeds(db2.EnableNetworkAsync());

        app.Dispose();
      });
    }