Skip to content

Commit 9d8841e

Browse files
committed
Add test to check task failure upon user change.
1 parent 74603b8 commit 9d8841e

File tree

5 files changed

+85
-9
lines changed

5 files changed

+85
-9
lines changed

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/FirestoreTest.java

+31-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import static com.google.firebase.firestore.AccessHelper.getAsyncQueue;
1818
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.newTestSettings;
1919
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.provider;
20+
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testChangeUserTo;
2021
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testCollection;
2122
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testCollectionWithDocs;
2223
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testDocument;
@@ -44,6 +45,7 @@
4445
import com.google.firebase.Timestamp;
4546
import com.google.firebase.firestore.FirebaseFirestoreException.Code;
4647
import com.google.firebase.firestore.Query.Direction;
48+
import com.google.firebase.firestore.auth.User;
4749
import com.google.firebase.firestore.testutil.EventAccumulator;
4850
import com.google.firebase.firestore.testutil.IntegrationTestUtil;
4951
import com.google.firebase.firestore.util.AsyncQueue.TimerId;
@@ -1098,23 +1100,47 @@ public void testShutdownCalledMultipleTimes() {
10981100
}
10991101

11001102
@Test
1101-
public void testAwaitPendingWritesResolves() {
1103+
public void testWaitForPendingWritesResolves() {
11021104
DocumentReference documentReference = testCollection("abc").document("123");
11031105
FirebaseFirestore firestore = documentReference.getFirestore();
11041106
Map<String, Object> data = map("foo", "bar");
11051107

11061108
waitFor(firestore.disableNetwork());
1107-
Task<Void> awaitsPendingWrites1 = firestore.awaitPendingWrites();
1109+
Task<Void> awaitsPendingWrites1 = firestore.waitForPendingWrites();
11081110
Task<Void> pendingWrite = documentReference.set(data);
1109-
Task<Void> awaitsPendingWrites2 = firestore.awaitPendingWrites();
1111+
Task<Void> awaitsPendingWrites2 = firestore.waitForPendingWrites();
11101112

1111-
assertTrue(!awaitsPendingWrites1.isComplete());
1113+
// `awaitsPendingWrites1` is complete immediately because there is no pending writes at
1114+
// the time it is created.
1115+
waitFor(awaitsPendingWrites1);
1116+
assertTrue(awaitsPendingWrites1.isComplete() && awaitsPendingWrites1.isSuccessful());
11121117
assertTrue(!pendingWrite.isComplete());
11131118
assertTrue(!awaitsPendingWrites2.isComplete());
11141119

11151120
waitFor(firestore.enableNetwork());
11161121
waitFor(pendingWrite);
1117-
waitFor(awaitsPendingWrites1);
11181122
waitFor(awaitsPendingWrites2);
1123+
assertTrue(awaitsPendingWrites2.isComplete() && awaitsPendingWrites2.isSuccessful());
1124+
}
1125+
1126+
@Test
1127+
public void testWaitForPendingWritesFailsWhenUserChanges() {
1128+
DocumentReference documentReference = testCollection("abc").document("123");
1129+
FirebaseFirestore firestore = documentReference.getFirestore();
1130+
Map<String, Object> data = map("foo", "bar");
1131+
1132+
// Prevent pending writes receiving acknowledgement.
1133+
waitFor(firestore.disableNetwork());
1134+
Task<Void> pendingWrite = documentReference.set(data);
1135+
Task<Void> awaitsPendingWrites = firestore.waitForPendingWrites();
1136+
assertTrue(!pendingWrite.isComplete());
1137+
assertTrue(!awaitsPendingWrites.isComplete());
1138+
1139+
testChangeUserTo(new User("new user"));
1140+
1141+
assertTrue(!pendingWrite.isComplete());
1142+
assertEquals(
1143+
"'waitForPendingWrites' task is cancelled due to User change.",
1144+
waitForException(awaitsPendingWrites).getMessage());
11191145
}
11201146
}

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/testutil/IntegrationTestUtil.java

+35-1
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@
3636
import com.google.firebase.firestore.MetadataChanges;
3737
import com.google.firebase.firestore.QuerySnapshot;
3838
import com.google.firebase.firestore.auth.EmptyCredentialsProvider;
39+
import com.google.firebase.firestore.auth.User;
3940
import com.google.firebase.firestore.core.DatabaseInfo;
4041
import com.google.firebase.firestore.local.Persistence;
4142
import com.google.firebase.firestore.model.DatabaseId;
4243
import com.google.firebase.firestore.testutil.provider.FirestoreProvider;
4344
import com.google.firebase.firestore.util.AsyncQueue;
45+
import com.google.firebase.firestore.util.Listener;
4446
import com.google.firebase.firestore.util.Logger;
4547
import com.google.firebase.firestore.util.Logger.Level;
4648
import java.util.ArrayList;
@@ -53,6 +55,34 @@
5355
import java.util.concurrent.TimeUnit;
5456
import java.util.concurrent.TimeoutException;
5557

58+
class MockCredentialsProvider extends EmptyCredentialsProvider {
59+
60+
private static MockCredentialsProvider instance;
61+
62+
public static MockCredentialsProvider instance() {
63+
if (MockCredentialsProvider.instance == null) {
64+
MockCredentialsProvider.instance = new MockCredentialsProvider();
65+
}
66+
return MockCredentialsProvider.instance;
67+
}
68+
69+
private MockCredentialsProvider() {
70+
super();
71+
}
72+
73+
@Override
74+
public void setChangeListener(Listener<User> changeListener) {
75+
super.setChangeListener(changeListener);
76+
this.listener = changeListener;
77+
}
78+
79+
public void changeUserTo(User user) {
80+
listener.onValue(user);
81+
}
82+
83+
private Listener<User> listener;
84+
}
85+
5686
/** A set of helper methods for tests */
5787
public class IntegrationTestUtil {
5888

@@ -239,7 +269,7 @@ public static FirebaseFirestore testFirestore(
239269
context,
240270
databaseId,
241271
persistenceKey,
242-
new EmptyCredentialsProvider(),
272+
MockCredentialsProvider.instance(),
243273
asyncQueue,
244274
/*firebaseApp=*/ null,
245275
/*instanceRegistry=*/ (dbId) -> {});
@@ -409,4 +439,8 @@ public static Map<String, Object> toDataMap(QuerySnapshot qrySnap) {
409439
public static boolean isRunningAgainstEmulator() {
410440
return CONNECT_TO_EMULATOR;
411441
}
442+
443+
public static void testChangeUserTo(User user) {
444+
MockCredentialsProvider.instance().changeUserTo(user);
445+
}
412446
}

firebase-firestore/src/main/java/com/google/firebase/firestore/FirebaseFirestore.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,8 @@ Task<Void> shutdown() {
370370
*
371371
* @return A {@link Task} which resolves when all pending writes are acknowledged by the server.
372372
*/
373-
Task<Void> awaitPendingWrites() {
374-
return client.awaitPendingWrites();
373+
Task<Void> waitForPendingWrites() {
374+
return client.waitForPendingWrites();
375375
}
376376

377377
@VisibleForTesting

firebase-firestore/src/main/java/com/google/firebase/firestore/core/FirestoreClient.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ public <TResult> Task<TResult> transaction(
228228
* Returns a task resolves when all the pending writes at the time when this method is called
229229
* received server acknowledgement. An acknowledgement can be either acceptance or rejections.
230230
*/
231-
public Task<Void> awaitPendingWrites() {
231+
public Task<Void> waitForPendingWrites() {
232232
this.verifyNotShutdown();
233233
if (!remoteStore.canUseNetwork()) {
234234
Logger.warn(

firebase-firestore/src/main/java/com/google/firebase/firestore/core/SyncEngine.java

+16
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ public void registerPendingWritesTask(TaskCompletionSource<Void> userTask) {
450450
if (largestPendingBatchId == 0) {
451451
// Complete the task right away if there is no pending writes at the moment.
452452
userTask.setResult(null);
453+
return;
453454
}
454455

455456
if (pendingWritesCallbacks.containsKey(largestPendingBatchId)) {
@@ -470,6 +471,20 @@ private void resolveTasksAwaitingForPendingWritesIfAny(int batchId) {
470471
}
471472
}
472473

474+
private void failOutstandingPendingWritesAwaitingTasks() {
475+
for (Map.Entry<Integer, List<TaskCompletionSource<Void>>> entry :
476+
pendingWritesCallbacks.entrySet()) {
477+
for (TaskCompletionSource<Void> task : entry.getValue()) {
478+
task.setException(
479+
new FirebaseFirestoreException(
480+
"'waitForPendingWrites' task is cancelled due to User change.",
481+
FirebaseFirestoreException.Code.CANCELLED));
482+
}
483+
}
484+
485+
pendingWritesCallbacks.clear();
486+
}
487+
473488
/** Resolves the task corresponding to this write result. */
474489
private void notifyUser(int batchId, @Nullable Status status) {
475490
Map<Integer, TaskCompletionSource<Void>> userTasks = mutationUserCallbacks.get(currentUser);
@@ -605,6 +620,7 @@ public void handleCredentialChange(User user) {
605620
// Notify local store and emit any resulting events from swapping out the mutation queue.
606621
ImmutableSortedMap<DocumentKey, MaybeDocument> changes = localStore.handleUserChange(user);
607622
emitNewSnapsAndNotifyLocalStore(changes, /*remoteEvent=*/ null);
623+
failOutstandingPendingWritesAwaitingTasks();
608624
}
609625

610626
// Notify remote store so it can restart its streams.

0 commit comments

Comments
 (0)