From 37d49ac37d3f50eaf07f6bc760788694a9977226 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 5 Feb 2024 12:44:05 -0500 Subject: [PATCH 01/17] change api interface --- .../FIRSnapshotListenOptions.h | 15 +++++ Firestore/Source/API/FIRDocumentReference.mm | 7 ++ Firestore/Source/API/FIRQuery.mm | 6 ++ .../Source/API/FIRSnapshotListenOptions.mm | 64 +++++++++++++++++++ .../FirebaseFirestore/FIRDocumentReference.h | 18 ++++++ .../Public/FirebaseFirestore/FIRQuery.h | 16 +++++ .../FIRSnapshotListenOptions.h | 39 +++++++++++ .../FirebaseFirestore/FirebaseFirestore.h | 1 + 8 files changed, 166 insertions(+) create mode 100644 FirebaseFirestoreInternal/FirebaseFirestore/FIRSnapshotListenOptions.h create mode 100644 Firestore/Source/API/FIRSnapshotListenOptions.mm create mode 100644 Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h diff --git a/FirebaseFirestoreInternal/FirebaseFirestore/FIRSnapshotListenOptions.h b/FirebaseFirestoreInternal/FirebaseFirestore/FIRSnapshotListenOptions.h new file mode 100644 index 00000000000..a98dcc88a8a --- /dev/null +++ b/FirebaseFirestoreInternal/FirebaseFirestore/FIRSnapshotListenOptions.h @@ -0,0 +1,15 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import \ No newline at end of file diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index b6f326ee8d9..49a8b000956 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -212,6 +212,13 @@ - (void)getDocumentWithSource:(FIRFirestoreSource)source return [self addSnapshotListenerInternalWithOptions:options listener:listener]; } +- (id)addSnapshotListenerWithOptions:(FIRSnapshotListenOptions *)options + listener:(FIRDocumentSnapshotBlock)listener { + // Mila + ListenOptions listenOptions = ListenOptions::FromIncludeMetadataChanges(false); + return [self addSnapshotListenerInternalWithOptions:listenOptions listener:listener]; +} + - (id)addSnapshotListenerInternalWithOptions:(ListenOptions)internalOptions listener:(FIRDocumentSnapshotBlock) listener { diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 98518183049..4adba59744f 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -191,6 +191,12 @@ - (void)getDocumentsWithSource:(FIRFirestoreSource)publicSource return [self addSnapshotListenerInternalWithOptions:options listener:listener]; } +- (id)addSnapshotListenerWithOptions:(FIRSnapshotListenOptions *)options + listener:(FIRQuerySnapshotBlock)listener { + auto listenOptions = ListenOptions::FromIncludeMetadataChanges(false); + return [self addSnapshotListenerInternalWithOptions:listenOptions listener:listener]; +} + - (id)addSnapshotListenerInternalWithOptions:(ListenOptions)internalOptions listener: (FIRQuerySnapshotBlock)listener { diff --git a/Firestore/Source/API/FIRSnapshotListenOptions.mm b/Firestore/Source/API/FIRSnapshotListenOptions.mm new file mode 100644 index 00000000000..a194bb8c227 --- /dev/null +++ b/Firestore/Source/API/FIRSnapshotListenOptions.mm @@ -0,0 +1,64 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRSnapshotListenOptions.h" + +#import + +#include +#include + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRSnapshotListenOptions + +// private method +- (instancetype)initPrivateWithSource:(FIRListenSource)source + includeMetadataChanges:(BOOL)includeMetadataChanges { + self = [self init]; + if (self) { + _source = source; + _includeMetadataChanges = includeMetadataChanges; + } + return self; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _source = FIRListenSourceDefault; + _includeMetadataChanges = NO; + } + return self; +} + +- (FIRSnapshotListenOptions *)optionsWithIncludeMetadataChanges:(BOOL)includeMetadataChanges { + FIRSnapshotListenOptions *newOptions = + [[FIRSnapshotListenOptions alloc] initPrivateWithSource:self.source + includeMetadataChanges:includeMetadataChanges]; + return newOptions; +} + +- (FIRSnapshotListenOptions *)optionsWithSource:(FIRListenSource)source { + FIRSnapshotListenOptions *newOptions = + [[FIRSnapshotListenOptions alloc] initPrivateWithSource:source + includeMetadataChanges:self.includeMetadataChanges]; + return newOptions; +} + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h b/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h index 70dbd0d02ad..a7437b7138b 100644 --- a/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h +++ b/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h @@ -18,6 +18,7 @@ #import "FIRFirestoreSource.h" #import "FIRListenerRegistration.h" +#import "FIRSnapshotListenOptions.h" @class FIRCollectionReference; @class FIRDocumentSnapshot; @@ -270,6 +271,23 @@ addSnapshotListenerWithIncludeMetadataChanges:(BOOL)includeMetadataChanges NS_SWIFT_NAME(addSnapshotListener(includeMetadataChanges:listener:)); // clang-format on +/** + * Attaches a listener for `DocumentSnapshot` events. + * + * @param options Sets snapshot listen options, including whether metadata-only changes should + * trigger snapshot events, the source that listens to, the executor to use to call the + * listener, or the activity to scope the listener to. + * @param listener The listener to attach. + * + * @return A `ListenerRegistration` that can be used to remove this listener. + */ +// clang-format off +- (id)addSnapshotListenerWithOptions: + (FIRSnapshotListenOptions *)options + listener:(void (^)(FIRDocumentSnapshot *_Nullable snapshot,NSError *_Nullable error))listener + NS_SWIFT_NAME(addSnapshotListener(options:listener:)); +// clang-format on + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Public/FirebaseFirestore/FIRQuery.h b/Firestore/Source/Public/FirebaseFirestore/FIRQuery.h index bde102c212a..c60f2d65438 100644 --- a/Firestore/Source/Public/FirebaseFirestore/FIRQuery.h +++ b/Firestore/Source/Public/FirebaseFirestore/FIRQuery.h @@ -18,6 +18,7 @@ #import "FIRFirestoreSource.h" #import "FIRListenerRegistration.h" +#import "FIRSnapshotListenOptions.h" @class FIRAggregateQuery; @class FIRAggregateField; @@ -104,6 +105,21 @@ NS_SWIFT_NAME(Query) NSError *_Nullable error))listener NS_SWIFT_NAME(addSnapshotListener(includeMetadataChanges:listener:)); +/** + * Attaches a listener for `QuerySnapshot` events. + * @param options Sets snapshot listen options, including whether metadata-only changes should + * trigger snapshot events, the source that listens to, the executor to use to call the + * listener, or the activity to scope the listener to. + * @param listener The listener to attach. + * + * @return A `ListenerRegistration` that can be used to remove this listener. + */ +- (id) + addSnapshotListenerWithOptions:(FIRSnapshotListenOptions *)options + listener:(void (^)(FIRQuerySnapshot *_Nullable snapshot, + NSError *_Nullable error))listener + NS_SWIFT_NAME(addSnapshotListener(options:listener:)); + #pragma mark - Filtering Data /** * Creates and returns a new Query with the additional filter. diff --git a/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h b/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h new file mode 100644 index 00000000000..cdf0d31d129 --- /dev/null +++ b/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, FIRListenSource) { + FIRListenSourceDefault, + FIRListenSourceCache +} NS_SWIFT_NAME(ListenSource); + +NS_SWIFT_NAME(SnapshotListenOptions) +@interface FIRSnapshotListenOptions : NSObject + +@property(nonatomic, readonly) FIRListenSource source; +@property(nonatomic, readonly) BOOL includeMetadataChanges; + +- (instancetype)init NS_DESIGNATED_INITIALIZER; + +- (FIRSnapshotListenOptions *)optionsWithIncludeMetadataChanges:(BOOL)includeMetadataChanges; +- (FIRSnapshotListenOptions *)optionsWithSource:(FIRListenSource)source; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Public/FirebaseFirestore/FirebaseFirestore.h b/Firestore/Source/Public/FirebaseFirestore/FirebaseFirestore.h index 6746aa489e8..9a0574a8207 100644 --- a/Firestore/Source/Public/FirebaseFirestore/FirebaseFirestore.h +++ b/Firestore/Source/Public/FirebaseFirestore/FirebaseFirestore.h @@ -34,6 +34,7 @@ #import "FIRLocalCacheSettings.h" #import "FIRQuery.h" #import "FIRQuerySnapshot.h" +#import "FIRSnapshotListenOptions.h" #import "FIRSnapshotMetadata.h" #import "FIRTimestamp.h" #import "FIRTransaction.h" From d6c0d13dd2c8732210ded8a51c3a382c4f3ad02d Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 6 Feb 2024 13:38:59 -0500 Subject: [PATCH 02/17] upload feature implementation --- Firestore/Source/API/FIRDocumentReference.mm | 6 ++- Firestore/Source/API/FIRQuery.mm | 5 +- Firestore/Source/API/converters.h | 4 ++ Firestore/Source/API/converters.mm | 12 +++++ .../FirebaseFirestore/FIRDocumentReference.h | 9 ++-- Firestore/core/src/api/listen_source.h | 37 ++++++++++++++ Firestore/core/src/core/event_manager.cc | 9 ++++ Firestore/core/src/core/event_manager.h | 11 +++- Firestore/core/src/core/listen_options.h | 50 +++++++++++++++++-- Firestore/core/src/core/query_listener.cc | 5 ++ Firestore/core/src/core/query_listener.h | 4 ++ Firestore/core/src/core/sync_engine.cc | 22 ++++++++ Firestore/core/src/core/sync_engine.h | 10 ++++ .../core/test/unit/core/event_manager_test.cc | 2 + 14 files changed, 173 insertions(+), 13 deletions(-) create mode 100644 Firestore/core/src/api/listen_source.h diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index 49a8b000956..baef4839fd3 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -20,6 +20,7 @@ #include #import "FIRFirestoreErrors.h" +#import "Firestore/Source/API/converters.h" #import "Firestore/Source/API/FIRCollectionReference+Internal.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" @@ -50,6 +51,7 @@ using firebase::firestore::api::DocumentSnapshotListener; using firebase::firestore::api::Firestore; using firebase::firestore::api::ListenerRegistration; +using firebase::firestore::api::MakeListenSource; using firebase::firestore::api::MakeSource; using firebase::firestore::api::Source; using firebase::firestore::core::EventListener; @@ -214,8 +216,8 @@ - (void)getDocumentWithSource:(FIRFirestoreSource)source - (id)addSnapshotListenerWithOptions:(FIRSnapshotListenOptions *)options listener:(FIRDocumentSnapshotBlock)listener { - // Mila - ListenOptions listenOptions = ListenOptions::FromIncludeMetadataChanges(false); + ListenOptions listenOptions = + ListenOptions::FromOptions(options.includeMetadataChanges, MakeListenSource(options.source)); return [self addSnapshotListenerInternalWithOptions:listenOptions listener:listener]; } diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index 4adba59744f..cc92bfefbb5 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -23,6 +23,7 @@ #import "FIRDocumentReference.h" #import "FIRFirestoreErrors.h" +#import "Firestore/Source/API/converters.h" #import "Firestore/Source/API/FIRAggregateField+Internal.h" #import "Firestore/Source/API/FIRAggregateQuery+Internal.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" @@ -69,6 +70,7 @@ using firebase::firestore::google_firestore_v1_Value; using firebase::firestore::google_firestore_v1_Value_fields; using firebase::firestore::api::Firestore; +using firebase::firestore::api::MakeListenSource; using firebase::firestore::api::Query; using firebase::firestore::api::QueryListenerRegistration; using firebase::firestore::api::QuerySnapshot; @@ -193,7 +195,8 @@ - (void)getDocumentsWithSource:(FIRFirestoreSource)publicSource - (id)addSnapshotListenerWithOptions:(FIRSnapshotListenOptions *)options listener:(FIRQuerySnapshotBlock)listener { - auto listenOptions = ListenOptions::FromIncludeMetadataChanges(false); + ListenOptions listenOptions = + ListenOptions::FromOptions(options.includeMetadataChanges, MakeListenSource(options.source)); return [self addSnapshotListenerInternalWithOptions:listenOptions listener:listener]; } diff --git a/Firestore/Source/API/converters.h b/Firestore/Source/API/converters.h index f5d0e4545b8..e777ba81a2c 100644 --- a/Firestore/Source/API/converters.h +++ b/Firestore/Source/API/converters.h @@ -24,6 +24,8 @@ #import #include +#import "FIRSnapshotListenOptions.h" +#import "Firestore/core/src/api/listen_source.h" @class FIRGeoPoint; @class FIRTimestamp; @@ -62,6 +64,8 @@ FIRTimestamp* MakeFIRTimestamp(const Timestamp& timestamp); FIRDocumentReference* MakeFIRDocumentReference(const model::DocumentKey& document_key, std::shared_ptr firestore); +ListenSource MakeListenSource(const FIRListenSource& source); + } // namespace api } // namespace firestore } // namespace firebase diff --git a/Firestore/Source/API/converters.mm b/Firestore/Source/API/converters.mm index 251d3e578bf..133cf6a1f02 100644 --- a/Firestore/Source/API/converters.mm +++ b/Firestore/Source/API/converters.mm @@ -25,6 +25,7 @@ #include "Firestore/core/include/firebase/firestore/geo_point.h" #include "Firestore/core/include/firebase/firestore/timestamp.h" #include "Firestore/core/src/api/firestore.h" +#import "Firestore/core/src/api/listen_source.h" #include "Firestore/core/src/model/document_key.h" NS_ASSUME_NONNULL_BEGIN @@ -61,6 +62,17 @@ Timestamp MakeTimestamp(NSDate* date) { return [[FIRDocumentReference alloc] initWithKey:key firestore:std::move(firestore)]; } +ListenSource MakeListenSource( const FIRListenSource& source) { + switch (source) { + case FIRListenSourceDefault: + return ListenSource::Default; + case FIRListenSourceCache: + return ListenSource::Cache; + default: + return ListenSource::Default; + } +} + } // namespace api } // namespace firestore } // namespace firebase diff --git a/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h b/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h index a7437b7138b..edbbb113ac3 100644 --- a/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h +++ b/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h @@ -281,12 +281,11 @@ addSnapshotListenerWithIncludeMetadataChanges:(BOOL)includeMetadataChanges * * @return A `ListenerRegistration` that can be used to remove this listener. */ -// clang-format off -- (id)addSnapshotListenerWithOptions: - (FIRSnapshotListenOptions *)options - listener:(void (^)(FIRDocumentSnapshot *_Nullable snapshot,NSError *_Nullable error))listener +- (id) + addSnapshotListenerWithOptions:(FIRSnapshotListenOptions *)options + listener:(void (^)(FIRDocumentSnapshot *_Nullable snapshot, + NSError *_Nullable error))listener NS_SWIFT_NAME(addSnapshotListener(options:listener:)); -// clang-format on @end diff --git a/Firestore/core/src/api/listen_source.h b/Firestore/core/src/api/listen_source.h new file mode 100644 index 00000000000..8fbfd92afbf --- /dev/null +++ b/Firestore/core/src/api/listen_source.h @@ -0,0 +1,37 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRESTORE_CORE_SRC_API_LISTEN_SOURCE_H_ +#define FIRESTORE_CORE_SRC_API_LISTEN_SOURCE_H_ + +namespace firebase { +namespace firestore { +namespace api { + +/** + * An enum that configures the source of snapshot listeners listening to. By + * providing a source enum, listener raises snapshot from local cache changes + * only, or from both local cache and watch changes(which is the default). + * + * See `FIRFirestoreListenSource` for more details. + */ +enum class ListenSource { Default, Cache }; + +} // namespace api +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_API_LISTEN_SOURCE_H_ diff --git a/Firestore/core/src/core/event_manager.cc b/Firestore/core/src/core/event_manager.cc index 283a8341076..52eb11801cc 100644 --- a/Firestore/core/src/core/event_manager.cc +++ b/Firestore/core/src/core/event_manager.cc @@ -41,6 +41,7 @@ model::TargetId EventManager::AddQueryListener( auto inserted = queries_.emplace(query, QueryListenersInfo{}); bool first_listen = inserted.second; QueryListenersInfo& query_info = inserted.first->second; + bool first_listen_to_remote_store = !query_info.has_remote_listeners() && listener->listens_to_remote_store(); query_info.listeners.push_back(listener); @@ -58,6 +59,8 @@ model::TargetId EventManager::AddQueryListener( if (first_listen) { query_info.target_id = query_event_source_->Listen(query); + } else if(first_listen_to_remote_store){ + query_event_source_->ListenToRemoteStore(query); } return query_info.target_id; } @@ -66,17 +69,23 @@ void EventManager::RemoveQueryListener( std::shared_ptr listener) { const Query& query = listener->query(); bool last_listen = false; + bool last_listen_to_remote_store = false; auto found_iter = queries_.find(query); if (found_iter != queries_.end()) { QueryListenersInfo& query_info = found_iter->second; query_info.Erase(listener); last_listen = query_info.listeners.empty(); + last_listen_to_remote_store = !query_info.has_remote_listeners() && listener->listens_to_remote_store(); + } if (last_listen) { queries_.erase(found_iter); query_event_source_->StopListening(query); + } else if(last_listen_to_remote_store){ + query_event_source_->StopListeningToRemoteStore(query); + } } diff --git a/Firestore/core/src/core/event_manager.h b/Firestore/core/src/core/event_manager.h index ba4f2ee56e9..2d15a06c793 100644 --- a/Firestore/core/src/core/event_manager.h +++ b/Firestore/core/src/core/event_manager.h @@ -29,13 +29,13 @@ #include "Firestore/core/src/util/empty.h" #include "Firestore/core/src/util/status_fwd.h" #include "absl/types/optional.h" +#include "Firestore/core/src/core/query_listener.h" namespace firebase { namespace firestore { namespace core { class QueryEventSource; -class QueryListener; /** * EventManager is responsible for mapping queries to query event listeners. @@ -97,6 +97,15 @@ class EventManager : public SyncEngineCallback { snapshot_ = snapshot; } + bool has_remote_listeners() { + for (const auto& listener : listeners) { + if (listener->listens_to_remote_store()) { + return true; + } + } + return false; + } + private: // Other members are public in this struct, ensure that any reads are // copies by requiring reads to go through a const getter. diff --git a/Firestore/core/src/core/listen_options.h b/Firestore/core/src/core/listen_options.h index cf76d872878..98ad3609579 100644 --- a/Firestore/core/src/core/listen_options.h +++ b/Firestore/core/src/core/listen_options.h @@ -17,10 +17,13 @@ #ifndef FIRESTORE_CORE_SRC_CORE_LISTEN_OPTIONS_H_ #define FIRESTORE_CORE_SRC_CORE_LISTEN_OPTIONS_H_ +#include "Firestore/core/src/api/listen_source.h" namespace firebase { namespace firestore { namespace core { +using api::ListenSource; + class ListenOptions { public: ListenOptions() = default; @@ -44,14 +47,36 @@ class ListenOptions { } /** - * Creates a default ListenOptions, with metadata changes and - * wait_for_sync_when_online disabled. + * Creates a new ListenOptions. + * + * @param include_query_metadata_changes Raise events when only metadata of + * the query changes. + * @param include_document_metadata_changes Raise events when only metadata of + * documents changes. + * @param wait_for_sync_when_online Wait for a sync with the server when + * online, but still raise events while offline. + * @param source sets the source snapshot listeners listen to. + */ + ListenOptions(bool include_query_metadata_changes, + bool include_document_metadata_changes, + bool wait_for_sync_when_online, + ListenSource source) + : include_query_metadata_changes_(include_query_metadata_changes), + include_document_metadata_changes_(include_document_metadata_changes), + wait_for_sync_when_online_(wait_for_sync_when_online), + source_(std::move(source)) { + } + + /** + * Creates a default ListenOptions, with metadata changes, + * wait_for_sync_when_online disabled, and listen source set to default. */ static ListenOptions DefaultOptions() { return ListenOptions( /*include_query_metadata_changes=*/false, /*include_document_metadata_changes=*/false, - /*wait_for_sync_when_online=*/false); + /*wait_for_sync_when_online=*/false, + /*source=*/ListenSource::Default); } /** @@ -63,7 +88,19 @@ class ListenOptions { return ListenOptions( /*include_query_metadata_changes=*/include_metadata_changes, /*include_document_metadata_changes=*/include_metadata_changes, - /*wait_for_sync_when_online=*/false); + /*wait_for_sync_when_online=*/false, + /*source=*/ListenSource::Default); + } + + /** + * Creates a ListenOptions which sets the source snapshot listeners listen to. + */ + static ListenOptions FromOptions(bool include_metadata_changes, + ListenSource source) { + return ListenOptions( + /*include_query_metadata_changes=*/include_metadata_changes, + /*include_document_metadata_changes=*/include_metadata_changes, + /*wait_for_sync_when_online=*/false, std::move(source)); } bool include_query_metadata_changes() const { @@ -78,10 +115,15 @@ class ListenOptions { return wait_for_sync_when_online_; } + ListenSource source() const { + return source_; + } + private: bool include_query_metadata_changes_ = false; bool include_document_metadata_changes_ = false; bool wait_for_sync_when_online_ = false; + ListenSource source_ = ListenSource::Default; }; } // namespace core diff --git a/Firestore/core/src/core/query_listener.cc b/Firestore/core/src/core/query_listener.cc index 3e0cbb2cceb..c0ec38e752a 100644 --- a/Firestore/core/src/core/query_listener.cc +++ b/Firestore/core/src/core/query_listener.cc @@ -136,6 +136,11 @@ bool QueryListener::ShouldRaiseInitialEvent(const ViewSnapshot& snapshot, return true; } + // Always raise first event if listening to cache + if (!listens_to_remote_store()) { + return true; + } + // NOTE: We consider OnlineState::Unknown as online (it should become Offline // or Online if we wait long enough). bool maybe_online = online_state != OnlineState::Offline; diff --git a/Firestore/core/src/core/query_listener.h b/Firestore/core/src/core/query_listener.h index eec5ada73b8..6b934a0de59 100644 --- a/Firestore/core/src/core/query_listener.h +++ b/Firestore/core/src/core/query_listener.h @@ -63,6 +63,10 @@ class QueryListener { return query_; } + bool listens_to_remote_store() const { + return options_.source() != ListenSource::Cache; + } + /** The last received view snapshot. */ const absl::optional& snapshot() const { return snapshot_; diff --git a/Firestore/core/src/core/sync_engine.cc b/Firestore/core/src/core/sync_engine.cc index 24a4c3f7803..a6ec3c6b1bb 100644 --- a/Firestore/core/src/core/sync_engine.cc +++ b/Firestore/core/src/core/sync_engine.cc @@ -161,6 +161,12 @@ ViewSnapshot SyncEngine::InitializeViewAndComputeSnapshot( return view_change.snapshot().value(); } +void SyncEngine::ListenToRemoteStore(Query query) { + AssertCallbackExists("ListenToRemoteStore"); + TargetData target_data = local_store_->AllocateTarget(query.ToTarget()); + remote_store_->Listen(std::move(target_data));; +} + void SyncEngine::StopListening(const Query& query) { AssertCallbackExists("StopListening"); @@ -181,6 +187,22 @@ void SyncEngine::StopListening(const Query& query) { } } +void SyncEngine::StopListeningToRemoteStore(const Query& query) { + AssertCallbackExists("stopListeningToRemoteStore"); + + auto query_view = query_views_by_query_[query]; + HARD_ASSERT(query_view, "Trying to stop listening to a query not found"); + + TargetId target_id = query_view->target_id(); + auto& queries = queries_by_target_[target_id]; + queries.erase(std::remove(queries.begin(), queries.end(), query), + queries.end()); + + if (queries.empty()) { + remote_store_->StopListening(target_id); + } +} + void SyncEngine::RemoveAndCleanupTarget(TargetId target_id, Status status) { for (const Query& query : queries_by_target_.at(target_id)) { query_views_by_query_.erase(query); diff --git a/Firestore/core/src/core/sync_engine.h b/Firestore/core/src/core/sync_engine.h index e8bfc028d02..dabe5749a75 100644 --- a/Firestore/core/src/core/sync_engine.h +++ b/Firestore/core/src/core/sync_engine.h @@ -78,8 +78,15 @@ class QueryEventSource { */ virtual model::TargetId Listen(Query query) = 0; + + virtual void ListenToRemoteStore(Query query) = 0; + + /** Stops listening to a query previously listened to via `Listen`. */ virtual void StopListening(const Query& query) = 0; + + virtual void StopListeningToRemoteStore(const Query& query) = 0; + }; /** @@ -108,7 +115,10 @@ class SyncEngine : public remote::RemoteStoreCallback, public QueryEventSource { sync_engine_callback_ = callback; } model::TargetId Listen(Query query) override; + void ListenToRemoteStore(Query query) override; void StopListening(const Query& query) override; + void StopListeningToRemoteStore(const Query& query) override; + /** * Initiates the write of local mutation batch which involves adding the diff --git a/Firestore/core/test/unit/core/event_manager_test.cc b/Firestore/core/test/unit/core/event_manager_test.cc index 9229dae919e..11f18c7830c 100644 --- a/Firestore/core/test/unit/core/event_manager_test.cc +++ b/Firestore/core/test/unit/core/event_manager_test.cc @@ -61,7 +61,9 @@ class MockEventSource : public core::QueryEventSource { public: MOCK_METHOD1(SetCallback, void(core::SyncEngineCallback*)); MOCK_METHOD1(Listen, model::TargetId(core::Query)); + MOCK_METHOD1(ListenToRemoteStore, void(core::Query)); MOCK_METHOD1(StopListening, void(const core::Query&)); + MOCK_METHOD1(StopListeningToRemoteStore, void(const core::Query&)); }; TEST(EventManagerTest, HandlesManyListnersPerQuery) { From 9fbe202c668b10fc44b841123e9961faf583193b Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 8 Feb 2024 12:43:18 -0500 Subject: [PATCH 03/17] add spec tests --- .../Firestore.xcodeproj/project.pbxproj | 14 + .../Example/Tests/SpecTests/FSTSpecTests.mm | 19 +- .../Tests/SpecTests/FSTSyncEngineTestDriver.h | 2 +- .../SpecTests/FSTSyncEngineTestDriver.mm | 3 +- .../json/listen_source_spec_test.json | 5918 +++++++++++++++++ .../SpecTests/json/listen_spec_test.json | 5 +- Firestore/Source/API/FIRDocumentReference.mm | 4 +- Firestore/Source/API/FIRQuery.mm | 2 +- Firestore/Source/API/converters.mm | 2 +- Firestore/core/src/core/event_manager.cc | 17 +- Firestore/core/src/core/event_manager.h | 2 +- Firestore/core/src/core/sync_engine.cc | 16 +- Firestore/core/src/core/sync_engine.h | 15 +- .../core/test/unit/core/event_manager_test.cc | 14 +- 14 files changed, 5995 insertions(+), 38 deletions(-) create mode 100644 Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index b0aa3693dd8..7637adf5aca 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -238,6 +238,7 @@ 21E588CF29C72813D8A7A0A1 /* FSTExceptionCatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B8BFD9B37D1029D238BDD71E /* FSTExceptionCatcher.m */; }; 21E66B6A4A00786C3E934EB1 /* query_engine_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B8A853940305237AFDA8050B /* query_engine_test.cc */; }; 224496E752E42E220F809FAC /* resource.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1C3F7302BF4AE6CBC00ECDD0 /* resource.pb.cc */; }; + 2252357505C92A067DAC38B0 /* listen_source_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 4D9E51DA7A275D8B1CAEAEB2 /* listen_source_spec_test.json */; }; 226574601C3F6D14DF14C16B /* recovery_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 9C1AFCC9E616EC33D6E169CF /* recovery_spec_test.json */; }; 227CFA0B2A01884C277E4F1D /* hashing_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54511E8D209805F8005BD28F /* hashing_test.cc */; }; 229D1A9381F698D71F229471 /* string_win_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 79507DF8378D3C42F5B36268 /* string_win_test.cc */; }; @@ -508,6 +509,7 @@ 5150E9F256E6E82D6F3CB3F1 /* bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F7FC06E0A47D393DE1759AE1 /* bundle_cache_test.cc */; }; 518BF03D57FBAD7C632D18F8 /* FIRQueryUnitTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = FF73B39D04D1760190E6B84A /* FIRQueryUnitTests.mm */; }; 51A483DE202CC3E9FCD8FF6E /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = B0520A41251254B3C24024A3 /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json */; }; + 5250AE69A391E7A3310E013B /* listen_source_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 4D9E51DA7A275D8B1CAEAEB2 /* listen_source_spec_test.json */; }; 52967C3DD7896BFA48840488 /* byte_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5342CDDB137B4E93E2E85CCA /* byte_string_test.cc */; }; 529AB59F636060FEA21BD4FF /* garbage_collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = AAED89D7690E194EF3BA1132 /* garbage_collection_spec_test.json */; }; 5360D52DCAD1069B1E4B0B9D /* testing_hooks_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A002425BC4FC4E805F4175B6 /* testing_hooks_test.cc */; }; @@ -816,6 +818,7 @@ 6F67601562343B63B8996F7A /* FSTTestingHooks.mm in Sources */ = {isa = PBXBuildFile; fileRef = D85AC18C55650ED230A71B82 /* FSTTestingHooks.mm */; }; 6F914209F46E6552B5A79570 /* async_queue_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */; }; 6FAC16B7FBD3B40D11A6A816 /* target.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7D20B89AAC00B5BCE7 /* target.pb.cc */; }; + 6FB40B88ACB4CFB34917319C /* listen_source_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 4D9E51DA7A275D8B1CAEAEB2 /* listen_source_spec_test.json */; }; 6FC85C48CF8235BA1845E1C8 /* FSTUserDataReaderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8D9892F204959C50613F16C8 /* FSTUserDataReaderTests.mm */; }; 6FCC64A1937E286E76C294D0 /* logic_utils_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 28B45B2104E2DAFBBF86DBB7 /* logic_utils_test.cc */; }; 6FD2369F24E884A9D767DD80 /* FIRDocumentSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04B202154AA00B64F25 /* FIRDocumentSnapshotTests.mm */; }; @@ -1050,6 +1053,7 @@ 9C86EEDEA131BFD50255EEF1 /* comparison_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB928200D59F600E00ABC /* comparison_test.cc */; }; 9CC32ACF397022BB7DF11B52 /* Validation_BloomFilterTest_MD5_500_0001_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = D22D4C211AC32E4F8B4883DA /* Validation_BloomFilterTest_MD5_500_0001_bloom_filter_proto.json */; }; 9CE07BAAD3D3BC5F069D38FE /* grpc_streaming_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964922154AB8F00EB9CFB /* grpc_streaming_reader_test.cc */; }; + 9CFF379C7404F7CE6B26AF29 /* listen_source_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 4D9E51DA7A275D8B1CAEAEB2 /* listen_source_spec_test.json */; }; 9D71628E38D9F64C965DF29E /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; 9E1997789F19BF2E9029012E /* FIRCompositeIndexQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 65AF0AB593C3AD81A1F1A57E /* FIRCompositeIndexQueryTests.mm */; }; 9E656F4FE92E8BFB7F625283 /* to_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B696858D2214B53900271095 /* to_string_test.cc */; }; @@ -1068,6 +1072,7 @@ A17DBC8F24127DA8A381F865 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; A186FECD0257B92FDB0E83B8 /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 5B96CC29E9946508F022859C /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json */; }; A192648233110B7B8BD65528 /* field_transform_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7515B47C92ABEEC66864B55C /* field_transform_test.cc */; }; + A1A466F55A1ED0AC5EE449BF /* listen_source_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 4D9E51DA7A275D8B1CAEAEB2 /* listen_source_spec_test.json */; }; A1F57CC739211F64F2E9232D /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; }; A215078DBFBB5A4F4DADE8A9 /* leveldb_index_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 166CE73C03AB4366AAC5201C /* leveldb_index_manager_test.cc */; }; A21819C437C3C80450D7EEEE /* writer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BC3C788D290A935C353CEAA1 /* writer_test.cc */; }; @@ -1161,6 +1166,7 @@ B0B779769926304268200015 /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; B0D10C3451EDFB016A6EAF03 /* writer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BC3C788D290A935C353CEAA1 /* writer_test.cc */; }; B0E745EAC5F37CA61F868F38 /* Validation_BloomFilterTest_MD5_50000_1_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 4B3E4A77493524333133C5DC /* Validation_BloomFilterTest_MD5_50000_1_bloom_filter_proto.json */; }; + B104B69726EF6A5B41DAFB17 /* listen_source_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 4D9E51DA7A275D8B1CAEAEB2 /* listen_source_spec_test.json */; }; B15D17049414E2F5AE72C9C6 /* memory_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F6CA0C5638AB6627CB5B4CF4 /* memory_local_store_test.cc */; }; B188D7EC9A100F365DB02490 /* Validation_BloomFilterTest_MD5_500_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = DD990FD89C165F4064B4F608 /* Validation_BloomFilterTest_MD5_500_01_membership_test_result.json */; }; B192F30DECA8C28007F9B1D0 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; @@ -1740,6 +1746,7 @@ 4B59C0A7B2A4548496ED4E7D /* Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json; sourceTree = ""; }; 4BD051DBE754950FEAC7A446 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json; sourceTree = ""; }; 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = string_apple_benchmark.mm; sourceTree = ""; }; + 4D9E51DA7A275D8B1CAEAEB2 /* listen_source_spec_test.json */ = {isa = PBXFileReference; includeInIndex = 1; path = listen_source_spec_test.json; sourceTree = ""; }; 4F5B96F3ABCD2CA901DB1CD4 /* bundle_builder.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bundle_builder.cc; sourceTree = ""; }; 526D755F65AC676234F57125 /* target_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = target_test.cc; sourceTree = ""; }; 52756B7624904C36FBB56000 /* fake_target_metadata_provider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = fake_target_metadata_provider.h; sourceTree = ""; }; @@ -2968,6 +2975,7 @@ 8C7278B604B8799F074F4E8C /* index_spec_test.json */, 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */, 54DA129F1F315EE100DD57A1 /* limit_spec_test.json */, + 4D9E51DA7A275D8B1CAEAEB2 /* listen_source_spec_test.json */, 54DA12A01F315EE100DD57A1 /* listen_spec_test.json */, 54DA12A11F315EE100DD57A1 /* offline_spec_test.json */, 54DA12A21F315EE100DD57A1 /* orderby_spec_test.json */, @@ -3378,6 +3386,7 @@ BFBE4732E93E38317B110778 /* index_spec_test.json in Resources */, 546877D72248206A005E3DE0 /* limbo_spec_test.json in Resources */, 546877D82248206A005E3DE0 /* limit_spec_test.json in Resources */, + 6FB40B88ACB4CFB34917319C /* listen_source_spec_test.json in Resources */, 546877D92248206A005E3DE0 /* listen_spec_test.json in Resources */, 546877DA2248206A005E3DE0 /* offline_spec_test.json in Resources */, 546877DB2248206A005E3DE0 /* orderby_spec_test.json in Resources */, @@ -3436,6 +3445,7 @@ 604B75044D6BEC2B7515EA1B /* index_spec_test.json in Resources */, 54ACB6CB224C11F400172E69 /* limbo_spec_test.json in Resources */, 54ACB6CC224C11F400172E69 /* limit_spec_test.json in Resources */, + 2252357505C92A067DAC38B0 /* listen_source_spec_test.json in Resources */, 54ACB6CD224C11F400172E69 /* listen_spec_test.json in Resources */, 54ACB6CE224C11F400172E69 /* offline_spec_test.json in Resources */, 54ACB6CF224C11F400172E69 /* orderby_spec_test.json in Resources */, @@ -3484,6 +3494,7 @@ 77C36312F8025EC73991D7DA /* index_spec_test.json in Resources */, F08DA55D31E44CB5B9170CCE /* limbo_spec_test.json in Resources */, 15A5F95DA733FD89A1E4147D /* limit_spec_test.json in Resources */, + 5250AE69A391E7A3310E013B /* listen_source_spec_test.json in Resources */, D73BBA4AB42940AB187169E3 /* listen_spec_test.json in Resources */, C15F5F1E7427738F20C2D789 /* offline_spec_test.json in Resources */, 4781186C01D33E67E07F0D0D /* orderby_spec_test.json in Resources */, @@ -3532,6 +3543,7 @@ 6156C6A837D78D49ED8B8812 /* index_spec_test.json in Resources */, 85BC2AB572A400114BF59255 /* limbo_spec_test.json in Resources */, 9F41D724D9947A89201495AD /* limit_spec_test.json in Resources */, + B104B69726EF6A5B41DAFB17 /* listen_source_spec_test.json in Resources */, 3CFFA6F016231446367E3A69 /* listen_spec_test.json in Resources */, A0C6C658DFEE58314586907B /* offline_spec_test.json in Resources */, D98430EA4FAA357D855FA50F /* orderby_spec_test.json in Resources */, @@ -3599,6 +3611,7 @@ 3783E25DFF9E5C0896D34FEF /* index_spec_test.json in Resources */, 54DA12A81F315EE100DD57A1 /* limbo_spec_test.json in Resources */, 54DA12A91F315EE100DD57A1 /* limit_spec_test.json in Resources */, + 9CFF379C7404F7CE6B26AF29 /* listen_source_spec_test.json in Resources */, 54DA12AA1F315EE100DD57A1 /* listen_spec_test.json in Resources */, 54DA12AB1F315EE100DD57A1 /* offline_spec_test.json in Resources */, 54DA12AC1F315EE100DD57A1 /* orderby_spec_test.json in Resources */, @@ -3665,6 +3678,7 @@ E04607A1E2964684184E8AEA /* index_spec_test.json in Resources */, 2AD8EE91928AE68DF268BEDA /* limbo_spec_test.json in Resources */, BC5AC8890974E0821431267E /* limit_spec_test.json in Resources */, + A1A466F55A1ED0AC5EE449BF /* listen_source_spec_test.json in Resources */, 5B89B1BA0AD400D9BF581420 /* listen_spec_test.json in Resources */, F660788F69B4336AC6CD2720 /* offline_spec_test.json in Resources */, 4F5714D37B6D119CB07ED8AE /* orderby_spec_test.json in Resources */, diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm index 622b3b527d7..71833017978 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm @@ -41,6 +41,7 @@ #include "Firestore/core/src/bundle/bundle_reader.h" #include "Firestore/core/src/bundle/bundle_serializer.h" #include "Firestore/core/src/core/field_filter.h" +#import "Firestore/core/src/core/listen_options.h" #include "Firestore/core/src/credentials/user.h" #include "Firestore/core/src/local/persistence.h" #include "Firestore/core/src/local/target_data.h" @@ -79,10 +80,12 @@ using firebase::firestore::Error; using firebase::firestore::google_firestore_v1_ArrayValue; using firebase::firestore::google_firestore_v1_Value; +using firebase::firestore::api::ListenSource; using firebase::firestore::api::LoadBundleTask; using firebase::firestore::bundle::BundleReader; using firebase::firestore::bundle::BundleSerializer; using firebase::firestore::core::DocumentViewChange; +using firebase::firestore::core::ListenOptions; using firebase::firestore::core::Query; using firebase::firestore::credentials::User; using firebase::firestore::local::Persistence; @@ -385,11 +388,25 @@ - (DocumentViewChange)parseChange:(NSDictionary *)jsonDoc ofType:(DocumentViewCh return DocumentViewChange{std::move(doc), type}; } +- (ListenOptions)parseOptions:(NSDictionary *)optionsSpec { + ListenOptions options = ListenOptions::FromIncludeMetadataChanges(true); + + if (optionsSpec != nil) { + ListenSource source = + [optionsSpec[@"source"] intValue] == 1 ? ListenSource::Cache : ListenSource::Default; + // TODO: extract include_metadata_changes if it exists in optionsSpec + options = ListenOptions::FromOptions(true, source); + } + + return options; +} + #pragma mark - Methods for doing the steps of the spec test. - (void)doListen:(NSDictionary *)listenSpec { Query query = [self parseQuery:listenSpec[@"query"]]; - TargetId actualID = [self.driver addUserListenerWithQuery:std::move(query)]; + ListenOptions options = [self parseOptions:listenSpec[@"options"]]; + TargetId actualID = [self.driver addUserListenerWithQuery:std::move(query) options:options]; TargetId expectedID = [listenSpec[@"targetId"] intValue]; XCTAssertEqual(actualID, expectedID, @"targetID assigned to listen"); diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h index c99836f8406..62794f2609a 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h @@ -148,7 +148,7 @@ typedef std:: * @param query A valid query to execute against the backend. * @return The target ID assigned by the system to track the query. */ -- (model::TargetId)addUserListenerWithQuery:(core::Query)query; +- (model::TargetId)addUserListenerWithQuery:(core::Query)query options:(core::ListenOptions)options; /** * Removes a listener from the FSTSyncEngine as if the user had removed a listener corresponding diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm index 41fe884c70f..de21fdf3dc5 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm @@ -476,10 +476,9 @@ - (FSTOutstandingWrite *)receiveWriteError:(int)errorCode return result; } -- (TargetId)addUserListenerWithQuery:(Query)query { +- (TargetId)addUserListenerWithQuery:(Query)query options:(ListenOptions)options { // TODO(dimond): Allow customizing listen options in spec tests // TODO(dimond): Change spec tests to verify isFromCache on snapshots - ListenOptions options = ListenOptions::FromIncludeMetadataChanges(true); auto listener = QueryListener::Create( query, options, [self, query](const StatusOr &maybe_snapshot) { FSTQueryEvent *event = [[FSTQueryEvent alloc] init]; diff --git a/Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json b/Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json new file mode 100644 index 00000000000..006e70433bd --- /dev/null +++ b/Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json @@ -0,0 +1,5918 @@ +{ + "Clients can have multiple listeners with different sources": { + "describeName": "Listens source options:", + "itName": "Clients can have multiple listeners with different sources", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 2, + "useEagerGCForMemory": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true + }, + { + "applyClientState": { + "visibility": "visible" + }, + "clientIndex": 0 + }, + { + "clientIndex": 0, + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "watchAck": [ + 2 + ] + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 0, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 1, + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 1, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "drainQueue": true + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 2000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + }, + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + }, + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + } + ] + }, + "Contents of query are cleared when listen is removed.": { + "comment": "Explicitly tests eager GC behavior", + "describeName": "Listens source options:", + "itName": "Contents of query are cleared when listen is removed.", + "tags": [ + "eager-gc" + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": true + }, + "steps": [ + { + "userSet": [ + "collection/a", + { + "key": "a" + } + ] + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "key": "a" + }, + "version": 0 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "writeAck": { + "version": 1000 + }, + "expectedState": { + "userCallbacks": { + "acknowledgedDocs": [ + "collection/a" + ], + "rejectedDocs": [ + ] + } + } + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 4 + }, + "expectedSnapshotEvents": [ + { + "added": [ + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + } + ] + }, + "Documents are cleared when listen is removed.": { + "comment": "", + "describeName": "Listens source options:", + "itName": "Documents are cleared when listen is removed.", + "tags": [ + "eager-gc" + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": true + }, + "steps": [ + { + "userSet": [ + "collection/a", + { + "matches": true + } + ] + }, + { + "userSet": [ + "collection/b", + { + "matches": true + } + ] + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + [ + "matches", + "==", + true + ] + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "matches": true + }, + "version": 0 + }, + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "matches": true + }, + "version": 0 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "query": { + "filters": [ + [ + "matches", + "==", + true + ] + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "writeAck": { + "version": 1000 + }, + "expectedState": { + "userCallbacks": { + "acknowledgedDocs": [ + "collection/a" + ], + "rejectedDocs": [ + ] + } + } + }, + { + "writeAck": { + "version": 2000 + }, + "expectedState": { + "userCallbacks": { + "acknowledgedDocs": [ + "collection/b" + ], + "rejectedDocs": [ + ] + } + } + }, + { + "userSet": [ + "collection/b", + { + "matches": false + } + ], + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "query": { + "filters": [ + [ + "matches", + "==", + true + ] + ], + "orderBys": [ + ], + "path": "collection" + }, + "removed": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "matches": true + }, + "version": 0 + } + ] + } + ] + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + [ + "matches", + "==", + true + ] + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "writeAck": { + "version": 3000 + }, + "expectedState": { + "userCallbacks": { + "acknowledgedDocs": [ + "collection/b" + ], + "rejectedDocs": [ + ] + } + } + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 4 + }, + "expectedSnapshotEvents": [ + { + "added": [ + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "userUnlisten": [ + 4, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + } + ] + }, + "Doesn't include unknown documents in cached result": { + "describeName": "Listens source options:", + "itName": "Doesn't include unknown documents in cached result", + "tags": [ + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": true + }, + "steps": [ + { + "userSet": [ + "collection/exists", + { + "key": "a" + } + ] + }, + { + "userPatch": [ + "collection/unknown", + { + "key": "b" + } + ] + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/exists", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "key": "a" + }, + "version": 0 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + } + ] + }, + "Doesn't raise 'hasPendingWrites' for deletes": { + "describeName": "Listens source options:", + "itName": "Doesn't raise 'hasPendingWrites' for deletes", + "tags": [ + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": false + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "userDelete": "collection/a", + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "removed": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ] + } + ] + }, + { + "writeAck": { + "version": 2000 + }, + "expectedState": { + "userCallbacks": { + "acknowledgedDocs": [ + "collection/a" + ], + "rejectedDocs": [ + ] + } + } + }, + { + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "value": null, + "version": 2000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + } + } + ] + }, + "Empty initial snapshot is raised from cache": { + "describeName": "Listens source options:", + "itName": "Empty initial snapshot is raised from cache", + "tags": [ + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": false + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + } + ] + }, + "Empty-due-to-delete initial snapshot is raised from cache": { + "describeName": "Listens source options:", + "itName": "Empty-due-to-delete initial snapshot is raised from cache", + "tags": [ + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": false + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "userDelete": "collection/a" + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + } + ] + }, + "Listeners with different source shares watch changes between primary and secondary clients": { + "describeName": "Listens source options:", + "itName": "Listeners with different source shares watch changes between primary and secondary clients", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 3, + "useEagerGCForMemory": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true + }, + { + "applyClientState": { + "visibility": "visible" + }, + "clientIndex": 0 + }, + { + "clientIndex": 0, + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "watchAck": [ + 2 + ] + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 1, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 2, + "drainQueue": true + }, + { + "clientIndex": 2, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 0, + "drainQueue": true + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 2000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 2, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 0, + "drainQueue": true + }, + { + "clientIndex": 0, + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + } + ] + }, + "Local mutations notifies listeners sourced from cache in all tabs": { + "describeName": "Listens source options:", + "itName": "Local mutations notifies listeners sourced from cache in all tabs", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 2, + "useEagerGCForMemory": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true + }, + { + "applyClientState": { + "visibility": "visible" + }, + "clientIndex": 0 + }, + { + "clientIndex": 0, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 1, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 0, + "drainQueue": true + }, + { + "clientIndex": 0, + "userSet": [ + "collection/a", + { + "key": "a" + } + ], + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "key": "a" + }, + "version": 0 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "key": "a" + }, + "version": 0 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + } + ] + }, + "Mirror queries being listened from different sources while listening to server in primary tab": { + "describeName": "Listens source options:", + "itName": "Mirror queries being listened from different sources while listening to server in primary tab", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 2, + "useEagerGCForMemory": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true + }, + { + "applyClientState": { + "visibility": "visible" + }, + "clientIndex": 0 + }, + { + "clientIndex": 0, + "userListen": { + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "watchAck": [ + 2 + ] + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 1, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 0, + "drainQueue": true + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": -1 + }, + "version": 2000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": -1 + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + }, + "removed": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ] + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": -1 + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + }, + "removed": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ] + } + ] + } + ] + }, + "Mirror queries being listened in different clients sourced from cache ": { + "describeName": "Listens source options:", + "itName": "Mirror queries being listened in different clients sourced from cache ", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 2, + "useEagerGCForMemory": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true + }, + { + "applyClientState": { + "visibility": "visible" + }, + "clientIndex": 0 + }, + { + "clientIndex": 0, + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "watchAck": [ + 2 + ] + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 0, + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 0, + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + }, + "targetId": 4 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 1, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + }, + "targetId": 4 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 0, + "drainQueue": true + }, + { + "clientIndex": 0, + "userUnlisten": [ + 4, + { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 0, + "userSet": [ + "collection/c", + { + "sort": -1 + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "sort": -1 + }, + "version": 0 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + }, + "removed": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ] + } + ] + } + ] + }, + "Mirror queries being listened in the same secondary client sourced from cache": { + "describeName": "Listens source options:", + "itName": "Mirror queries being listened in the same secondary client sourced from cache", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 2, + "useEagerGCForMemory": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true + }, + { + "applyClientState": { + "visibility": "visible" + }, + "clientIndex": 0 + }, + { + "clientIndex": 0, + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "watchAck": [ + 2 + ] + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 0, + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 0, + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 1, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + }, + "targetId": 4 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 1, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + }, + "targetId": 4 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 1, + "userUnlisten": [ + 4, + { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 1, + "userSet": [ + "collection/c", + { + "sort": -1 + } + ], + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "sort": -1 + }, + "version": 0 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + }, + "removed": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ] + } + ] + } + ] + }, + "Mirror queries from different sources while listening to server in secondary tab": { + "describeName": "Listens source options:", + "itName": "Mirror queries from different sources while listening to server in secondary tab", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 2, + "useEagerGCForMemory": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true + }, + { + "applyClientState": { + "visibility": "visible" + }, + "clientIndex": 0 + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 1, + "userListen": { + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "drainQueue": true, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "watchAck": [ + 2 + ] + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + } + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 0, + "drainQueue": true + }, + { + "clientIndex": 0, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + }, + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 0 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": -1 + }, + "version": 2000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": -1 + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToLast", + "orderBys": [ + [ + "sort", + "desc" + ] + ], + "path": "collection" + }, + "removed": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ] + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": -1 + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "limit": 2, + "limitType": "LimitToFirst", + "orderBys": [ + [ + "sort", + "asc" + ] + ], + "path": "collection" + }, + "removed": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "sort": 1 + }, + "version": 1000 + } + ] + } + ] + } + ] + }, + "Newer deleted docs from bundles should delete cached docs": { + "describeName": "Listens source options:", + "itName": "Newer deleted docs from bundles should delete cached docs", + "tags": [ + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": false + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "loadBundle": "127{\"metadata\":{\"id\":\"test-bundle\",\"createTime\":\"1970-01-01T00:00:00.003000000Z\",\"version\":1,\"totalDocuments\":1,\"totalBytes\":158}}155{\"documentMetadata\":{\"name\":\"projects/test-project/databases/(default)/documents/collection/a\",\"readTime\":\"1970-01-01T00:00:00.003000000Z\",\"exists\":false}}", + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "removed": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 1000 + } + ] + } + ] + } + ] + }, + "Newer docs from bundles should keep not raise snapshot if there are unacknowledged writes": { + "describeName": "Listens source options:", + "itName": "Newer docs from bundles should keep not raise snapshot if there are unacknowledged writes", + "tags": [ + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": false + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 250 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-250" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 250 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 250 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 250 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "userPatch": [ + "collection/a", + { + "value": "patched" + } + ], + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "modified": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "value": "patched" + }, + "version": 0 + } + ], + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "loadBundle": "127{\"metadata\":{\"id\":\"test-bundle\",\"createTime\":\"1970-01-01T00:00:00.001001000Z\",\"version\":1,\"totalDocuments\":1,\"totalBytes\":388}}154{\"documentMetadata\":{\"name\":\"projects/test-project/databases/(default)/documents/collection/a\",\"readTime\":\"1970-01-01T00:00:00.001001000Z\",\"exists\":true}}228{\"document\":{\"name\":\"projects/test-project/databases/(default)/documents/collection/a\",\"createTime\":\"1970-01-01T00:00:00.000250000Z\",\"updateTime\":\"1970-01-01T00:00:00.001001000Z\",\"fields\":{\"value\":{\"stringValue\":\"fromBundle\"}}}}" + } + ] + }, + "Newer docs from bundles should overwrite cache": { + "describeName": "Listens source options:", + "itName": "Newer docs from bundles should overwrite cache", + "tags": [ + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": false + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "loadBundle": "127{\"metadata\":{\"id\":\"test-bundle\",\"createTime\":\"1970-01-01T00:00:00.003000000Z\",\"version\":1,\"totalDocuments\":1,\"totalBytes\":379}}154{\"documentMetadata\":{\"name\":\"projects/test-project/databases/(default)/documents/collection/a\",\"readTime\":\"1970-01-01T00:00:00.003000000Z\",\"exists\":true}}219{\"document\":{\"name\":\"projects/test-project/databases/(default)/documents/collection/a\",\"createTime\":\"1970-01-01T00:00:00.001999000Z\",\"updateTime\":\"1970-01-01T00:00:00.002999000Z\",\"fields\":{\"value\":{\"stringValue\":\"b\"}}}}", + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "modified": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "b" + }, + "version": 2999 + } + ], + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + } + ] + }, + "Newer docs from bundles should raise snapshot only when Watch catches up with acknowledged writes": { + "describeName": "Listens source options:", + "itName": "Newer docs from bundles should raise snapshot only when Watch catches up with acknowledged writes", + "tags": [ + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": false + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 250 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-250" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 250 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 250 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 250 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "userPatch": [ + "collection/a", + { + "value": "patched" + } + ], + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "modified": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "value": "patched" + }, + "version": 0 + } + ], + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "writeAck": { + "version": 1000 + }, + "expectedState": { + "userCallbacks": { + "acknowledgedDocs": [ + "collection/a" + ], + "rejectedDocs": [ + ] + } + } + }, + { + "loadBundle": "127{\"metadata\":{\"id\":\"test-bundle\",\"createTime\":\"1970-01-01T00:00:00.000500000Z\",\"version\":1,\"totalDocuments\":1,\"totalBytes\":379}}154{\"documentMetadata\":{\"name\":\"projects/test-project/databases/(default)/documents/collection/a\",\"readTime\":\"1970-01-01T00:00:00.000500000Z\",\"exists\":true}}219{\"document\":{\"name\":\"projects/test-project/databases/(default)/documents/collection/a\",\"createTime\":\"1970-01-01T00:00:00.000250000Z\",\"updateTime\":\"1970-01-01T00:00:00.000500000Z\",\"fields\":{\"value\":{\"stringValue\":\"b\"}}}}" + }, + { + "loadBundle": "127{\"metadata\":{\"id\":\"test-bundle\",\"createTime\":\"1970-01-01T00:00:00.001001000Z\",\"version\":1,\"totalDocuments\":1,\"totalBytes\":388}}154{\"documentMetadata\":{\"name\":\"projects/test-project/databases/(default)/documents/collection/a\",\"readTime\":\"1970-01-01T00:00:00.001001000Z\",\"exists\":true}}228{\"document\":{\"name\":\"projects/test-project/databases/(default)/documents/collection/a\",\"createTime\":\"1970-01-01T00:00:00.000250000Z\",\"updateTime\":\"1970-01-01T00:00:00.001001000Z\",\"fields\":{\"value\":{\"stringValue\":\"fromBundle\"}}}}", + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "modified": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "fromBundle" + }, + "version": 1001 + } + ], + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + } + ] + }, + "Older deleted docs from bundles should do nothing": { + "describeName": "Listens source options:", + "itName": "Older deleted docs from bundles should do nothing", + "tags": [ + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": false + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "value": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "loadBundle": "127{\"metadata\":{\"id\":\"test-bundle\",\"createTime\":\"1970-01-01T00:00:00.000999000Z\",\"version\":1,\"totalDocuments\":1,\"totalBytes\":158}}155{\"documentMetadata\":{\"name\":\"projects/test-project/databases/(default)/documents/collection/a\",\"readTime\":\"1970-01-01T00:00:00.000999000Z\",\"exists\":false}}" + } + ] + }, + "Primary client should not invoke watch request while all clients are listening to cache": { + "describeName": "Listens source options:", + "itName": "Primary client should not invoke watch request while all clients are listening to cache", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 2, + "useEagerGCForMemory": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true + }, + { + "applyClientState": { + "visibility": "visible" + }, + "clientIndex": 0 + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 1, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 0, + "drainQueue": true, + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 1, + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 0, + "drainQueue": true, + "expectedState": { + "activeTargets": { + } + } + } + ] + }, + "Query is executed by primary client even if it only includes listeners sourced from cache": { + "describeName": "Listens source options:", + "itName": "Query is executed by primary client even if it only includes listeners sourced from cache", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 2, + "useEagerGCForMemory": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true + }, + { + "applyClientState": { + "visibility": "visible" + }, + "clientIndex": 0 + }, + { + "clientIndex": 0, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 1, + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "drainQueue": true, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "watchAck": [ + 2 + ] + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 0, + "drainQueue": true + }, + { + "clientIndex": 0, + "watchCurrent": [ + [ + 2 + ], + "resume-token-2000" + ] + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + } + ] + }, + "Query only raises events in participating clients": { + "describeName": "Listens source options:", + "itName": "Query only raises events in participating clients", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 4, + "useEagerGCForMemory": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true + }, + { + "applyClientState": { + "visibility": "visible" + }, + "clientIndex": 0 + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 2, + "drainQueue": true + }, + { + "clientIndex": 2, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 3, + "drainQueue": true + }, + { + "clientIndex": 3, + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "drainQueue": true, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "watchAck": [ + 2 + ] + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + } + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 2, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 3, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + } + ] + }, + "Un-listen to listeners from different source": { + "describeName": "Listens source options:", + "itName": "Un-listen to listeners from different source", + "tags": [ + "multi-client" + ], + "config": { + "numClients": 2, + "useEagerGCForMemory": false + }, + "steps": [ + { + "clientIndex": 0, + "drainQueue": true + }, + { + "applyClientState": { + "visibility": "visible" + }, + "clientIndex": 0 + }, + { + "clientIndex": 0, + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "clientIndex": 0, + "watchAck": [ + 2 + ] + }, + { + "clientIndex": 0, + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "clientIndex": 0, + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "clientIndex": 0, + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true + }, + { + "clientIndex": 1, + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 0, + "drainQueue": true + }, + { + "clientIndex": 0, + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "clientIndex": 0, + "userSet": [ + "collection/b", + { + "key": "b" + } + ] + }, + { + "clientIndex": 1, + "drainQueue": true, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "key": "b" + }, + "version": 0 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "clientIndex": 1, + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + } + ] + }, + "onSnapshotsInSync fires for multiple listeners": { + "describeName": "Listens source options:", + "itName": "onSnapshotsInSync fires for multiple listeners", + "tags": [ + ], + "config": { + "numClients": 1, + "useEagerGCForMemory": false + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "userListen": { + "options": { + "source": 1 + }, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "addSnapshotsInSyncListener": true, + "expectedSnapshotsInSyncEvents": 1 + }, + { + "userSet": [ + "collection/a", + { + "v": 2 + } + ], + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "modified": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "v": 2 + }, + "version": 0 + } + ], + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedSnapshotsInSyncEvents": 1 + }, + { + "addSnapshotsInSyncListener": true, + "expectedSnapshotsInSyncEvents": 1 + }, + { + "addSnapshotsInSyncListener": true, + "expectedSnapshotsInSyncEvents": 1 + }, + { + "userSet": [ + "collection/a", + { + "v": 3 + } + ], + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "modified": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "v": 3 + }, + "version": 0 + } + ], + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedSnapshotsInSyncEvents": 3 + }, + { + "removeSnapshotsInSyncListener": true + }, + { + "userSet": [ + "collection/a", + { + "v": 4 + } + ], + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": true, + "modified": [ + { + "createTime": 0, + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": true + }, + "value": { + "v": 4 + }, + "version": 0 + } + ], + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedSnapshotsInSyncEvents": 2 + } + ] + } +} diff --git a/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json b/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json index 7370a0cd675..617c50f55b6 100644 --- a/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json +++ b/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json @@ -13270,7 +13270,10 @@ "describeName": "Listens:", "itName": "Secondary client advances query state with global snapshot from primary", "tags": [ - "multi-client" + "multi-client", + "no-web", + "no-ios", + "no-android" ], "config": { "numClients": 2, diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index baef4839fd3..a50a2c7df71 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -20,7 +20,6 @@ #include #import "FIRFirestoreErrors.h" -#import "Firestore/Source/API/converters.h" #import "Firestore/Source/API/FIRCollectionReference+Internal.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" @@ -28,6 +27,7 @@ #import "Firestore/Source/API/FIRFirestoreSource+Internal.h" #import "Firestore/Source/API/FIRListenerRegistration+Internal.h" #import "Firestore/Source/API/FSTUserDataReader.h" +#import "Firestore/Source/API/converters.h" #include "Firestore/core/src/api/collection_reference.h" #include "Firestore/core/src/api/document_reference.h" @@ -216,7 +216,7 @@ - (void)getDocumentWithSource:(FIRFirestoreSource)source - (id)addSnapshotListenerWithOptions:(FIRSnapshotListenOptions *)options listener:(FIRDocumentSnapshotBlock)listener { - ListenOptions listenOptions = + ListenOptions listenOptions = ListenOptions::FromOptions(options.includeMetadataChanges, MakeListenSource(options.source)); return [self addSnapshotListenerInternalWithOptions:listenOptions listener:listener]; } diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index cc92bfefbb5..d4185488341 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -23,7 +23,6 @@ #import "FIRDocumentReference.h" #import "FIRFirestoreErrors.h" -#import "Firestore/Source/API/converters.h" #import "Firestore/Source/API/FIRAggregateField+Internal.h" #import "Firestore/Source/API/FIRAggregateQuery+Internal.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" @@ -38,6 +37,7 @@ #import "Firestore/Source/API/FIRQuerySnapshot+Internal.h" #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/API/FSTUserDataReader.h" +#import "Firestore/Source/API/converters.h" #include "Firestore/core/src/api/query_core.h" #include "Firestore/core/src/api/query_listener_registration.h" diff --git a/Firestore/Source/API/converters.mm b/Firestore/Source/API/converters.mm index 133cf6a1f02..8bfd5a07090 100644 --- a/Firestore/Source/API/converters.mm +++ b/Firestore/Source/API/converters.mm @@ -62,7 +62,7 @@ Timestamp MakeTimestamp(NSDate* date) { return [[FIRDocumentReference alloc] initWithKey:key firestore:std::move(firestore)]; } -ListenSource MakeListenSource( const FIRListenSource& source) { +ListenSource MakeListenSource(const FIRListenSource& source) { switch (source) { case FIRListenSourceDefault: return ListenSource::Default; diff --git a/Firestore/core/src/core/event_manager.cc b/Firestore/core/src/core/event_manager.cc index 52eb11801cc..7db9f19935b 100644 --- a/Firestore/core/src/core/event_manager.cc +++ b/Firestore/core/src/core/event_manager.cc @@ -41,7 +41,8 @@ model::TargetId EventManager::AddQueryListener( auto inserted = queries_.emplace(query, QueryListenersInfo{}); bool first_listen = inserted.second; QueryListenersInfo& query_info = inserted.first->second; - bool first_listen_to_remote_store = !query_info.has_remote_listeners() && listener->listens_to_remote_store(); + bool first_listen_to_remote_store = + !query_info.has_remote_listeners() && listener->listens_to_remote_store(); query_info.listeners.push_back(listener); @@ -58,8 +59,9 @@ model::TargetId EventManager::AddQueryListener( } if (first_listen) { - query_info.target_id = query_event_source_->Listen(query); - } else if(first_listen_to_remote_store){ + query_info.target_id = + query_event_source_->Listen(query, first_listen_to_remote_store); + } else if (first_listen_to_remote_store) { query_event_source_->ListenToRemoteStore(query); } return query_info.target_id; @@ -76,16 +78,15 @@ void EventManager::RemoveQueryListener( QueryListenersInfo& query_info = found_iter->second; query_info.Erase(listener); last_listen = query_info.listeners.empty(); - last_listen_to_remote_store = !query_info.has_remote_listeners() && listener->listens_to_remote_store(); - + last_listen_to_remote_store = !query_info.has_remote_listeners() && + listener->listens_to_remote_store(); } if (last_listen) { queries_.erase(found_iter); - query_event_source_->StopListening(query); - } else if(last_listen_to_remote_store){ + query_event_source_->StopListening(query, last_listen_to_remote_store); + } else if (last_listen_to_remote_store) { query_event_source_->StopListeningToRemoteStore(query); - } } diff --git a/Firestore/core/src/core/event_manager.h b/Firestore/core/src/core/event_manager.h index 2d15a06c793..ea521976bca 100644 --- a/Firestore/core/src/core/event_manager.h +++ b/Firestore/core/src/core/event_manager.h @@ -23,13 +23,13 @@ #include #include "Firestore/core/src/core/query.h" +#include "Firestore/core/src/core/query_listener.h" #include "Firestore/core/src/core/sync_engine_callback.h" #include "Firestore/core/src/core/view_snapshot.h" #include "Firestore/core/src/model/model_fwd.h" #include "Firestore/core/src/util/empty.h" #include "Firestore/core/src/util/status_fwd.h" #include "absl/types/optional.h" -#include "Firestore/core/src/core/query_listener.h" namespace firebase { namespace firestore { diff --git a/Firestore/core/src/core/sync_engine.cc b/Firestore/core/src/core/sync_engine.cc index a6ec3c6b1bb..f51492dea16 100644 --- a/Firestore/core/src/core/sync_engine.cc +++ b/Firestore/core/src/core/sync_engine.cc @@ -104,7 +104,7 @@ void SyncEngine::AssertCallbackExists(absl::string_view source) { "Tried to call '%s' before callback was registered.", source); } -TargetId SyncEngine::Listen(Query query) { +TargetId SyncEngine::Listen(Query query, bool shouldListenToRemote) { AssertCallbackExists("Listen"); HARD_ASSERT(query_views_by_query_.find(query) == query_views_by_query_.end(), @@ -121,7 +121,9 @@ TargetId SyncEngine::Listen(Query query) { snapshots.push_back(std::move(view_snapshot)); sync_engine_callback_->OnViewSnapshots(std::move(snapshots)); - remote_store_->Listen(std::move(target_data)); + if (shouldListenToRemote) { + remote_store_->Listen(std::move(target_data)); + } return target_id; } @@ -164,10 +166,12 @@ ViewSnapshot SyncEngine::InitializeViewAndComputeSnapshot( void SyncEngine::ListenToRemoteStore(Query query) { AssertCallbackExists("ListenToRemoteStore"); TargetData target_data = local_store_->AllocateTarget(query.ToTarget()); - remote_store_->Listen(std::move(target_data));; + remote_store_->Listen(std::move(target_data)); + ; } -void SyncEngine::StopListening(const Query& query) { +void SyncEngine::StopListening(const Query& query, + bool shouldUnlistenToRemote) { AssertCallbackExists("StopListening"); auto query_view = query_views_by_query_[query]; @@ -182,7 +186,9 @@ void SyncEngine::StopListening(const Query& query) { if (queries.empty()) { local_store_->ReleaseTarget(target_id); - remote_store_->StopListening(target_id); + if (shouldUnlistenToRemote) { + remote_store_->StopListening(target_id); + } RemoveAndCleanupTarget(target_id, Status::OK()); } } diff --git a/Firestore/core/src/core/sync_engine.h b/Firestore/core/src/core/sync_engine.h index dabe5749a75..dda14569e19 100644 --- a/Firestore/core/src/core/sync_engine.h +++ b/Firestore/core/src/core/sync_engine.h @@ -76,17 +76,15 @@ class QueryEventSource { * * @return the target ID assigned to the query. */ - virtual model::TargetId Listen(Query query) = 0; - + virtual model::TargetId Listen(Query query, bool shouldListenToRemote) = 0; virtual void ListenToRemoteStore(Query query) = 0; - /** Stops listening to a query previously listened to via `Listen`. */ - virtual void StopListening(const Query& query) = 0; + virtual void StopListening(const Query& query, + bool shouldUnlistenToRemote) = 0; virtual void StopListeningToRemoteStore(const Query& query) = 0; - }; /** @@ -114,12 +112,13 @@ class SyncEngine : public remote::RemoteStoreCallback, public QueryEventSource { void SetCallback(SyncEngineCallback* callback) override { sync_engine_callback_ = callback; } - model::TargetId Listen(Query query) override; + model::TargetId Listen(Query query, + bool shouldListenToRemote = true) override; void ListenToRemoteStore(Query query) override; - void StopListening(const Query& query) override; + void StopListening(const Query& query, + bool shouldUnlistenToRemote = true) override; void StopListeningToRemoteStore(const Query& query) override; - /** * Initiates the write of local mutation batch which involves adding the * writes to the mutation queue, notifying the remote store about new diff --git a/Firestore/core/test/unit/core/event_manager_test.cc b/Firestore/core/test/unit/core/event_manager_test.cc index 11f18c7830c..2f9966e78ab 100644 --- a/Firestore/core/test/unit/core/event_manager_test.cc +++ b/Firestore/core/test/unit/core/event_manager_test.cc @@ -60,9 +60,9 @@ std::shared_ptr NoopQueryListener(core::Query query) { class MockEventSource : public core::QueryEventSource { public: MOCK_METHOD1(SetCallback, void(core::SyncEngineCallback*)); - MOCK_METHOD1(Listen, model::TargetId(core::Query)); + MOCK_METHOD2(Listen, model::TargetId(core::Query, bool)); MOCK_METHOD1(ListenToRemoteStore, void(core::Query)); - MOCK_METHOD1(StopListening, void(const core::Query&)); + MOCK_METHOD2(StopListening, void(const core::Query&, bool)); MOCK_METHOD1(StopListeningToRemoteStore, void(const core::Query&)); }; @@ -75,14 +75,14 @@ TEST(EventManagerTest, HandlesManyListnersPerQuery) { EXPECT_CALL(mock_event_source, SetCallback(_)); EventManager event_manager(&mock_event_source); - EXPECT_CALL(mock_event_source, Listen(query)); + EXPECT_CALL(mock_event_source, Listen(query, true)); event_manager.AddQueryListener(listener1); // Expecting no activity from mock_event_source. event_manager.AddQueryListener(listener2); event_manager.RemoveQueryListener(listener2); - EXPECT_CALL(mock_event_source, StopListening(query)); + EXPECT_CALL(mock_event_source, StopListening(query, true)); event_manager.RemoveQueryListener(listener1); } @@ -93,7 +93,7 @@ TEST(EventManagerTest, HandlesUnlistenOnUnknownListenerGracefully) { MockEventSource mock_event_source; EventManager event_manager(&mock_event_source); - EXPECT_CALL(mock_event_source, StopListening(_)).Times(0); + EXPECT_CALL(mock_event_source, StopListening(_, true)).Times(0); event_manager.RemoveQueryListener(listener); } @@ -130,10 +130,10 @@ TEST(EventManagerTest, NotifiesListenersInTheRightOrder) { MockEventSource mock_event_source; EventManager event_manager(&mock_event_source); - EXPECT_CALL(mock_event_source, Listen(query1)); + EXPECT_CALL(mock_event_source, Listen(query1, true)); event_manager.AddQueryListener(listener1); - EXPECT_CALL(mock_event_source, Listen(query2)); + EXPECT_CALL(mock_event_source, Listen(query2, true)); event_manager.AddQueryListener(listener2); event_manager.AddQueryListener(listener3); From 1e8aafaff343fd5c231fe4e57f4d61d2f3647c4a Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:45:09 -0500 Subject: [PATCH 04/17] Create FIRSnapshotListenerSourceTests.mm --- .../API/FIRSnapshotListenerSourceTests.mm | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm diff --git a/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm b/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm new file mode 100644 index 00000000000..b46f05fed63 --- /dev/null +++ b/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm @@ -0,0 +1,67 @@ +/* + * Copyright 2024 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#import + +#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h" +#import "Firestore/Example/Tests/Util/FSTHelpers.h" +#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" +#import "Firestore/Example/Tests/Util/FSTTestingHooks.h" + +#import "Firestore/Source/API/FIRAggregateQuerySnapshot+Internal.h" +#import "Firestore/Source/API/FIRQuery+Internal.h" + +@interface FIRSnapshotListenerSourceTests : FSTIntegrationTestCase +@end + +@implementation FIRSnapshotListenerSourceTests + +- (FIRSnapshotListenOptions *)optionsWithSourceFromCache { + FIRSnapshotListenOptions *options = [[FIRSnapshotListenOptions alloc] init]; + return [options optionsWithSource:FIRListenSourceCache]; +} +- (FIRSnapshotListenOptions *)optionsWithSourceFromCacheAndIncludeMetadataChanges { + FIRSnapshotListenOptions *options = [[FIRSnapshotListenOptions alloc] init]; + return [[options optionsWithSource:FIRListenSourceCache] optionsWithIncludeMetadataChanges:YES]; +} + +- (void)canRaiseSnapshotFromCacheForQuery { + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0} + }]; + + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + + // populate the cache. + [self readDocumentSetForRef:query]; + + + FIRSnapshotListenOptions *optionsFromCache = [self optionsWithSourceFromCache]; + id registration = [query addSnapshotListenerWithOptions:optionsFromCache listener:self.eventAccumulator.valueEventHandler]; + + FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @1L}])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + + [registration remove]; +} + + + + +@end From 95d2914c659cf60032f32fee90ad0452635eefc4 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 9 Feb 2024 13:26:35 -0500 Subject: [PATCH 05/17] add integration tests --- .../Firestore.xcodeproj/project.pbxproj | 192 ++++++ .../Tests/Integration/API/FIRBundlesTests.mm | 46 ++ .../API/FIRSnapshotListenerSourceTests.mm | 597 +++++++++++++++++- 3 files changed, 813 insertions(+), 22 deletions(-) diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 7637adf5aca..3c84debef5d 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ 056542AD1D0F78E29E22EFA9 /* grpc_connection_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D9649021544D4F00EB9CFB /* grpc_connection_test.cc */; }; 0575F3004B896D94456A74CE /* status_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3CAA33F964042646FDDAF9F9 /* status_testing.cc */; }; 0595B5EBEB8F09952B72C883 /* logic_utils_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 28B45B2104E2DAFBBF86DBB7 /* logic_utils_test.cc */; }; + 05BAC479CA3AB13B7A5047E7 /* FIRSnapshotListenerSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 230BC40D8C715CABFB10BBA9 /* FIRSnapshotListenerSourceTests.mm */; }; 05D99904EA713414928DD920 /* query_listener_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7C3F995E040E9E9C5E8514BB /* query_listener_test.cc */; }; 062072B72773A055001655D7 /* AsyncAwaitIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062072B62773A055001655D7 /* AsyncAwaitIntegrationTests.swift */; }; 062072B82773A055001655D7 /* AsyncAwaitIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062072B62773A055001655D7 /* AsyncAwaitIntegrationTests.swift */; }; @@ -143,6 +144,7 @@ 13ED75EFC2F6917951518A4B /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3D050936A2D52257FD17FB6E /* md5_test.cc */; }; 143FBD21E02C709E3E6E8993 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */; }; 1465E362F7BA7A3D063E61C7 /* database_id_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB71064B201FA60300344F18 /* database_id_test.cc */; }; + 14664F6DD55A8ADE13524FEF /* CMakeConfigureLog.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 6739481496019FB93579846B /* CMakeConfigureLog.yaml */; }; 146C140B254F3837A4DD7AE8 /* bits_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D01201BC69F00D97691 /* bits_test.cc */; }; 152543FD706D5E8851C8DA92 /* precondition_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */; }; 153DBBCAF6D4FFA8ABC2EBDF /* leveldb_query_engine_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DB1F1E1B1ED15E8D042144B1 /* leveldb_query_engine_test.cc */; }; @@ -270,6 +272,7 @@ 26C4E52128C8E7B5B96BECC4 /* defer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8ABAC2E0402213D837F73DC3 /* defer_test.cc */; }; 26C577D159CFFD73E24D543C /* memory_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 74FBEFA4FE4B12C435011763 /* memory_mutation_queue_test.cc */; }; 26CB3D7C871BC56456C6021E /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; }; + 26FE3A7AC99693E1029BC437 /* rules.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 086F5AEE493AF6473EF134D0 /* rules.ninja */; }; 276A563D546698B6AAC20164 /* annotations.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */; }; 27AF4C4BAFE079892D4F5341 /* Validation_BloomFilterTest_MD5_50000_1_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 4B3E4A77493524333133C5DC /* Validation_BloomFilterTest_MD5_50000_1_bloom_filter_proto.json */; }; 27E46C94AAB087C80A97FF7F /* FIRServerTimestampTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06E202154D600B64F25 /* FIRServerTimestampTests.mm */; }; @@ -278,11 +281,13 @@ 284A5280F868B2B4B5A1C848 /* leveldb_target_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = E76F0CDF28E5FA62D21DE648 /* leveldb_target_cache_test.cc */; }; 28691225046DF9DF181B3350 /* ordered_code_benchmark.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0473AFFF5567E667A125347B /* ordered_code_benchmark.cc */; }; 28E4B4A53A739AE2C9CF4159 /* FIRDocumentSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04B202154AA00B64F25 /* FIRDocumentSnapshotTests.mm */; }; + 28E7535BEFC7BB66FB58AA97 /* build.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 50E21D4A2B87A0A80E248614 /* build.ninja */; }; 29243A4BBB2E2B1530A62C59 /* leveldb_transaction_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 88CF09277CFA45EE1273E3BA /* leveldb_transaction_test.cc */; }; 292BCC76AF1B916752764A8F /* leveldb_bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8E9CD82E60893DDD7757B798 /* leveldb_bundle_cache_test.cc */; }; 297DC2B3C1EB136D58F4BA9C /* byte_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5342CDDB137B4E93E2E85CCA /* byte_string_test.cc */; }; 298E0F8F6EB27AA36BA1CE76 /* FIRQueryUnitTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = FF73B39D04D1760190E6B84A /* FIRQueryUnitTests.mm */; }; 29954A3172DDFE5133D91E24 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; + 2A08DEF81996D661D3FE48BC /* CMakeCXXCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = 5CE650FC4B2A48B9C8BA9C88 /* CMakeCXXCompilerId.o */; }; 2A0925323776AD50C1105BC0 /* counting_query_engine.cc in Sources */ = {isa = PBXBuildFile; fileRef = 99434327614FEFF7F7DC88EC /* counting_query_engine.cc */; }; 2A365DB6DF32631964FE690A /* stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5B5414D28802BC76FDADABD6 /* stream_test.cc */; }; 2A499CFB2831612A045977CD /* message_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CE37875365497FFA8687B745 /* message_test.cc */; }; @@ -308,6 +313,7 @@ 2E0BBA7E627EB240BA11B0D0 /* exponential_backoff_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D1B68420E2AB1A00B35856 /* exponential_backoff_test.cc */; }; 2E169CF1E9E499F054BB873A /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; 2E373EA9D5FF8C6DE2507675 /* field_index_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BF76A8DA34B5B67B4DD74666 /* field_index_test.cc */; }; + 2E3DC61BEF1F7ED477C0B216 /* CMakeCXXCompilerId.cpp in Resources */ = {isa = PBXBuildFile; fileRef = C1396907A38E6E67313C0D74 /* CMakeCXXCompilerId.cpp */; }; 2E5758FE6CFE753B04D50F89 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */; }; 2E76BC76BBCE5FCDDCF5EEBE /* leveldb_bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8E9CD82E60893DDD7757B798 /* leveldb_bundle_cache_test.cc */; }; 2E7CAC076447970DE881E703 /* aggregate_query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AF924C79F49F793992A84879 /* aggregate_query_test.cc */; }; @@ -367,6 +373,7 @@ 37EC6C6EA9169BB99078CA96 /* reference_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 132E32997D781B896672D30A /* reference_set_test.cc */; }; 380A137B785A5A6991BEDF4B /* leveldb_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5FF903AEFA7A3284660FA4C5 /* leveldb_local_store_test.cc */; }; 380E543B7BC6F648BBB250B4 /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3D050936A2D52257FD17FB6E /* md5_test.cc */; }; + 38159A77164B60443D556932 /* clion-environment.txt in Resources */ = {isa = PBXBuildFile; fileRef = B075588AAD5B99791B537B51 /* clion-environment.txt */; }; 38208AC761FF994BA69822BE /* async_queue_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */; }; 3887E1635B31DCD7BC0922BD /* existence_filter_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */; }; 38C37F0CE0AB18F1AAE6E67C /* FSTExceptionCatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B8BFD9B37D1029D238BDD71E /* FSTExceptionCatcher.m */; }; @@ -393,6 +400,7 @@ 3C5D441E7D5C140F0FB14D91 /* bloom_filter_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A2E6F09AD1EE0A6A452E9A08 /* bloom_filter_test.cc */; }; 3CFFA6F016231446367E3A69 /* listen_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A01F315EE100DD57A1 /* listen_spec_test.json */; }; 3D22F56C0DE7C7256C75DC06 /* tree_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4D20A36DBB00BCEB75 /* tree_sorted_map_test.cc */; }; + 3D916BCFBE8366532F4592D8 /* cmake_install.cmake in Resources */ = {isa = PBXBuildFile; fileRef = B03849DC3F7474A592D71754 /* cmake_install.cmake */; }; 3D9619906F09108E34FF0C95 /* FSTSmokeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */; }; 3DBB48F077C97200F32B51A0 /* value_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 40F9D09063A07F710811A84F /* value_util_test.cc */; }; 3DBBC644BE08B140BCC23BD5 /* string_apple_benchmark.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */; }; @@ -401,6 +409,7 @@ 3E101CE56C70F06BA2FDD56C /* Validation_BloomFilterTest_MD5_50000_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 3841925AA60E13A027F565E6 /* Validation_BloomFilterTest_MD5_50000_1_membership_test_result.json */; }; 3E38E4B33855DD6CF7526225 /* bundle_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B5C2A94EE24E60543F62CC35 /* bundle_serializer_test.cc */; }; 3E5FD39FE7442883AB3CE1F2 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 5C68EE4CB94C0DD6E333F546 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json */; }; + 3EC384D0D86A71653799DDD1 /* FIRSnapshotListenerSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 230BC40D8C715CABFB10BBA9 /* FIRSnapshotListenerSourceTests.mm */; }; 3F3C2DAD9F9326BF789B1C96 /* serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 61F72C5520BC48FD001A68CB /* serializer_test.cc */; }; 3F4B6300198FD78E7B19BC5A /* strerror_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */; }; 3F6C9F8A993CF4B0CD51E7F0 /* lru_garbage_collector_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 277EAACC4DD7C21332E8496A /* lru_garbage_collector_test.cc */; }; @@ -409,7 +418,9 @@ 40431BF2A368D0C891229F6E /* FSTMemorySpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */; }; 409B29C81132718B36BF2497 /* Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C8582DFD74E8060C7072104B /* Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json */; }; 409C0F2BFC2E1BECFFAC4D32 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; + 40EADA93E01E6DAF55143DD4 /* TargetDirectories.txt in Resources */ = {isa = PBXBuildFile; fileRef = ACD733FAB49C8DDEB1F9165B /* TargetDirectories.txt */; }; 412BE974741729A6683C386F /* aggregate_query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AF924C79F49F793992A84879 /* aggregate_query_test.cc */; }; + 4130140DE4E5043E3E000721 /* CMakeDetermineCompilerABI_C.bin in Resources */ = {isa = PBXBuildFile; fileRef = CCB165E99909ED6EE0F882D3 /* CMakeDetermineCompilerABI_C.bin */; }; 4173B61CB74EB4CD1D89EE68 /* latlng.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9220B89AAC00B5BCE7 /* latlng.pb.cc */; }; 4194B7BB8B0352E1AC5D69B9 /* precondition_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */; }; 41C1C67BD1A10F2A8D1F5316 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BD051DBE754950FEAC7A446 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json */; }; @@ -438,6 +449,7 @@ 46EAC2828CD942F27834F497 /* persistence_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9113B6F513D0473AEABBAF1F /* persistence_testing.cc */; }; 470A37727BBF516B05ED276A /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; 47136EEB53CF80D7C8436F38 /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = B0520A41251254B3C24024A3 /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json */; }; + 472D630C4885EF2A051D0236 /* CMakeCCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 595B30E8CC49F13B0C750D3D /* CMakeCCompiler.cmake */; }; 4747A986288114C2B7CD179E /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; 474DF520B9859479845C8A4D /* bundle_builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4F5B96F3ABCD2CA901DB1CD4 /* bundle_builder.cc */; }; 475FE2D34C6555A54D77A054 /* empty_credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8FA60B08D59FEA0D6751E87F /* empty_credentials_provider_test.cc */; }; @@ -473,6 +485,7 @@ 4C4D780CA9367DBA324D97FF /* load_bundle_task_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8F1A7B4158D9DD76EE4836BF /* load_bundle_task_test.cc */; }; 4C5292BF643BF14FA2AC5DB1 /* settings_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD12BC1DB2480886D2FB0005 /* settings_test.cc */; }; 4C66806697D7BCA730FA3697 /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D221C2DDC800EFB9CC /* common.pb.cc */; }; + 4C8AA63E619E23AB2A122DCA /* CMakeConfigureLog.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 6739481496019FB93579846B /* CMakeConfigureLog.yaml */; }; 4CC78CA0E9E03F5DCF13FEBD /* Pods_Firestore_Tests_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7DF4A6F740086A2D8C0E28E /* Pods_Firestore_Tests_tvOS.framework */; }; 4CDFF1AE3D639AA89C5C4411 /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; 4D1775B7916D4CDAD1BF1876 /* bundle.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = A366F6AE1A5A77548485C091 /* bundle.pb.cc */; }; @@ -644,9 +657,11 @@ 55E84644D385A70E607A0F91 /* leveldb_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5FF903AEFA7A3284660FA4C5 /* leveldb_local_store_test.cc */; }; 568EC1C0F68A7B95E57C8C6C /* leveldb_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */; }; 56D85436D3C864B804851B15 /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; + 5706E3A2894546779918907A /* CMakeDetermineCompilerABI_CXX.bin in Resources */ = {isa = PBXBuildFile; fileRef = F62829EC3A07509E3C54247E /* CMakeDetermineCompilerABI_CXX.bin */; }; 57171BD004A1691B19A76453 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */; }; 5778E5F1FABEFA450B8CF4BC /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = B0520A41251254B3C24024A3 /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json */; }; 5785C7190F8F8DDE879F16B7 /* Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 478DC75A0DCA6249A616DD30 /* Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json */; }; + 578B89F99DEF39417976162D /* rules.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 086F5AEE493AF6473EF134D0 /* rules.ninja */; }; 57F0E1A1F2B614BA74961D9A /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */; }; 583DF65751B7BBD0A222CAB4 /* byte_stream_cpp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 01D10113ECC5B446DB35E96D /* byte_stream_cpp_test.cc */; }; 58693C153EC597BC25EE9648 /* firebase_auth_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F869D85E900E5AF6CD02E2FC /* firebase_auth_credentials_provider_test.mm */; }; @@ -660,6 +675,7 @@ 59E95B64C460C860E2BC7464 /* load_bundle_task_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8F1A7B4158D9DD76EE4836BF /* load_bundle_task_test.cc */; }; 59F3A9669E0AF835E62D6674 /* Validation_BloomFilterTest_MD5_1_1_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 3FDD0050CA08C8302400C5FB /* Validation_BloomFilterTest_MD5_1_1_bloom_filter_proto.json */; }; 59F512D155DE361095A04ED4 /* FIRSnapshotMetadataTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04D202154AA00B64F25 /* FIRSnapshotMetadataTests.mm */; }; + 59F6CEADB513CBB5F4738161 /* CMakeDetermineCompilerABI_CXX.bin in Resources */ = {isa = PBXBuildFile; fileRef = F62829EC3A07509E3C54247E /* CMakeDetermineCompilerABI_CXX.bin */; }; 5A080105CCBFDB6BF3F3772D /* path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 403DBF6EFB541DFD01582AA3 /* path_test.cc */; }; 5A44725457D6B7805FD66EEB /* bundle_loader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A853C81A6A5A51C9D0389EDA /* bundle_loader_test.cc */; }; 5ACF26A3B0A33784CC525FB0 /* aggregate_query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AF924C79F49F793992A84879 /* aggregate_query_test.cc */; }; @@ -720,6 +736,7 @@ 60985657831B8DDE2C65AC8B /* FIRFieldsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */; }; 60C72F86D2231B1B6592A5E6 /* filesystem_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F51859B394D01C0C507282F1 /* filesystem_test.cc */; }; 6105A1365831B79A7DEEA4F3 /* path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 403DBF6EFB541DFD01582AA3 /* path_test.cc */; }; + 6117E9BC3F39DF2175392D2D /* CMakeCXXCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 255C95F5F18BBB1E6BF77B62 /* CMakeCXXCompiler.cmake */; }; 6141D3FDF5728FCE9CC1DBFA /* bundle_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 79EAA9F7B1B9592B5F053923 /* bundle_spec_test.json */; }; 6156C6A837D78D49ED8B8812 /* index_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 8C7278B604B8799F074F4E8C /* index_spec_test.json */; }; 6161B5032047140C00A99DBB /* FIRFirestoreSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */; }; @@ -748,6 +765,7 @@ 62EC5F7FB416BA124A2B4604 /* byte_stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 432C71959255C5DBDF522F52 /* byte_stream_test.cc */; }; 62F86BBE7DDA5B295B57C8DA /* string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0EE5300F8233D14025EF0456 /* string_apple_test.mm */; }; 6300709ECDE8E0B5A8645F8D /* time_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5497CB76229DECDE000FB92F /* time_testing.cc */; }; + 630A50115EB50102D3DF359B /* CMakeCache.txt in Resources */ = {isa = PBXBuildFile; fileRef = A1A5A15FBB6B8FBF90E12B31 /* CMakeCache.txt */; }; 6325D0E43A402BC5866C9C0E /* defer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8ABAC2E0402213D837F73DC3 /* defer_test.cc */; }; 6359EA7D5C76D462BD31B5E5 /* watch_change_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2D7472BC70C024D736FF74D9 /* watch_change_test.cc */; }; 6380CACCF96A9B26900983DC /* leveldb_target_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = E76F0CDF28E5FA62D21DE648 /* leveldb_target_cache_test.cc */; }; @@ -814,6 +832,7 @@ 6F256C06FCBA46378EC35D72 /* leveldb_overlay_migration_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D8A6D52723B1BABE1B7B8D8F /* leveldb_overlay_migration_manager_test.cc */; }; 6F3CAC76D918D6B0917EDF92 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; 6F45846C159D3C063DBD3CBE /* FirestoreEncoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1235769422B86E65007DDFA9 /* FirestoreEncoderTests.swift */; }; + 6F4DBC1B2FAEEDDAAFC1FBD3 /* build.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 50E21D4A2B87A0A80E248614 /* build.ninja */; }; 6F511ABFD023AEB81F92DB12 /* maybe_document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7E20B89AAC00B5BCE7 /* maybe_document.pb.cc */; }; 6F67601562343B63B8996F7A /* FSTTestingHooks.mm in Sources */ = {isa = PBXBuildFile; fileRef = D85AC18C55650ED230A71B82 /* FSTTestingHooks.mm */; }; 6F914209F46E6552B5A79570 /* async_queue_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */; }; @@ -844,6 +863,7 @@ 73866AA12082B0A5009BB4FF /* FIRArrayTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */; }; 7394B5C29C6E524C2AF964E6 /* counting_query_engine.cc in Sources */ = {isa = PBXBuildFile; fileRef = 99434327614FEFF7F7DC88EC /* counting_query_engine.cc */; }; 73E42D984FB36173A2BDA57C /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; + 73FB09EAD9B8C22EF03A1702 /* CMakeSystem.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 658741EEE206F2C8C4159412 /* CMakeSystem.cmake */; }; 73FE5066020EF9B2892C86BF /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; }; 743DF2DF38CE289F13F44043 /* status_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3CAA33F964042646FDDAF9F9 /* status_testing.cc */; }; 7495E3BAE536CD839EE20F31 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; @@ -878,7 +898,9 @@ 7A8DF35E7DB4278E67E6BDB3 /* snapshot_version_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */; }; 7AA8771FE1F048D012E5E317 /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; 7ACA8D967438B5CD9DA4C884 /* memory_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F6CA0C5638AB6627CB5B4CF4 /* memory_local_store_test.cc */; }; + 7ACBA78419279E4BAAE67234 /* CMakeConfigureLog.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 6739481496019FB93579846B /* CMakeConfigureLog.yaml */; }; 7AD020FC27493FF8E659436C /* existence_filter_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */; }; + 7AF62F2F7C186028303D7AEC /* cmake_install.cmake in Resources */ = {isa = PBXBuildFile; fileRef = B03849DC3F7474A592D71754 /* cmake_install.cmake */; }; 7B0EA399F899537ACCC84E53 /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; 7B0F073BDB6D0D6E542E23D4 /* query.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D621C2DDC800EFB9CC /* query.pb.cc */; }; 7B74447D211586D9D1CC82BB /* datastore_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3167BD972EFF8EC636530E59 /* datastore_test.cc */; }; @@ -902,6 +924,7 @@ 7DED491019248CE9B9E9EB50 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; 7E1B1335B2EC566FB25B710C /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C8FB22BCB9F454DA44BA80C8 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json */; }; 7E82D412BB56728BEBB7EF46 /* bundle_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B5C2A94EE24E60543F62CC35 /* bundle_serializer_test.cc */; }; + 7E8D4C5AF3649DCD4F736ECA /* CMakeCXXCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = 5CE650FC4B2A48B9C8BA9C88 /* CMakeCXXCompilerId.o */; }; 7E97B0F04E25610FF37E9259 /* memory_target_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2286F308EFB0534B1BDE05B9 /* memory_target_cache_test.cc */; }; 7EAB3129A58368EE4BD449ED /* leveldb_migrations_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = EF83ACD5E1E9F25845A9ACED /* leveldb_migrations_test.cc */; }; 7EF540911720DAAF516BEDF0 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; @@ -909,6 +932,7 @@ 7F5501F917A11DE4E11F5CC7 /* Validation_BloomFilterTest_MD5_50000_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 3841925AA60E13A027F565E6 /* Validation_BloomFilterTest_MD5_50000_1_membership_test_result.json */; }; 7F6199159E24E19E2A3F5601 /* schedule_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B0B005A79E765AF02793DCE /* schedule_test.cc */; }; 7F771EB980D9CFAAB4764233 /* view_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = A5466E7809AD2871FFDE6C76 /* view_testing.cc */; }; + 7F87A0D114D0C629532853D8 /* CMakeCXXCompilerId.cpp in Resources */ = {isa = PBXBuildFile; fileRef = C1396907A38E6E67313C0D74 /* CMakeCXXCompilerId.cpp */; }; 7F9CE96304D413F7E7AA0DA0 /* memory_target_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2286F308EFB0534B1BDE05B9 /* memory_target_cache_test.cc */; }; 7FF39B8BD834F8267BDCBCC6 /* status_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5493A423225F9990006DE7BA /* status_apple_test.mm */; }; 804B0C6CCE3933CF3948F249 /* grpc_streaming_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964922154AB8F00EB9CFB /* grpc_streaming_reader_test.cc */; }; @@ -917,6 +941,7 @@ 80D7FEBB1056E489F24C6C8F /* firebase_app_check_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F119BDDF2F06B3C0883B8297 /* firebase_app_check_credentials_provider_test.mm */; }; 80D8B7D6FFFEA12AF10E4E2B /* leveldb_overlay_migration_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D8A6D52723B1BABE1B7B8D8F /* leveldb_overlay_migration_manager_test.cc */; }; 814724DE70EFC3DDF439CD78 /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; + 8150ED535D52D9F06922DD7A /* TargetDirectories.txt in Resources */ = {isa = PBXBuildFile; fileRef = ACD733FAB49C8DDEB1F9165B /* TargetDirectories.txt */; }; 816E8E62DC163649BA96951C /* EncodableFieldValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1235769122B7E915007DDFA9 /* EncodableFieldValueTests.swift */; }; 81A6B241E63540900F205817 /* view_snapshot_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CC572A9168BBEF7B83E4BBC5 /* view_snapshot_test.cc */; }; 81AD038D81C1A8C2074B98B1 /* Validation_BloomFilterTest_MD5_5000_0001_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 728F617782600536F2561463 /* Validation_BloomFilterTest_MD5_5000_0001_bloom_filter_proto.json */; }; @@ -933,6 +958,7 @@ 8388418F43042605FB9BFB92 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; 839D8B502026706419FE09D6 /* leveldb_index_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 166CE73C03AB4366AAC5201C /* leveldb_index_manager_test.cc */; }; 83A9CD3B6E791A860CE81FA1 /* async_queue_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */; }; + 83C76AE4AD8EA09842C5E3F0 /* CMakeDetermineCompilerABI_C.bin in Resources */ = {isa = PBXBuildFile; fileRef = CCB165E99909ED6EE0F882D3 /* CMakeDetermineCompilerABI_C.bin */; }; 8403D519C916C72B9C7F2FA1 /* FIRValidationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06D202154D600B64F25 /* FIRValidationTests.mm */; }; 8405FF2BFBB233031A887398 /* event_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6F57521E161450FAF89075ED /* event_manager_test.cc */; }; 8413BD9958F6DD52C466D70F /* sorted_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */; }; @@ -941,7 +967,9 @@ 8460C97C9209D7DAF07090BD /* FIRFieldsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */; }; 84E75527F3739131C09BEAA5 /* target_index_matcher_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 63136A2371C0C013EC7A540C /* target_index_matcher_test.cc */; }; 851346D66DEC223E839E3AA9 /* memory_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 74FBEFA4FE4B12C435011763 /* memory_mutation_queue_test.cc */; }; + 852C67A7F19AECE97AEDC367 /* CMakeCache.txt in Resources */ = {isa = PBXBuildFile; fileRef = A1A5A15FBB6B8FBF90E12B31 /* CMakeCache.txt */; }; 856A1EAAD674ADBDAAEDAC37 /* bundle_builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4F5B96F3ABCD2CA901DB1CD4 /* bundle_builder.cc */; }; + 85A036436686AA6D71FDF073 /* cmake.check_cache in Resources */ = {isa = PBXBuildFile; fileRef = 1B9C7CBE95D2497D4E0B5741 /* cmake.check_cache */; }; 85A33A9CE33207C2333DDD32 /* FIRTransactionOptionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CF39ECA1293D21A0A2AB2626 /* FIRTransactionOptionsTests.mm */; }; 85B8918FC8C5DC62482E39C3 /* resource_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2B02024FFD70028D6BE /* resource_path_test.cc */; }; 85BC2AB572A400114BF59255 /* limbo_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */; }; @@ -954,10 +982,12 @@ 86494278BE08F10A8AAF9603 /* iterator_adaptors_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */; }; 867B370BF2DF84B6AB94B874 /* filesystem_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA02DA2FCD0001CFC6EB08DA /* filesystem_testing.cc */; }; 8683BBC3AC7B01937606A83B /* firestore.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D421C2DDC800EFB9CC /* firestore.pb.cc */; }; + 86A3EBF677D4C9FDE5562C5B /* CMakeDetermineCompilerABI_CXX.bin in Resources */ = {isa = PBXBuildFile; fileRef = F62829EC3A07509E3C54247E /* CMakeDetermineCompilerABI_CXX.bin */; }; 86B413EC49E3BBBEBF1FB7A0 /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AB49283E544497A9C5A0E59 /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json */; }; 86E6FC2B7657C35B342E1436 /* sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */; }; 8705C4856498F66E471A0997 /* FIRWriteBatchTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; + 87AA0B64034604D5461100A8 /* cmake_install.cmake in Resources */ = {isa = PBXBuildFile; fileRef = B03849DC3F7474A592D71754 /* cmake_install.cmake */; }; 87B5972F1C67CB8D53ADA024 /* object_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 214877F52A705012D6720CA0 /* object_value_test.cc */; }; 87B5AC3EBF0E83166B142FA4 /* string_apple_benchmark.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */; }; 881E55152AB34465412F8542 /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; @@ -975,11 +1005,13 @@ 8B0EC945E74A03BD3ED8F9AA /* status_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3CAA33F964042646FDDAF9F9 /* status_testing.cc */; }; 8B2921C75DB7DD912AE14B8F /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = D8E530B27D5641B9C26A452C /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json */; }; 8B31F63673F3B5238DE95AFB /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; }; + 8B35131E57699519EF23EE7E /* CMakeCXXCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 255C95F5F18BBB1E6BF77B62 /* CMakeCXXCompiler.cmake */; }; 8B3EB33933D11CF897EAF4C3 /* leveldb_index_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 166CE73C03AB4366AAC5201C /* leveldb_index_manager_test.cc */; }; 8C39F6D4B3AA9074DF00CFB8 /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CFC201A2EE200D97691 /* string_util_test.cc */; }; 8C602DAD4E8296AB5EFB962A /* firestore.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D421C2DDC800EFB9CC /* firestore.pb.cc */; }; 8C82D4D3F9AB63E79CC52DC8 /* Pods_Firestore_IntegrationTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */; }; 8D0EF43F1B7B156550E65C20 /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; }; + 8D43DF691E6ABA6A35EEE059 /* clion-Debug-log.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3C3B362DACE291EF501F4533 /* clion-Debug-log.txt */; }; 8DBA8DC55722ED9D3A1BB2C9 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 1A7D48A017ECB54FD381D126 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json */; }; 8E103A426D6E650DC338F281 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C8FB22BCB9F454DA44BA80C8 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json */; }; 8E41D53C77C30372840B0367 /* Validation_BloomFilterTest_MD5_5000_0001_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 728F617782600536F2561463 /* Validation_BloomFilterTest_MD5_5000_0001_bloom_filter_proto.json */; }; @@ -992,6 +1024,7 @@ 900D0E9F18CE3DB954DD0D1E /* async_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB467B208E9A8200554BA2 /* async_queue_test.cc */; }; 9012B0E121B99B9C7E54160B /* query_engine_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B8A853940305237AFDA8050B /* query_engine_test.cc */; }; 9016EF298E41456060578C90 /* field_transform_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7515B47C92ABEEC66864B55C /* field_transform_test.cc */; }; + 9066CD3ABC6357B97DF264C4 /* build.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 50E21D4A2B87A0A80E248614 /* build.ninja */; }; 906DB5C85F57EFCBD2027E60 /* grpc_unary_call_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964942163E63900EB9CFB /* grpc_unary_call_test.cc */; }; 907DF0E63248DBF0912CC56D /* filesystem_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA02DA2FCD0001CFC6EB08DA /* filesystem_testing.cc */; }; 90B9302B082E6252AF4E7DC7 /* leveldb_migrations_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = EF83ACD5E1E9F25845A9ACED /* leveldb_migrations_test.cc */; }; @@ -1008,6 +1041,7 @@ 925BE64990449E93242A00A2 /* memory_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 74FBEFA4FE4B12C435011763 /* memory_mutation_queue_test.cc */; }; 92D7081085679497DC112EDB /* persistence_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9113B6F513D0473AEABBAF1F /* persistence_testing.cc */; }; 92EFF0CC2993B43CBC7A61FF /* grpc_streaming_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964922154AB8F00EB9CFB /* grpc_streaming_reader_test.cc */; }; + 9366DC0A832BD2EE267BF61F /* CMakeCCompilerId.c in Sources */ = {isa = PBXBuildFile; fileRef = 987319851B67BC36AF095879 /* CMakeCCompilerId.c */; }; 9382BE7190E7750EE7CCCE7C /* write_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A51F315EE100DD57A1 /* write_spec_test.json */; }; 938F2AF6EC5CD0B839300DB0 /* query.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D621C2DDC800EFB9CC /* query.pb.cc */; }; 939C898FE9D129F6A2EA259C /* FSTHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03A2021401F00B64F25 /* FSTHelpers.mm */; }; @@ -1048,6 +1082,7 @@ 9B2CD4CBB1DFE8BC3C81A335 /* async_queue_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4680208EA0BE00554BA2 /* async_queue_libdispatch_test.mm */; }; 9B9BFC16E26BDE4AE0CDFF4B /* firebase_auth_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F869D85E900E5AF6CD02E2FC /* firebase_auth_credentials_provider_test.mm */; }; 9BEC62D59EB2C68342F493CD /* credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2F4FA4576525144C5069A7A5 /* credentials_provider_test.cc */; }; + 9BF2FDA87D5A6D44C93903C4 /* CMakeCCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 595B30E8CC49F13B0C750D3D /* CMakeCCompiler.cmake */; }; 9C1F25177DC5753B075DCF65 /* existence_filter_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */; }; 9C366448F9BA7A4AC0821AF7 /* bundle_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 79EAA9F7B1B9592B5F053923 /* bundle_spec_test.json */; }; 9C86EEDEA131BFD50255EEF1 /* comparison_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB928200D59F600E00ABC /* comparison_test.cc */; }; @@ -1063,6 +1098,7 @@ 9F9244225BE2EC88AA0CE4EF /* sorted_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */; }; A05BC6BDA2ABE405009211A9 /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; A06FBB7367CDD496887B86F8 /* leveldb_opener_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 75860CD13AF47EB1EA39EC2F /* leveldb_opener_test.cc */; }; + A0B6EA2529C0090CF0AE7F09 /* CMakeCCompilerId.c in Sources */ = {isa = PBXBuildFile; fileRef = 987319851B67BC36AF095879 /* CMakeCCompilerId.c */; }; A0C6C658DFEE58314586907B /* offline_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A11F315EE100DD57A1 /* offline_spec_test.json */; }; A0D61250F959BC52CEFF9467 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C8FB22BCB9F454DA44BA80C8 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json */; }; A0E1C7F5C7093A498F65C5CF /* memory_bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB4AB1388538CD3CB19EB028 /* memory_bundle_cache_test.cc */; }; @@ -1096,6 +1132,7 @@ A61BB461F3E5822175F81719 /* memory_remote_document_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1CA9800A53669EFBFFB824E3 /* memory_remote_document_cache_test.cc */; }; A6A916A7DEA41EE29FD13508 /* watch_change_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2D7472BC70C024D736FF74D9 /* watch_change_test.cc */; }; A6A9946A006AA87240B37E31 /* defer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8ABAC2E0402213D837F73DC3 /* defer_test.cc */; }; + A6AD059A06E650993296D300 /* CMakeCache.txt in Resources */ = {isa = PBXBuildFile; fileRef = A1A5A15FBB6B8FBF90E12B31 /* CMakeCache.txt */; }; A6BDA28DBC85BC1BAB7061F4 /* leveldb_document_overlay_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AE89CFF09C6804573841397F /* leveldb_document_overlay_cache_test.cc */; }; A6D57EC3A0BF39060705ED29 /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; A6E236CE8B3A47BE32254436 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; @@ -1112,6 +1149,7 @@ A9206FF8FF8834347E9C7DDB /* leveldb_overlay_migration_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D8A6D52723B1BABE1B7B8D8F /* leveldb_overlay_migration_manager_test.cc */; }; A97ED2BAAEDB0F765BBD5F98 /* local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 307FF03D0297024D59348EBD /* local_store_test.cc */; }; A9A9994FB8042838671E8506 /* view_snapshot_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CC572A9168BBEF7B83E4BBC5 /* view_snapshot_test.cc */; }; + AA09E52B7BB746E17C507297 /* cmake.check_cache in Resources */ = {isa = PBXBuildFile; fileRef = 1B9C7CBE95D2497D4E0B5741 /* cmake.check_cache */; }; AA13B6E1EF0AD9E9857AAE1C /* byte_stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 432C71959255C5DBDF522F52 /* byte_stream_test.cc */; }; AA859F27A9098D6886B222A8 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BD051DBE754950FEAC7A446 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json */; }; AAC15E7CCAE79619B2ABB972 /* XCTestCase+Await.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */; }; @@ -1193,7 +1231,9 @@ B510921E4CD441289F6B2B78 /* FSTExceptionCatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B8BFD9B37D1029D238BDD71E /* FSTExceptionCatcher.m */; }; B513F723728E923DFF34F60F /* leveldb_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */; }; B54BA1E76636C0C93334271B /* settings_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD12BC1DB2480886D2FB0005 /* settings_test.cc */; }; + B55BEC44BFBC266607C9AC3A /* CMakeDetermineCompilerABI_C.bin in Resources */ = {isa = PBXBuildFile; fileRef = CCB165E99909ED6EE0F882D3 /* CMakeDetermineCompilerABI_C.bin */; }; B576823475FBCA5EFA583F9C /* leveldb_migrations_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = EF83ACD5E1E9F25845A9ACED /* leveldb_migrations_test.cc */; }; + B58E1F10A44829D192B75390 /* clion-Debug-log.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3C3B362DACE291EF501F4533 /* clion-Debug-log.txt */; }; B592DB7DB492B1C1D5E67D01 /* write.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D921C2DDC800EFB9CC /* write.pb.cc */; }; B5AEF7E4EBC29653DEE856A2 /* strerror_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */; }; B60BAF9ED610F9D4E245EEB3 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 1A7D48A017ECB54FD381D126 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json */; }; @@ -1223,6 +1263,7 @@ B6FB4690208F9BB300554BA2 /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; B6FDE6F91D3F81D045E962A0 /* bits_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D01201BC69F00D97691 /* bits_test.cc */; }; B743F4E121E879EF34536A51 /* leveldb_index_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 166CE73C03AB4366AAC5201C /* leveldb_index_manager_test.cc */; }; + B752A44092D47887759B3046 /* TargetDirectories.txt in Resources */ = {isa = PBXBuildFile; fileRef = ACD733FAB49C8DDEB1F9165B /* TargetDirectories.txt */; }; B7DD5FC63A78FF00E80332C0 /* grpc_stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6BBE42F21262CF400C6A53E /* grpc_stream_test.cc */; }; B8062EBDB8E5B680E46A6DD1 /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; }; B81B6F327B5E3FE820DC3FB3 /* aggregation_result.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = D872D754B8AD88E28AF28B28 /* aggregation_result.pb.cc */; }; @@ -1233,6 +1274,7 @@ B896E5DE1CC27347FAC009C3 /* BasicCompileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE0761F61F2FE68D003233AF /* BasicCompileTests.swift */; }; B921A4F35B58925D958DD9A6 /* reference_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 132E32997D781B896672D30A /* reference_set_test.cc */; }; B9706A5CD29195A613CF4147 /* bundle_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6ECAF7DE28A19C69DF386D88 /* bundle_reader_test.cc */; }; + B970C9A35BDDB6B01AB0454C /* FIRSnapshotListenerSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 230BC40D8C715CABFB10BBA9 /* FIRSnapshotListenerSourceTests.mm */; }; B99452AB7E16B72D1C01FBBC /* datastore_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3167BD972EFF8EC636530E59 /* datastore_test.cc */; }; B998971CE6D0D1DD2AD9250A /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 5B96CC29E9946508F022859C /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json */; }; BA0BB02821F1949783C8AA50 /* FIRCollectionReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */; }; @@ -1240,6 +1282,7 @@ BA3C0BA8082A6FB2546E47AC /* CodableTimestampTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B65C996438B84DBC7616640 /* CodableTimestampTests.swift */; }; BA9A65BD6D993B2801A3C768 /* grpc_connection_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D9649021544D4F00EB9CFB /* grpc_connection_test.cc */; }; BAB43C839445782040657239 /* executor_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4687208F9B9100554BA2 /* executor_std_test.cc */; }; + BACB779C724E629322DF1788 /* CMakeCCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = A19043284553A1757B6D52DD /* CMakeCCompilerId.o */; }; BACBBF4AF2F5455673AEAB35 /* leveldb_migrations_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = EF83ACD5E1E9F25845A9ACED /* leveldb_migrations_test.cc */; }; BB15588CC1622904CF5AD210 /* sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */; }; BB1A6F7D8F06E74FB6E525C5 /* document_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6152AD5202A5385000E5744 /* document_key_test.cc */; }; @@ -1260,11 +1303,13 @@ BDDAE67000DBF10E9EA7FED0 /* nanopb_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6F5B6C1399F92FD60F2C582B /* nanopb_util_test.cc */; }; BDF3A6C121F2773BB3A347A7 /* counting_query_engine.cc in Sources */ = {isa = PBXBuildFile; fileRef = 99434327614FEFF7F7DC88EC /* counting_query_engine.cc */; }; BE1D7C7E413449AFFBA21BCB /* overlay_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = E1459FA70B8FC18DE4B80D0D /* overlay_test.cc */; }; + BE36D35215706DAD0706F226 /* CMakeCXXCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = 5CE650FC4B2A48B9C8BA9C88 /* CMakeCXXCompilerId.o */; }; BE767D2312D2BE84484309A0 /* event_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6F57521E161450FAF89075ED /* event_manager_test.cc */; }; BE92E16A9B9B7AD5EB072919 /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; BEE0294A23AB993E5DE0E946 /* leveldb_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */; }; BEF0365AD2718B8B70715978 /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; BEF35ECEE80F9F5161E7743A /* filter_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F02F734F272C3C70D1307076 /* filter_test.cc */; }; + BF624DA8E6F60FD86BC36D90 /* CMakeCCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 595B30E8CC49F13B0C750D3D /* CMakeCCompiler.cmake */; }; BFBE4732E93E38317B110778 /* index_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 8C7278B604B8799F074F4E8C /* index_spec_test.json */; }; BFCDC78CD851F109EB7A1422 /* Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 57F8EE51B5EFC9FAB185B66C /* Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json */; }; BFEAC4151D3AA8CE1F92CC2D /* FSTSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03020213FFC00B64F25 /* FSTSpecTests.mm */; }; @@ -1272,6 +1317,7 @@ C06E54352661FCFB91968640 /* mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3068AA9DFBBA86C1FE2A946E /* mutation_queue_test.cc */; }; C09BDBA73261578F9DA74CEE /* firebase_auth_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F869D85E900E5AF6CD02E2FC /* firebase_auth_credentials_provider_test.mm */; }; C0AD8DB5A84CAAEE36230899 /* status_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352C20A3B3D7003E0143 /* status_test.cc */; }; + C0ECFC49885BFC559FDD0E8A /* CMakeCXXCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 255C95F5F18BBB1E6BF77B62 /* CMakeCXXCompiler.cmake */; }; C0EFC5FB79517679C377C252 /* schedule_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B0B005A79E765AF02793DCE /* schedule_test.cc */; }; C10417B067155BE78E19807D /* FIRIndexingTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 795AA8FC31D2AF6864B07D39 /* FIRIndexingTests.mm */; }; C1237EE2A74F174A3DF5978B /* memory_target_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2286F308EFB0534B1BDE05B9 /* memory_target_cache_test.cc */; }; @@ -1357,6 +1403,7 @@ D156B9F19B5B29E77664FDFC /* logic_utils_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 28B45B2104E2DAFBBF86DBB7 /* logic_utils_test.cc */; }; D1690214781198276492442D /* event_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6F57521E161450FAF89075ED /* event_manager_test.cc */; }; D18DBCE3FE34BF5F14CF8ABD /* mutation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = C8522DE226C467C54E6788D8 /* mutation_test.cc */; }; + D1B0B9ED583F83E027C49A9B /* CMakeSystem.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 658741EEE206F2C8C4159412 /* CMakeSystem.cmake */; }; D1BCDAEACF6408200DFB9870 /* overlay_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = E1459FA70B8FC18DE4B80D0D /* overlay_test.cc */; }; D21060F8115A5F48FC3BF335 /* local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 307FF03D0297024D59348EBD /* local_store_test.cc */; }; D22B96C19A0F3DE998D4320C /* delayed_constructor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */; }; @@ -1371,6 +1418,7 @@ D3CB03747E34D7C0365638F1 /* transform_operation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 33607A3AE91548BD219EC9C6 /* transform_operation_test.cc */; }; D4572060A0FD4D448470D329 /* leveldb_transaction_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 88CF09277CFA45EE1273E3BA /* leveldb_transaction_test.cc */; }; D4D8BA32ACC5C2B1B29711C0 /* memory_lru_garbage_collector_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9765D47FA12FA283F4EFAD02 /* memory_lru_garbage_collector_test.cc */; }; + D4EC6FAED8E00499D598CC55 /* rules.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 086F5AEE493AF6473EF134D0 /* rules.ninja */; }; D4F85AEACD2FD03C738D1052 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 5C68EE4CB94C0DD6E333F546 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json */; }; D50232D696F19C2881AC01CE /* token_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A082AFDD981B07B5AD78FDE8 /* token_test.cc */; }; D550446303227FB1B381133C /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; @@ -1393,6 +1441,9 @@ D73BBA4AB42940AB187169E3 /* listen_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A01F315EE100DD57A1 /* listen_spec_test.json */; }; D756A1A63E626572EE8DF592 /* firestore.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D421C2DDC800EFB9CC /* firestore.pb.cc */; }; D77941FD93DBE862AEF1F623 /* FSTTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */; }; + D7E1009A9C7C84F6BE8804A5 /* CMakeSystem.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 658741EEE206F2C8C4159412 /* CMakeSystem.cmake */; }; + D871697BED043E498873CEFD /* CMakeCCompilerId.c in Sources */ = {isa = PBXBuildFile; fileRef = 987319851B67BC36AF095879 /* CMakeCCompilerId.c */; }; + D90E6FE567D20E897A0B1201 /* CMakeCCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = A19043284553A1757B6D52DD /* CMakeCCompilerId.o */; }; D91D86B29B86A60C05879A48 /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; }; D9366A834BFF13246DC3AF9E /* field_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2AD2023DDB20028D6BE /* field_path_test.cc */; }; D94A1862B8FB778225DB54A1 /* filesystem_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F51859B394D01C0C507282F1 /* filesystem_test.cc */; }; @@ -1418,6 +1469,7 @@ DC1C711290E12F8EF3601151 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; DC48407370E87F2233D7AB7E /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; DC6804424FC8F7B3044DD0BB /* random_access_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 014C60628830D95031574D15 /* random_access_queue_test.cc */; }; + DC7F5C18D65040E505E580A9 /* clion-environment.txt in Resources */ = {isa = PBXBuildFile; fileRef = B075588AAD5B99791B537B51 /* clion-environment.txt */; }; DCC8F3D4AA87C81AB3FD9491 /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3D050936A2D52257FD17FB6E /* md5_test.cc */; }; DCD83C545D764FB15FD88B02 /* counting_query_engine.cc in Sources */ = {isa = PBXBuildFile; fileRef = 99434327614FEFF7F7DC88EC /* counting_query_engine.cc */; }; DD04F7FE7A1ADE230A247DBC /* byte_stream_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7628664347B9C96462D4BF17 /* byte_stream_apple_test.mm */; }; @@ -1463,6 +1515,7 @@ E3319DC1804B69F0ED1FFE02 /* memory_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 74FBEFA4FE4B12C435011763 /* memory_mutation_queue_test.cc */; }; E375FBA0632EFB4D14C4E5A9 /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; }; E37C52277CD00C57E5848A0E /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 5C68EE4CB94C0DD6E333F546 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json */; }; + E432BC38147553374D70AE62 /* CMakeCXXCompilerId.cpp in Resources */ = {isa = PBXBuildFile; fileRef = C1396907A38E6E67313C0D74 /* CMakeCXXCompilerId.cpp */; }; E434ACDF63F219F3031F292E /* ConditionalConformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3228F51DCDC2E90D5C58F97 /* ConditionalConformanceTests.swift */; }; E435450184AEB51EE8435F66 /* write.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D921C2DDC800EFB9CC /* write.pb.cc */; }; E441A53D035479C53C74A0E6 /* recovery_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 9C1AFCC9E616EC33D6E169CF /* recovery_spec_test.json */; }; @@ -1486,12 +1539,14 @@ E72A77095FF6814267DF0F6D /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = E2E39422953DE1D3C7B97E77 /* md5_testing.cc */; }; E74D6C1056DE29969B5C4C62 /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3D050936A2D52257FD17FB6E /* md5_test.cc */; }; E764F0F389E7119220EB212C /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; + E789BD9F16671F4AA48E9A52 /* CMakeCCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = A19043284553A1757B6D52DD /* CMakeCCompilerId.o */; }; E7CE4B1ECD008983FAB90F44 /* string_format_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54131E9620ADE678001DF3FF /* string_format_test.cc */; }; E7D415B8717701B952C344E5 /* executor_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4687208F9B9100554BA2 /* executor_std_test.cc */; }; E827A3B15D6C8C1298A7BC72 /* Validation_BloomFilterTest_MD5_5000_1_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 4375BDCDBCA9938C7F086730 /* Validation_BloomFilterTest_MD5_5000_1_bloom_filter_proto.json */; }; E82F8EBBC8CC37299A459E73 /* hashing_test_apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = B69CF3F02227386500B281C8 /* hashing_test_apple.mm */; }; E8495A8D1E11C0844339CCA3 /* database_info_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D92E20235D22000A432D /* database_info_test.cc */; }; E8608D40B683938C6D785627 /* credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2F4FA4576525144C5069A7A5 /* credentials_provider_test.cc */; }; + E86B903869E65E81DA7ED5C6 /* clion-Debug-log.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3C3B362DACE291EF501F4533 /* clion-Debug-log.txt */; }; E884336B43BBD1194C17E3C4 /* status_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3CAA33F964042646FDDAF9F9 /* status_testing.cc */; }; E8AB8024B70F6C960D8C7530 /* document_overlay_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = FFCA39825D9678A03D1845D0 /* document_overlay_cache_test.cc */; }; E8BA7055EDB8B03CC99A528F /* recovery_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 9C1AFCC9E616EC33D6E169CF /* recovery_spec_test.json */; }; @@ -1541,6 +1596,7 @@ F05B277F16BDE6A47FE0F943 /* local_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F8043813A5D16963EC02B182 /* local_serializer_test.cc */; }; F08DA55D31E44CB5B9170CCE /* limbo_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */; }; F091532DEE529255FB008E25 /* snapshot_version_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */; }; + F0B0A9C420B3429807FFAF33 /* clion-environment.txt in Resources */ = {isa = PBXBuildFile; fileRef = B075588AAD5B99791B537B51 /* clion-environment.txt */; }; F0C8EB1F4FB56401CFA4F374 /* object_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 214877F52A705012D6720CA0 /* object_value_test.cc */; }; F0EA84FB66813F2BC164EF7C /* token_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A082AFDD981B07B5AD78FDE8 /* token_test.cc */; }; F10A3E4E164A5458DFF7EDE6 /* leveldb_remote_document_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0840319686A223CC4AD3FAB1 /* leveldb_remote_document_cache_test.cc */; }; @@ -1557,6 +1613,7 @@ F3261CBFC169DB375A0D9492 /* FSTMockDatastore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02D20213FFC00B64F25 /* FSTMockDatastore.mm */; }; F3DEF2DB11FADAABDAA4C8BB /* bundle_builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4F5B96F3ABCD2CA901DB1CD4 /* bundle_builder.cc */; }; F3F09BC931A717CEFF4E14B9 /* FIRFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04A202154AA00B64F25 /* FIRFieldValueTests.mm */; }; + F424CBF1A655EA030F5A2FD3 /* cmake.check_cache in Resources */ = {isa = PBXBuildFile; fileRef = 1B9C7CBE95D2497D4E0B5741 /* cmake.check_cache */; }; F481368DB694B3B4D0C8E4A2 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; F4F00BF4E87D7F0F0F8831DB /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; F4FAC5A7D40A0A9A3EA77998 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; @@ -1678,6 +1735,7 @@ 0473AFFF5567E667A125347B /* ordered_code_benchmark.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = ordered_code_benchmark.cc; sourceTree = ""; }; 062072B62773A055001655D7 /* AsyncAwaitIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncAwaitIntegrationTests.swift; sourceTree = ""; }; 0840319686A223CC4AD3FAB1 /* leveldb_remote_document_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_remote_document_cache_test.cc; sourceTree = ""; }; + 086F5AEE493AF6473EF134D0 /* rules.ninja */ = {isa = PBXFileReference; includeInIndex = 1; name = rules.ninja; path = "cmake-build-debug/CMakeFiles/rules.ninja"; sourceTree = ""; }; 0D964D4936953635AC7E0834 /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json; sourceTree = ""; }; 0EE5300F8233D14025EF0456 /* string_apple_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = string_apple_test.mm; sourceTree = ""; }; 11984BA0A99D7A7ABA5B0D90 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig"; sourceTree = ""; }; @@ -1692,6 +1750,7 @@ 1A7D48A017ECB54FD381D126 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json; sourceTree = ""; }; 1A8141230C7E3986EACEF0B6 /* thread_safe_memoizer_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = thread_safe_memoizer_test.cc; sourceTree = ""; }; 1B342370EAE3AA02393E33EB /* cc_compilation_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = cc_compilation_test.cc; path = api/cc_compilation_test.cc; sourceTree = ""; }; + 1B9C7CBE95D2497D4E0B5741 /* cmake.check_cache */ = {isa = PBXFileReference; includeInIndex = 1; name = cmake.check_cache; path = "cmake-build-debug/CMakeFiles/cmake.check_cache"; sourceTree = ""; }; 1B9F95EC29FAD3F100EEC075 /* FIRAggregateQueryUnitTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRAggregateQueryUnitTests.mm; sourceTree = ""; }; 1C01D8CE367C56BB2624E299 /* index.pb.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = index.pb.h; path = admin/index.pb.h; sourceTree = ""; }; 1C3F7302BF4AE6CBC00ECDD0 /* resource.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = resource.pb.cc; sourceTree = ""; }; @@ -1701,6 +1760,8 @@ 214877F52A705012D6720CA0 /* object_value_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = object_value_test.cc; sourceTree = ""; }; 2220F583583EFC28DE792ABE /* Pods_Firestore_IntegrationTests_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2286F308EFB0534B1BDE05B9 /* memory_target_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_target_cache_test.cc; sourceTree = ""; }; + 230BC40D8C715CABFB10BBA9 /* FIRSnapshotListenerSourceTests.mm */ = {isa = PBXFileReference; includeInIndex = 1; path = FIRSnapshotListenerSourceTests.mm; sourceTree = ""; }; + 255C95F5F18BBB1E6BF77B62 /* CMakeCXXCompiler.cmake */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCXXCompiler.cmake; path = "cmake-build-debug/CMakeFiles/3.26.4/CMakeCXXCompiler.cmake"; sourceTree = ""; }; 277EAACC4DD7C21332E8496A /* lru_garbage_collector_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = lru_garbage_collector_test.cc; sourceTree = ""; }; 28B45B2104E2DAFBBF86DBB7 /* logic_utils_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = logic_utils_test.cc; sourceTree = ""; }; 29D9C76922DAC6F710BC1EF4 /* memory_document_overlay_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_document_overlay_cache_test.cc; sourceTree = ""; }; @@ -1727,6 +1788,7 @@ 397FB002E298B780F1E223E2 /* Pods-Firestore_Tests_macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_macOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_macOS/Pods-Firestore_Tests_macOS.release.xcconfig"; sourceTree = ""; }; 39B832380209CC5BAF93BC52 /* Pods_Firestore_IntegrationTests_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = remote_store_spec_test.json; sourceTree = ""; }; + 3C3B362DACE291EF501F4533 /* clion-Debug-log.txt */ = {isa = PBXFileReference; includeInIndex = 1; name = "clion-Debug-log.txt"; path = "cmake-build-debug/CMakeFiles/clion-Debug-log.txt"; sourceTree = ""; }; 3C81DE3772628FE297055662 /* Pods-Firestore_Example_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.debug.xcconfig"; sourceTree = ""; }; 3CAA33F964042646FDDAF9F9 /* status_testing.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = status_testing.cc; sourceTree = ""; }; 3D050936A2D52257FD17FB6E /* md5_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = md5_test.cc; sourceTree = ""; }; @@ -1748,6 +1810,7 @@ 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = string_apple_benchmark.mm; sourceTree = ""; }; 4D9E51DA7A275D8B1CAEAEB2 /* listen_source_spec_test.json */ = {isa = PBXFileReference; includeInIndex = 1; path = listen_source_spec_test.json; sourceTree = ""; }; 4F5B96F3ABCD2CA901DB1CD4 /* bundle_builder.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bundle_builder.cc; sourceTree = ""; }; + 50E21D4A2B87A0A80E248614 /* build.ninja */ = {isa = PBXFileReference; includeInIndex = 1; name = build.ninja; path = "cmake-build-debug/build.ninja"; sourceTree = ""; }; 526D755F65AC676234F57125 /* target_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = target_test.cc; sourceTree = ""; }; 52756B7624904C36FBB56000 /* fake_target_metadata_provider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = fake_target_metadata_provider.h; sourceTree = ""; }; 5342CDDB137B4E93E2E85CCA /* byte_string_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = byte_string_test.cc; path = nanopb/byte_string_test.cc; sourceTree = ""; }; @@ -1853,12 +1916,14 @@ 57F8EE51B5EFC9FAB185B66C /* Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json; sourceTree = ""; }; 584AE2C37A55B408541A6FF3 /* remote_event_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = remote_event_test.cc; sourceTree = ""; }; 5918805E993304321A05E82B /* Pods_Firestore_Example_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 595B30E8CC49F13B0C750D3D /* CMakeCCompiler.cmake */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCCompiler.cmake; path = "cmake-build-debug/CMakeFiles/3.26.4/CMakeCCompiler.cmake"; sourceTree = ""; }; 5B5414D28802BC76FDADABD6 /* stream_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = stream_test.cc; sourceTree = ""; }; 5B96CC29E9946508F022859C /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json; sourceTree = ""; }; 5C68EE4CB94C0DD6E333F546 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_1_01_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_1_01_membership_test_result.json; sourceTree = ""; }; 5C7942B6244F4C416B11B86C /* leveldb_mutation_queue_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_mutation_queue_test.cc; sourceTree = ""; }; 5CAE131920FFFED600BE9A4A /* Firestore_Benchmarks_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_Benchmarks_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5CAE131D20FFFED600BE9A4A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5CE650FC4B2A48B9C8BA9C88 /* CMakeCXXCompilerId.o */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCXXCompilerId.o; path = "cmake-build-debug/CMakeFiles/3.26.4/CompilerIdCXX/CMakeCXXCompilerId.o"; sourceTree = ""; }; 5E19B9B2105BA618DA9EE99C /* query_engine_test.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = query_engine_test.h; sourceTree = ""; }; 5FF903AEFA7A3284660FA4C5 /* leveldb_local_store_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_local_store_test.cc; sourceTree = ""; }; 6003F58A195388D20070C39A /* Firestore_Example_iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Firestore_Example_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1900,7 +1965,9 @@ 62E54B832A9E910A003347C8 /* IndexingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndexingTests.swift; sourceTree = ""; }; 63136A2371C0C013EC7A540C /* target_index_matcher_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = target_index_matcher_test.cc; sourceTree = ""; }; 64AA92CFA356A2360F3C5646 /* filesystem_testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = filesystem_testing.h; sourceTree = ""; }; + 658741EEE206F2C8C4159412 /* CMakeSystem.cmake */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeSystem.cmake; path = "cmake-build-debug/CMakeFiles/3.26.4/CMakeSystem.cmake"; sourceTree = ""; }; 65AF0AB593C3AD81A1F1A57E /* FIRCompositeIndexQueryTests.mm */ = {isa = PBXFileReference; includeInIndex = 1; path = FIRCompositeIndexQueryTests.mm; sourceTree = ""; }; + 6739481496019FB93579846B /* CMakeConfigureLog.yaml */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeConfigureLog.yaml; path = "cmake-build-debug/CMakeFiles/CMakeConfigureLog.yaml"; sourceTree = ""; }; 67786C62C76A740AEDBD8CD3 /* FSTTestingHooks.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = FSTTestingHooks.h; sourceTree = ""; }; 69E6C311558EC77729A16CF1 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; sourceTree = ""; }; 6A7A30A2DB3367E08939E789 /* bloom_filter.pb.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = bloom_filter.pb.h; sourceTree = ""; }; @@ -1963,12 +2030,15 @@ 9765D47FA12FA283F4EFAD02 /* memory_lru_garbage_collector_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_lru_garbage_collector_test.cc; sourceTree = ""; }; 97C492D2524E92927C11F425 /* Pods-Firestore_FuzzTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_FuzzTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS.release.xcconfig"; sourceTree = ""; }; 98366480BD1FD44A1FEDD982 /* Pods-Firestore_Example_macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_macOS/Pods-Firestore_Example_macOS.debug.xcconfig"; sourceTree = ""; }; + 987319851B67BC36AF095879 /* CMakeCCompilerId.c */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCCompilerId.c; path = "cmake-build-debug/CMakeFiles/3.26.4/CompilerIdC/CMakeCCompilerId.c"; sourceTree = ""; }; 99434327614FEFF7F7DC88EC /* counting_query_engine.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = counting_query_engine.cc; sourceTree = ""; }; 9B0B005A79E765AF02793DCE /* schedule_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = schedule_test.cc; sourceTree = ""; }; 9C1AFCC9E616EC33D6E169CF /* recovery_spec_test.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; path = recovery_spec_test.json; sourceTree = ""; }; 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = string_format_apple_test.mm; sourceTree = ""; }; A002425BC4FC4E805F4175B6 /* testing_hooks_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = testing_hooks_test.cc; sourceTree = ""; }; A082AFDD981B07B5AD78FDE8 /* token_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = token_test.cc; path = credentials/token_test.cc; sourceTree = ""; }; + A19043284553A1757B6D52DD /* CMakeCCompilerId.o */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCCompilerId.o; path = "cmake-build-debug/CMakeFiles/3.26.4/CompilerIdC/CMakeCCompilerId.o"; sourceTree = ""; }; + A1A5A15FBB6B8FBF90E12B31 /* CMakeCache.txt */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCache.txt; path = "cmake-build-debug/CMakeCache.txt"; sourceTree = ""; }; A20BAA3D2F994384279727EC /* md5_testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = md5_testing.h; sourceTree = ""; }; A2E6F09AD1EE0A6A452E9A08 /* bloom_filter_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bloom_filter_test.cc; sourceTree = ""; }; A366F6AE1A5A77548485C091 /* bundle.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bundle.pb.cc; sourceTree = ""; }; @@ -1990,10 +2060,13 @@ AB7BAB332012B519001E0872 /* geo_point_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = geo_point_test.cc; sourceTree = ""; }; ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = snapshot_version_test.cc; sourceTree = ""; }; ABF6506B201131F8005F2C74 /* timestamp_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = timestamp_test.cc; sourceTree = ""; }; + ACD733FAB49C8DDEB1F9165B /* TargetDirectories.txt */ = {isa = PBXFileReference; includeInIndex = 1; name = TargetDirectories.txt; path = "cmake-build-debug/CMakeFiles/TargetDirectories.txt"; sourceTree = ""; }; AE4A9E38D65688EE000EE2A1 /* index_manager_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = index_manager_test.cc; sourceTree = ""; }; AE89CFF09C6804573841397F /* leveldb_document_overlay_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_document_overlay_cache_test.cc; sourceTree = ""; }; AF924C79F49F793992A84879 /* aggregate_query_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = aggregate_query_test.cc; path = api/aggregate_query_test.cc; sourceTree = ""; }; + B03849DC3F7474A592D71754 /* cmake_install.cmake */ = {isa = PBXFileReference; includeInIndex = 1; name = cmake_install.cmake; path = "cmake-build-debug/cmake_install.cmake"; sourceTree = ""; }; B0520A41251254B3C24024A3 /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json; sourceTree = ""; }; + B075588AAD5B99791B537B51 /* clion-environment.txt */ = {isa = PBXFileReference; includeInIndex = 1; name = "clion-environment.txt"; path = "cmake-build-debug/CMakeFiles/clion-environment.txt"; sourceTree = ""; }; B3F5B3AAE791A5911B9EAA82 /* Pods-Firestore_Tests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS.release.xcconfig"; sourceTree = ""; }; B5C2A94EE24E60543F62CC35 /* bundle_serializer_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = bundle_serializer_test.cc; path = bundle/bundle_serializer_test.cc; sourceTree = ""; }; B5C37696557C81A6C2B7271A /* target_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = target_cache_test.cc; sourceTree = ""; }; @@ -2032,6 +2105,7 @@ BD01F0E43E4E2A07B8B05099 /* Pods-Firestore_Tests_macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_macOS/Pods-Firestore_Tests_macOS.debug.xcconfig"; sourceTree = ""; }; BF76A8DA34B5B67B4DD74666 /* field_index_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = field_index_test.cc; sourceTree = ""; }; C0C7C8977C94F9F9AFA4DB00 /* local_store_test.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = local_store_test.h; sourceTree = ""; }; + C1396907A38E6E67313C0D74 /* CMakeCXXCompilerId.cpp */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = CMakeCXXCompilerId.cpp; path = "cmake-build-debug/CMakeFiles/3.26.4/CompilerIdCXX/CMakeCXXCompilerId.cpp"; sourceTree = ""; }; C7429071B33BDF80A7FA2F8A /* view_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = view_test.cc; sourceTree = ""; }; C8522DE226C467C54E6788D8 /* mutation_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = mutation_test.cc; sourceTree = ""; }; C8582DFD74E8060C7072104B /* Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json; sourceTree = ""; }; @@ -2039,6 +2113,7 @@ C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json; sourceTree = ""; }; CB7B2D4691C380DE3EB59038 /* lru_garbage_collector_test.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = lru_garbage_collector_test.h; sourceTree = ""; }; CC572A9168BBEF7B83E4BBC5 /* view_snapshot_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = view_snapshot_test.cc; sourceTree = ""; }; + CCB165E99909ED6EE0F882D3 /* CMakeDetermineCompilerABI_C.bin */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeDetermineCompilerABI_C.bin; path = "cmake-build-debug/CMakeFiles/3.26.4/CMakeDetermineCompilerABI_C.bin"; sourceTree = ""; }; CCC9BD953F121B9E29F9AA42 /* user_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = user_test.cc; path = credentials/user_test.cc; sourceTree = ""; }; CD422AF3E4515FB8E9BE67A0 /* equals_tester.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = equals_tester.h; sourceTree = ""; }; CE37875365497FFA8687B745 /* message_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = message_test.cc; path = nanopb/message_test.cc; sourceTree = ""; }; @@ -2090,6 +2165,7 @@ F119BDDF2F06B3C0883B8297 /* firebase_app_check_credentials_provider_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; name = firebase_app_check_credentials_provider_test.mm; path = credentials/firebase_app_check_credentials_provider_test.mm; sourceTree = ""; }; F354C0FE92645B56A6C6FD44 /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS.release.xcconfig"; sourceTree = ""; }; F51859B394D01C0C507282F1 /* filesystem_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = filesystem_test.cc; sourceTree = ""; }; + F62829EC3A07509E3C54247E /* CMakeDetermineCompilerABI_CXX.bin */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeDetermineCompilerABI_CXX.bin; path = "cmake-build-debug/CMakeFiles/3.26.4/CMakeDetermineCompilerABI_CXX.bin"; sourceTree = ""; }; F694C3CE4B77B3C0FA4BBA53 /* Pods_Firestore_Benchmarks_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Benchmarks_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F6CA0C5638AB6627CB5B4CF4 /* memory_local_store_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_local_store_test.cc; sourceTree = ""; }; F7FC06E0A47D393DE1759AE1 /* bundle_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bundle_cache_test.cc; sourceTree = ""; }; @@ -2241,6 +2317,20 @@ name = admin; sourceTree = ""; }; + 475693833C55E80B2C4CA461 /* 3.26.4 */ = { + isa = PBXGroup; + children = ( + DBEB6B536E39F1EC1027C339 /* CompilerIdC */, + 69F99B85B5E15C19EFF6A21F /* CompilerIdCXX */, + 595B30E8CC49F13B0C750D3D /* CMakeCCompiler.cmake */, + 255C95F5F18BBB1E6BF77B62 /* CMakeCXXCompiler.cmake */, + CCB165E99909ED6EE0F882D3 /* CMakeDetermineCompilerABI_C.bin */, + F62829EC3A07509E3C54247E /* CMakeDetermineCompilerABI_CXX.bin */, + 658741EEE206F2C8C4159412 /* CMakeSystem.cmake */, + ); + name = 3.26.4; + sourceTree = ""; + }; 543B4F0520A91E4B001F506D /* App */ = { isa = PBXGroup; children = ( @@ -2741,6 +2831,15 @@ path = rpc; sourceTree = ""; }; + 69F99B85B5E15C19EFF6A21F /* CompilerIdCXX */ = { + isa = PBXGroup; + children = ( + C1396907A38E6E67313C0D74 /* CMakeCXXCompilerId.cpp */, + 5CE650FC4B2A48B9C8BA9C88 /* CMakeCXXCompilerId.o */, + ); + name = CompilerIdCXX; + sourceTree = ""; + }; 6EA39FDC20FE81DD008D461F /* FuzzingTargets */ = { isa = PBXGroup; children = ( @@ -2898,6 +2997,26 @@ path = macOS; sourceTree = ""; }; + DBEB6B536E39F1EC1027C339 /* CompilerIdC */ = { + isa = PBXGroup; + children = ( + 987319851B67BC36AF095879 /* CMakeCCompilerId.c */, + A19043284553A1757B6D52DD /* CMakeCCompilerId.o */, + ); + name = CompilerIdC; + sourceTree = ""; + }; + DC6A402639629C05EF451D8C /* cmake-build-debug */ = { + isa = PBXGroup; + children = ( + E3277B52449F042F11FDA06A /* CMakeFiles */, + A1A5A15FBB6B8FBF90E12B31 /* CMakeCache.txt */, + 50E21D4A2B87A0A80E248614 /* build.ninja */, + B03849DC3F7474A592D71754 /* cmake_install.cmake */, + ); + name = "cmake-build-debug"; + sourceTree = ""; + }; DE2EF06E1F3D07D7003D0CDC /* Immutable */ = { isa = PBXGroup; children = ( @@ -2994,6 +3113,7 @@ isa = PBXGroup; children = ( DE51B1BC1F0D48AC0013853F /* API */, + DC6A402639629C05EF451D8C /* cmake-build-debug */, 5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */, 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */, 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */, @@ -3018,6 +3138,7 @@ D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */, 5492E069202154D500B64F25 /* FIRQueryTests.mm */, 5492E06E202154D600B64F25 /* FIRServerTimestampTests.mm */, + 230BC40D8C715CABFB10BBA9 /* FIRSnapshotListenerSourceTests.mm */, 5492E071202154D600B64F25 /* FIRTypeTests.mm */, 5492E06D202154D600B64F25 /* FIRValidationTests.mm */, 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */, @@ -3025,6 +3146,20 @@ path = API; sourceTree = ""; }; + E3277B52449F042F11FDA06A /* CMakeFiles */ = { + isa = PBXGroup; + children = ( + 475693833C55E80B2C4CA461 /* 3.26.4 */, + 6739481496019FB93579846B /* CMakeConfigureLog.yaml */, + ACD733FAB49C8DDEB1F9165B /* TargetDirectories.txt */, + 3C3B362DACE291EF501F4533 /* clion-Debug-log.txt */, + B075588AAD5B99791B537B51 /* clion-environment.txt */, + 1B9C7CBE95D2497D4E0B5741 /* cmake.check_cache */, + 086F5AEE493AF6473EF134D0 /* rules.ninja */, + ); + name = CMakeFiles; + sourceTree = ""; + }; F7BA529161F1713BDF685C65 /* bundle */ = { isa = PBXGroup; children = ( @@ -3463,6 +3598,17 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 472D630C4885EF2A051D0236 /* CMakeCCompiler.cmake in Resources */, + E789BD9F16671F4AA48E9A52 /* CMakeCCompilerId.o in Resources */, + 6117E9BC3F39DF2175392D2D /* CMakeCXXCompiler.cmake in Resources */, + 2E3DC61BEF1F7ED477C0B216 /* CMakeCXXCompilerId.cpp in Resources */, + 7E8D4C5AF3649DCD4F736ECA /* CMakeCXXCompilerId.o in Resources */, + 630A50115EB50102D3DF359B /* CMakeCache.txt in Resources */, + 7ACBA78419279E4BAAE67234 /* CMakeConfigureLog.yaml in Resources */, + 83C76AE4AD8EA09842C5E3F0 /* CMakeDetermineCompilerABI_C.bin in Resources */, + 86A3EBF677D4C9FDE5562C5B /* CMakeDetermineCompilerABI_CXX.bin in Resources */, + D7E1009A9C7C84F6BE8804A5 /* CMakeSystem.cmake in Resources */, + 8150ED535D52D9F06922DD7A /* TargetDirectories.txt in Resources */, 8242BB61FBF44B9F5CAC35A7 /* Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json in Resources */, 2E5758FE6CFE753B04D50F89 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */, 2FAE0BCBE559ED7214AEFEB7 /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json in Resources */, @@ -3487,7 +3633,12 @@ AC44D6363F57CEAAB291ED49 /* Validation_BloomFilterTest_MD5_500_01_membership_test_result.json in Resources */, 8B2921C75DB7DD912AE14B8F /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json in Resources */, B491EF0E70DC0542644F623E /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json in Resources */, + 9066CD3ABC6357B97DF264C4 /* build.ninja in Resources */, 4B5FA86D9568ECE20C6D3AD1 /* bundle_spec_test.json in Resources */, + E86B903869E65E81DA7ED5C6 /* clion-Debug-log.txt in Resources */, + F0B0A9C420B3429807FFAF33 /* clion-environment.txt in Resources */, + AA09E52B7BB746E17C507297 /* cmake.check_cache in Resources */, + 3D916BCFBE8366532F4592D8 /* cmake_install.cmake in Resources */, 08839E1CEAAC07E350257E9D /* collection_spec_test.json in Resources */, 9C1F25177DC5753B075DCF65 /* existence_filter_spec_test.json in Resources */, 0DDCAC7C7CA55CF10AE0E809 /* garbage_collection_spec_test.json in Resources */, @@ -3504,6 +3655,7 @@ E8BA7055EDB8B03CC99A528F /* recovery_spec_test.json in Resources */, E21D819A06D9691A4B313440 /* remote_store_spec_test.json in Resources */, B43014A0517F31246419E08A /* resume_token_spec_test.json in Resources */, + D4EC6FAED8E00499D598CC55 /* rules.ninja in Resources */, 9382BE7190E7750EE7CCCE7C /* write_spec_test.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3512,6 +3664,17 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + BF624DA8E6F60FD86BC36D90 /* CMakeCCompiler.cmake in Resources */, + BACB779C724E629322DF1788 /* CMakeCCompilerId.o in Resources */, + 8B35131E57699519EF23EE7E /* CMakeCXXCompiler.cmake in Resources */, + E432BC38147553374D70AE62 /* CMakeCXXCompilerId.cpp in Resources */, + 2A08DEF81996D661D3FE48BC /* CMakeCXXCompilerId.o in Resources */, + 852C67A7F19AECE97AEDC367 /* CMakeCache.txt in Resources */, + 14664F6DD55A8ADE13524FEF /* CMakeConfigureLog.yaml in Resources */, + B55BEC44BFBC266607C9AC3A /* CMakeDetermineCompilerABI_C.bin in Resources */, + 5706E3A2894546779918907A /* CMakeDetermineCompilerABI_CXX.bin in Resources */, + D1B0B9ED583F83E027C49A9B /* CMakeSystem.cmake in Resources */, + B752A44092D47887759B3046 /* TargetDirectories.txt in Resources */, 0C9887A2F6728CB9E8A4C3CA /* Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json in Resources */, 57F0E1A1F2B614BA74961D9A /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */, F2876F16CF689FD7FFBA9DFA /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json in Resources */, @@ -3536,7 +3699,12 @@ F1F8FB9254E9A5107161A7B2 /* Validation_BloomFilterTest_MD5_500_01_membership_test_result.json in Resources */, 34B62A40BB56F9574B87B28B /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json in Resources */, C1C3369C7ECE069B76A84AD1 /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json in Resources */, + 6F4DBC1B2FAEEDDAAFC1FBD3 /* build.ninja in Resources */, 0B002E2E2012B32EB801C6D5 /* bundle_spec_test.json in Resources */, + 8D43DF691E6ABA6A35EEE059 /* clion-Debug-log.txt in Resources */, + 38159A77164B60443D556932 /* clion-environment.txt in Resources */, + 85A036436686AA6D71FDF073 /* cmake.check_cache in Resources */, + 7AF62F2F7C186028303D7AEC /* cmake_install.cmake in Resources */, 009CDC6F03AC92F3E345085E /* collection_spec_test.json in Resources */, 7AD020FC27493FF8E659436C /* existence_filter_spec_test.json in Resources */, 1BB0C34B2E8D8BCC5882430A /* garbage_collection_spec_test.json in Resources */, @@ -3553,6 +3721,7 @@ 34E866DB52AAB7DB76B69A91 /* recovery_spec_test.json in Resources */, AF81B6A91987826426F18647 /* remote_store_spec_test.json in Resources */, CC94A33318F983907E9ED509 /* resume_token_spec_test.json in Resources */, + 578B89F99DEF39417976162D /* rules.ninja in Resources */, 2DB56B6DED2C93014AE5C51A /* write_spec_test.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3647,6 +3816,17 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9BF2FDA87D5A6D44C93903C4 /* CMakeCCompiler.cmake in Resources */, + D90E6FE567D20E897A0B1201 /* CMakeCCompilerId.o in Resources */, + C0ECFC49885BFC559FDD0E8A /* CMakeCXXCompiler.cmake in Resources */, + 7F87A0D114D0C629532853D8 /* CMakeCXXCompilerId.cpp in Resources */, + BE36D35215706DAD0706F226 /* CMakeCXXCompilerId.o in Resources */, + A6AD059A06E650993296D300 /* CMakeCache.txt in Resources */, + 4C8AA63E619E23AB2A122DCA /* CMakeConfigureLog.yaml in Resources */, + 4130140DE4E5043E3E000721 /* CMakeDetermineCompilerABI_C.bin in Resources */, + 59F6CEADB513CBB5F4738161 /* CMakeDetermineCompilerABI_CXX.bin in Resources */, + 73FB09EAD9B8C22EF03A1702 /* CMakeSystem.cmake in Resources */, + 40EADA93E01E6DAF55143DD4 /* TargetDirectories.txt in Resources */, 7A7DB86955670B85B4514A1F /* Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json in Resources */, 06E0914D76667F1345EC17F5 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */, 37461AF1ACC2E64DF1709736 /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json in Resources */, @@ -3671,7 +3851,12 @@ 662E94803D6FABE56F0D22C9 /* Validation_BloomFilterTest_MD5_500_01_membership_test_result.json in Resources */, 2618255E63631038B64DF3BB /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json in Resources */, 42A98512D4C9EC6722334FE6 /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json in Resources */, + 28E7535BEFC7BB66FB58AA97 /* build.ninja in Resources */, 32030FA5B4BE6ABDFF2F974E /* bundle_spec_test.json in Resources */, + B58E1F10A44829D192B75390 /* clion-Debug-log.txt in Resources */, + DC7F5C18D65040E505E580A9 /* clion-environment.txt in Resources */, + F424CBF1A655EA030F5A2FD3 /* cmake.check_cache in Resources */, + 87AA0B64034604D5461100A8 /* cmake_install.cmake in Resources */, 46B104DEE6014D881F7ED169 /* collection_spec_test.json in Resources */, 3887E1635B31DCD7BC0922BD /* existence_filter_spec_test.json in Resources */, 47B8ED6737A24EF96B1ED318 /* garbage_collection_spec_test.json in Resources */, @@ -3688,6 +3873,7 @@ E441A53D035479C53C74A0E6 /* recovery_spec_test.json in Resources */, 022BA1619A576F6818B212C5 /* remote_store_spec_test.json in Resources */, 9EE81B1FB9B7C664B7B0A904 /* resume_token_spec_test.json in Resources */, + 26FE3A7AC99693E1029BC437 /* rules.ninja in Resources */, E6688C8E524770A3C6EBB33A /* write_spec_test.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -4531,6 +4717,7 @@ EF6C286F29E6D22200A7D4F1 /* AggregationIntegrationTests.swift in Sources */, 062072B92773A055001655D7 /* AsyncAwaitIntegrationTests.swift in Sources */, 733AFC467B600967536BD70F /* BasicCompileTests.swift in Sources */, + D871697BED043E498873CEFD /* CMakeCCompilerId.c in Sources */, 79987AF2DF1FCE799008B846 /* CodableGeoPointTests.swift in Sources */, 1C79AE3FBFC91800E30D092C /* CodableIntegrationTests.swift in Sources */, BA3C0BA8082A6FB2546E47AC /* CodableTimestampTests.swift in Sources */, @@ -4561,6 +4748,7 @@ 9A29D572C64CA1FA62F591D4 /* FIRQueryTests.mm in Sources */, 9A8B01AF6F19D248202FBC0A /* FIRQueryUnitTests.mm in Sources */, FA7837C5CDFB273DE447E447 /* FIRServerTimestampTests.mm in Sources */, + B970C9A35BDDB6B01AB0454C /* FIRSnapshotListenerSourceTests.mm in Sources */, 67BC2B77C1CC47388E79D774 /* FIRSnapshotMetadataTests.mm in Sources */, 041CF73F67F6A22BF317625A /* FIRTimestampTest.m in Sources */, 58B84B550725D9812729C7F7 /* FIRTransactionOptionsTests.mm in Sources */, @@ -4771,6 +4959,7 @@ EF6C286E29E6D22200A7D4F1 /* AggregationIntegrationTests.swift in Sources */, 062072B82773A055001655D7 /* AsyncAwaitIntegrationTests.swift in Sources */, B896E5DE1CC27347FAC009C3 /* BasicCompileTests.swift in Sources */, + A0B6EA2529C0090CF0AE7F09 /* CMakeCCompilerId.c in Sources */, 722F9A798F39F7D1FE7CF270 /* CodableGeoPointTests.swift in Sources */, CF5DE1ED21DD0A9783383A35 /* CodableIntegrationTests.swift in Sources */, 32B0739404FA588608E1F41A /* CodableTimestampTests.swift in Sources */, @@ -4801,6 +4990,7 @@ 6A4F6B42C628D55CCE0C311F /* FIRQueryTests.mm in Sources */, 298E0F8F6EB27AA36BA1CE76 /* FIRQueryUnitTests.mm in Sources */, 27E46C94AAB087C80A97FF7F /* FIRServerTimestampTests.mm in Sources */, + 05BAC479CA3AB13B7A5047E7 /* FIRSnapshotListenerSourceTests.mm in Sources */, 59F512D155DE361095A04ED4 /* FIRSnapshotMetadataTests.mm in Sources */, FE1C0263F6570DAC54A60F5C /* FIRTimestampTest.m in Sources */, 339D4DD13E1518BA79FF12EA /* FIRTransactionOptionsTests.mm in Sources */, @@ -5258,6 +5448,7 @@ EF6C286D29E6D22200A7D4F1 /* AggregationIntegrationTests.swift in Sources */, 062072B72773A055001655D7 /* AsyncAwaitIntegrationTests.swift in Sources */, F731A0CCD0220B370BC1BE8B /* BasicCompileTests.swift in Sources */, + 9366DC0A832BD2EE267BF61F /* CMakeCCompilerId.c in Sources */, 7C5E017689012489AAB7718D /* CodableGeoPointTests.swift in Sources */, 54C3242322D3B627000FE6DD /* CodableIntegrationTests.swift in Sources */, 70AB665EB6A473FF6C4CFD31 /* CodableTimestampTests.swift in Sources */, @@ -5288,6 +5479,7 @@ 5492E072202154D600B64F25 /* FIRQueryTests.mm in Sources */, 82E3634FCF4A882948B81839 /* FIRQueryUnitTests.mm in Sources */, 5492E077202154D600B64F25 /* FIRServerTimestampTests.mm in Sources */, + 3EC384D0D86A71653799DDD1 /* FIRSnapshotListenerSourceTests.mm in Sources */, 716289F99B5316B3CC5E5CE9 /* FIRSnapshotMetadataTests.mm in Sources */, 02B83EB79020AE6CBA60A410 /* FIRTimestampTest.m in Sources */, 913C2DB6951A2ED24778686C /* FIRTransactionOptionsTests.mm in Sources */, diff --git a/Firestore/Example/Tests/Integration/API/FIRBundlesTests.mm b/Firestore/Example/Tests/Integration/API/FIRBundlesTests.mm index c6870e1fec2..094d2a82b18 100644 --- a/Firestore/Example/Tests/Integration/API/FIRBundlesTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRBundlesTests.mm @@ -147,6 +147,52 @@ - (void)testLoadWithDocumentsThatAreAlreadyPulledFromBackend { [registration remove]; } +- (void)testLoadWithDocumentsWhileListeningToCache { + [self writeDocumentRef:[self.db documentWithPath:@"coll-1/a"] data:@{@"bar" : @"newValueA"}]; + [self writeDocumentRef:[self.db documentWithPath:@"coll-1/b"] data:@{@"bar" : @"newValueB"}]; + + // Finishing receiving backend event. + FIRCollectionReference* collection = [self.db collectionWithPath:@"coll-1"]; + FIRSnapshotListenOptions* options = [[FIRSnapshotListenOptions alloc] init]; + options = [options optionsWithSource:FIRListenSourceCache]; + id registration = + [collection addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; + [self.eventAccumulator awaitRemoteEvent]; + + // We should see no more snapshots from loading the bundle, because the data there is older. + [self.eventAccumulator assertNoAdditionalEvents]; + + auto bundle = [self defaultBundle]; + NSMutableArray* progresses = [[NSMutableArray alloc] init]; + __block FIRLoadBundleTaskProgress* result; + XCTestExpectation* expectation = [self expectationWithDescription:@"loading complete"]; + FIRLoadBundleTask* task = + [self.db loadBundle:[MakeNSString(bundle) dataUsingEncoding:NSUTF8StringEncoding] + completion:^(FIRLoadBundleTaskProgress* progress, NSError* error) { + result = progress; + XCTAssertNil(error); + [expectation fulfill]; + }]; + [task addObserver:^(FIRLoadBundleTaskProgress* progress) { + [progresses addObject:progress]; + }]; + + [self awaitExpectation:expectation]; + + XCTAssertEqual(4ul, progresses.count); + [self verifyProgress:progresses[0] hasLoadedDocument:0]; + [self verifyProgress:progresses[1] hasLoadedDocument:1]; + [self verifyProgress:progresses[2] hasLoadedDocument:2]; + [self verifySuccessProgress:progresses[3]]; + XCTAssertEqualObjects(progresses[3], result); + + [self verifyNamedQuery:@"limit" hasResult:@[ @{@"bar" : @"newValueB"} ]]; + [self verifyNamedQuery:@"limit-to-last" hasResult:@[ @{@"bar" : @"newValueA"} ]]; + + [registration remove]; +} + - (void)testLoadDocumentsWithProgressUpdates { NSMutableArray* progresses = [[NSMutableArray alloc] init]; auto bundle = [self defaultBundle]; diff --git a/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm b/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm index b46f05fed63..d303cca7407 100644 --- a/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm @@ -32,36 +32,589 @@ @interface FIRSnapshotListenerSourceTests : FSTIntegrationTestCase @implementation FIRSnapshotListenerSourceTests - (FIRSnapshotListenOptions *)optionsWithSourceFromCache { - FIRSnapshotListenOptions *options = [[FIRSnapshotListenOptions alloc] init]; - return [options optionsWithSource:FIRListenSourceCache]; + FIRSnapshotListenOptions *options = [[FIRSnapshotListenOptions alloc] init]; + return [options optionsWithSource:FIRListenSourceCache]; } - (FIRSnapshotListenOptions *)optionsWithSourceFromCacheAndIncludeMetadataChanges { - FIRSnapshotListenOptions *options = [[FIRSnapshotListenOptions alloc] init]; - return [[options optionsWithSource:FIRListenSourceCache] optionsWithIncludeMetadataChanges:YES]; + FIRSnapshotListenOptions *options = [[FIRSnapshotListenOptions alloc] init]; + return [[options optionsWithSource:FIRListenSourceCache] optionsWithIncludeMetadataChanges:YES]; } -- (void)canRaiseSnapshotFromCacheForQuery { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0} - }]; - - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; - - // populate the cache. - [self readDocumentSetForRef:query]; - - - FIRSnapshotListenOptions *optionsFromCache = [self optionsWithSourceFromCache]; - id registration = [query addSnapshotListenerWithOptions:optionsFromCache listener:self.eventAccumulator.valueEventHandler]; +- (void)testCanRaiseSnapshotFromCacheForQuery { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; - FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @1L}])); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - - [registration remove]; + [self readDocumentSetForRef:collRef]; // populate the cache. + + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id registration = + [query addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; + + FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + + [self.eventAccumulator assertNoAdditionalEvents]; + [registration remove]; +} + +- (void)testCanRaiseSnapshotFromCacheForDocumentReference { + FIRDocumentReference *docRef = [self documentRef]; + [docRef setData:@{@"k" : @"a", @"sort" : @0}]; + + [self readDocumentForRef:docRef]; // populate the cache. + + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id registration = + [docRef addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; + + FIRDocumentSnapshot *docSnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(docSnap.data, (@{@"k" : @"a", @"sort" : @0L})); + XCTAssertEqual(docSnap.metadata.isFromCache, YES); + + [self.eventAccumulator assertNoAdditionalEvents]; + [registration remove]; +} + +- (void)testListenToCacheShouldNotBeAffectedByOnlineStatusChange { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + + [self readDocumentSetForRef:collRef]; // populate the cache. + + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCacheAndIncludeMetadataChanges]; + id registration = + [query addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; + + FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + + [self disableNetwork]; + [self enableNetwork]; + + [self.eventAccumulator assertNoAdditionalEvents]; + [registration remove]; +} + +- (void)testMultipleListenersSourcedFromCacheCanWorkIndependently { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + + [self readDocumentSetForRef:collRef]; // populate the cache. + + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id registration1 = + [query addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; + id registration2 = + [query addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; + + FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + + // Do a local mutation + [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @1}]; + + querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), + (@[ @{@"k" : @"a", @"sort" : @0L}, @{@"k" : @"b", @"sort" : @1L} ])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), + (@[ @{@"k" : @"a", @"sort" : @0L}, @{@"k" : @"b", @"sort" : @1L} ])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + + // Detach one listener, and do a local mutation. The other listener + // should not be affected. + [registration1 remove]; + [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @2}]; + + querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects( + FIRQuerySnapshotGetData(querySnap), (@[ + @{@"k" : @"a", @"sort" : @0L}, @{@"k" : @"b", @"sort" : @1L}, @{@"k" : @"c", @"sort" : @2L} + ])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + + [self.eventAccumulator assertNoAdditionalEvents]; + [registration2 remove]; +} + +// Two queries that mapped to the same target ID are referred to as +// "mirror queries". An example for a mirror query is a limitToLast() +// query and a limit() query that share the same backend Target ID. +// Since limitToLast() queries are sent to the backend with a modified +// orderBy() clause, they can map to the same target representation as +// limit() query, even if both queries appear separate to the user. +- (void)testListenUnlistenRelistenToMirrorQueriesFromCache { + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0}, + @"b" : @{@"k" : @"b", @"sort" : @1}, + @"c" : @{@"k" : @"c", @"sort" : @1}, + }]; + + [self readDocumentSetForRef:collRef]; // populate the cache. + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + + // Setup a `limit` query. + FIRQuery *limit = [[collRef queryOrderedByField:@"sort" descending:NO] queryLimitedTo:2]; + FSTEventAccumulator *limitAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + id limitRegistration = + [limit addSnapshotListenerWithOptions:options listener:limitAccumulator.valueEventHandler]; + + // Setup a mirroring `limitToLast` query. + FIRQuery *limitToLast = [[collRef queryOrderedByField:@"sort" + descending:YES] queryLimitedToLast:2]; + FSTEventAccumulator *limitToLastAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + id limitToLastRegistration = + [limitToLast addSnapshotListenerWithOptions:options + listener:limitToLastAccumulator.valueEventHandler]; + + // Verify both queries get expected result. + FIRQuerySnapshot *snapshot = [limitAccumulator awaitEventWithName:@"Snapshot"]; + NSArray *expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"b", @"sort" : @1} ]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); + snapshot = [limitToLastAccumulator awaitEventWithName:@"Snapshot"]; + expected = @[ @{@"k" : @"b", @"sort" : @1}, @{@"k" : @"a", @"sort" : @0} ]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); + + // Unlisten then re-listen to the limit query. + [limitRegistration remove]; + limitRegistration = [limit addSnapshotListenerWithOptions:options + listener:limitAccumulator.valueEventHandler]; + snapshot = [limitAccumulator awaitEventWithName:@"Snapshot"]; + expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"b", @"sort" : @1} ]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); + XCTAssertEqual(snapshot.metadata.isFromCache, YES); + + // Add a document that would change the result set. + [self addDocumentRef:collRef data:@{@"k" : @"d", @"sort" : @-1}]; + + // Verify both queries get expected result. + snapshot = [limitAccumulator awaitEventWithName:@"Snapshot"]; + expected = @[ @{@"k" : @"d", @"sort" : @-1}, @{@"k" : @"a", @"sort" : @0} ]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); + XCTAssertEqual(snapshot.metadata.hasPendingWrites, YES); + snapshot = [limitToLastAccumulator awaitEventWithName:@"Snapshot"]; + expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"d", @"sort" : @-1} ]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); + XCTAssertEqual(snapshot.metadata.hasPendingWrites, YES); + + // Unlisten to limitToLast, update a doc, then relisten to limitToLast + [limitToLastRegistration remove]; + [self updateDocumentRef:[collRef documentWithPath:@"a"] data:@{@"k" : @"a", @"sort" : @-2}]; + limitToLastRegistration = + [limitToLast addSnapshotListenerWithOptions:options + listener:limitToLastAccumulator.valueEventHandler]; + + // Verify both queries get expected result. + snapshot = [limitAccumulator awaitEventWithName:@"Snapshot"]; + expected = @[ @{@"k" : @"a", @"sort" : @-2}, @{@"k" : @"d", @"sort" : @-1} ]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); + XCTAssertEqual(snapshot.metadata.hasPendingWrites, YES); + + snapshot = [limitToLastAccumulator awaitEventWithName:@"Snapshot"]; + expected = @[ @{@"k" : @"d", @"sort" : @-1}, @{@"k" : @"a", @"sort" : @-2} ]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); + // We listened to LimitToLast query after the doc update. + XCTAssertEqual(snapshot.metadata.hasPendingWrites, NO); +} + +- (void)testCanListenToDefaultSourceFirstAndThenCache { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + + // Listen to the query with default options, which will also populates the cache + FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + id defaultRegistration = + [query addSnapshotListener:defaultAccumulator.valueEventHandler]; + FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + + // Listen to the same query from cache + FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id cacheRegistration = + [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; + querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + // The metadata is sync with server due to the default listener + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + + [defaultAccumulator assertNoAdditionalEvents]; + [cacheAccumulator assertNoAdditionalEvents]; + [defaultRegistration remove]; + [cacheRegistration remove]; +} + +- (void)testCanListenToCacheSourceFirstAndThenDefault { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + // Listen to the cache + FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id cacheRegistration = + [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; + FIRQuerySnapshot *querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + // Cache is empty + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + + // Listen to the same query from server + FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + id defaultRegistration = + [query addSnapshotListener:defaultAccumulator.valueEventHandler]; + querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + + // Default listener updates the cache, whish triggers cache listener to raise snapshot. + querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + // The metadata is sync with server due to the default listener + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + + [defaultAccumulator assertNoAdditionalEvents]; + [cacheAccumulator assertNoAdditionalEvents]; + [defaultRegistration remove]; + [cacheRegistration remove]; +} + +- (void)testWillNotGetMetadataOnlyUpdatesIfListeningToCacheOnly { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + + [self readDocumentSetForRef:collRef]; // populate the cache. + + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCacheAndIncludeMetadataChanges]; + id registration = + [query addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; + + FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + + // Do a local mutation + [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @1}]; + + querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), + (@[ @{@"k" : @"a", @"sort" : @0L}, @{@"k" : @"b", @"sort" : @1L} ])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + XCTAssertEqual(querySnap.metadata.hasPendingWrites, YES); + + // As we are not listening to server, the listener will not get notified + // when local mutation is acknowledged by server. + [self.eventAccumulator assertNoAdditionalEvents]; + [registration remove]; } +- (void)testWillHaveSynceMetadataUpdatesWhenListeningToBothCacheAndDefaultSource { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + [self readDocumentSetForRef:collRef]; // populate the cache. + // Listen to the cache + FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCacheAndIncludeMetadataChanges]; + id cacheRegistration = + [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; + FIRQuerySnapshot *querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + + // Listen to the same query from server + FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + id defaultRegistration = + [query addSnapshotListenerWithIncludeMetadataChanges:YES + listener:defaultAccumulator.valueEventHandler]; + querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + // First snapshot will be raised from cache. + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + // Second snapshot will be raised from server result + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + + // As listening to metadata changes, the cache listener also gets triggered and synced + // with default listener. + querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + // The metadata is sync with server due to the default listener + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + + // Do a local mutation + [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @1}]; + + // snapshot gets triggered by local mutation + NSArray *expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"b", @"sort" : @1} ]; + querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); + XCTAssertEqual(querySnap.metadata.hasPendingWrites, YES); + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); + XCTAssertEqual(querySnap.metadata.hasPendingWrites, YES); + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + + // Local mutation gets acknowledged by the server + querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqual(querySnap.metadata.hasPendingWrites, NO); + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqual(querySnap.metadata.hasPendingWrites, NO); + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + + [defaultAccumulator assertNoAdditionalEvents]; + [cacheAccumulator assertNoAdditionalEvents]; + [defaultRegistration remove]; + [cacheRegistration remove]; +} + +- (void)testCanUnlistenToDefaultSourceWhileStillListeningToCache { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + + // Listen to the query with both source options + FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + id defaultRegistration = + [query addSnapshotListener:defaultAccumulator.valueEventHandler]; + [defaultAccumulator awaitEventWithName:@"Snapshot"]; + FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id cacheRegistration = + [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; + [cacheAccumulator awaitEventWithName:@"Snapshot"]; + + // Un-listen to the default listener. + [defaultRegistration remove]; + + // Add a document and verify listener to cache works as expected + [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @-1}]; + [defaultAccumulator assertNoAdditionalEvents]; + + FIRQuerySnapshot *querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), + (@[ @{@"k" : @"b", @"sort" : @-1L}, @{@"k" : @"a", @"sort" : @0L} ])); + + [cacheAccumulator assertNoAdditionalEvents]; + [cacheRegistration remove]; +} + +- (void)testCanUnlistenToCacheSourceWhileStillListeningToServer { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + + // Listen to the query with both source options + FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + id defaultRegistration = + [query addSnapshotListener:defaultAccumulator.valueEventHandler]; + [defaultAccumulator awaitEventWithName:@"Snapshot"]; + FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id cacheRegistration = + [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; + [cacheAccumulator awaitEventWithName:@"Snapshot"]; + + // Un-listen to cache. + [cacheRegistration remove]; + + // Add a document and verify listener to server works as expected. + [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @-1}]; + [cacheAccumulator assertNoAdditionalEvents]; + + FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), + (@[ @{@"k" : @"b", @"sort" : @-1L}, @{@"k" : @"a", @"sort" : @0L} ])); + + [defaultAccumulator assertNoAdditionalEvents]; + [defaultRegistration remove]; +} + +- (void)testCanListenUnlistenRelistenToSameQueryWithDifferentSourceOptions { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + + // Listen to the query with default options, which will also populates the cache + FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + id defaultRegistration = + [query addSnapshotListener:defaultAccumulator.valueEventHandler]; + FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + + // Listen to the same query from cache + FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id cacheRegistration = + [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; + querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + + // Un-listen to the default listener, add a doc and re-listen. + [defaultRegistration remove]; + [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @1}]; + NSArray *expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"b", @"sort" : @1} ]; + + querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); + + defaultRegistration = [query addSnapshotListener:defaultAccumulator.valueEventHandler]; + querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); + + // Un-listen to cache, update a doc, then re-listen to cache. + [cacheRegistration remove]; + [self updateDocumentRef:[collRef documentWithPath:@"a"] data:@{@"k" : @"a", @"sort" : @2}]; + expected = @[ @{@"k" : @"b", @"sort" : @1}, @{@"k" : @"a", @"sort" : @2} ]; + + querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); + + cacheRegistration = [query addSnapshotListenerWithOptions:options + listener:cacheAccumulator.valueEventHandler]; + querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); + + [defaultAccumulator assertNoAdditionalEvents]; + [cacheAccumulator assertNoAdditionalEvents]; + [defaultRegistration remove]; + [cacheRegistration remove]; +} + +- (void)testCanListenToCompositeIndexQueriesFromCache { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + [self readDocumentSetForRef:collRef]; // populate the cache. + + FIRQuery *query = [[collRef queryWhereField:@"k" isLessThanOrEqualTo:@"a"] queryWhereField:@"sort" + isGreaterThanOrEqualTo:@0]; + + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id registration = + [query addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; + FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + + [self.eventAccumulator assertNoAdditionalEvents]; + [registration remove]; +} + +- (void)testCanRaiseInitialSnapshotFromCachedEmptyResults { + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{}]; + + // Populate the cache with empty query result. + FIRQuerySnapshot *querySnapshot = [self readDocumentSetForRef:collRef]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnapshot), @[]); + + // Add a snapshot listener whose first event should be raised from cache. + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id registration = + [collRef addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; + + querySnapshot = [self.eventAccumulator awaitEventWithName:@"initial event"]; + XCTAssertTrue(querySnapshot.metadata.fromCache); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnapshot), @[]); + + [registration remove]; +} + +- (void)testWillNotBeTriggeredByTransactionsWhileListeningToCache { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id registration = + [collRef addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; + + FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[])); + + FIRDocumentReference *docRef = [self documentRef]; + // Use a transaction to perform a write without triggering any local events. + [docRef.firestore + runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **) { + [transaction setData:@{@"k" : @"a"} forDocument:docRef]; + return nil; + } + completion:^(id, NSError *){ + }]; + + // There should be no events raised + [self.eventAccumulator assertNoAdditionalEvents]; + [registration remove]; +} + +- (void)testSharesServerSideUpdatesWhenListeningToBothCacheAndDefault { + FIRCollectionReference *collRef = + [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + + // Listen to the query with default options, which will also populates the cache + FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + id defaultRegistration = + [query addSnapshotListener:defaultAccumulator.valueEventHandler]; + FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + + // Listen to the same query from cache + FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; + id cacheRegistration = + [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; + querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + + // Use a transaction to mock server side updates + FIRDocumentReference *docRef = [collRef documentWithAutoID]; + // Use a transaction to perform a write without triggering any local events. + [docRef.firestore + runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **) { + [transaction setData:@{@"k" : @"b", @"sort" : @1} forDocument:docRef]; + return nil; + } + completion:^(id, NSError *){ + }]; + + // Default listener receives the server update + querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + NSArray *expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"b", @"sort" : @1} ]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + + // Cache listener raises snapshot as well + querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + + [defaultAccumulator assertNoAdditionalEvents]; + [cacheAccumulator assertNoAdditionalEvents]; + [defaultRegistration remove]; + [cacheRegistration remove]; +} @end From 114496e42d03cd09a2e7d03c338e34c4b01d9d47 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 13 Feb 2024 09:57:00 -0500 Subject: [PATCH 06/17] format --- Firestore/Example/Tests/SpecTests/FSTSpecTests.mm | 2 +- Firestore/core/src/core/listen_options.h | 1 + Firestore/core/src/core/sync_engine.cc | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm index 71833017978..acec5291ae3 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm @@ -394,7 +394,7 @@ - (ListenOptions)parseOptions:(NSDictionary *)optionsSpec { if (optionsSpec != nil) { ListenSource source = [optionsSpec[@"source"] intValue] == 1 ? ListenSource::Cache : ListenSource::Default; - // TODO: extract include_metadata_changes if it exists in optionsSpec + // include_metadata_changes are default to true in spec tests options = ListenOptions::FromOptions(true, source); } diff --git a/Firestore/core/src/core/listen_options.h b/Firestore/core/src/core/listen_options.h index 98ad3609579..76177b74a9e 100644 --- a/Firestore/core/src/core/listen_options.h +++ b/Firestore/core/src/core/listen_options.h @@ -17,6 +17,7 @@ #ifndef FIRESTORE_CORE_SRC_CORE_LISTEN_OPTIONS_H_ #define FIRESTORE_CORE_SRC_CORE_LISTEN_OPTIONS_H_ +#include #include "Firestore/core/src/api/listen_source.h" namespace firebase { namespace firestore { diff --git a/Firestore/core/src/core/sync_engine.cc b/Firestore/core/src/core/sync_engine.cc index f51492dea16..c1e5a96e241 100644 --- a/Firestore/core/src/core/sync_engine.cc +++ b/Firestore/core/src/core/sync_engine.cc @@ -167,7 +167,6 @@ void SyncEngine::ListenToRemoteStore(Query query) { AssertCallbackExists("ListenToRemoteStore"); TargetData target_data = local_store_->AllocateTarget(query.ToTarget()); remote_store_->Listen(std::move(target_data)); - ; } void SyncEngine::StopListening(const Query& query, From aa3cea21c65c5eb982f11105d1c036892fa060f8 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:00:31 -0500 Subject: [PATCH 07/17] use enums --- .../Tests/Integration/API/FIRBundlesTests.mm | 46 ------------ .../SpecTests/FSTSyncEngineTestDriver.mm | 1 - .../json/listen_source_spec_test.json | 27 +------ .../SpecTests/json/listen_spec_test.json | 5 +- .../Source/API/FIRSnapshotListenOptions.mm | 4 +- Firestore/core/src/api/listen_source.h | 2 +- Firestore/core/src/core/event_manager.cc | 73 ++++++++++++++----- Firestore/core/src/core/event_manager.h | 14 ++++ 8 files changed, 76 insertions(+), 96 deletions(-) diff --git a/Firestore/Example/Tests/Integration/API/FIRBundlesTests.mm b/Firestore/Example/Tests/Integration/API/FIRBundlesTests.mm index 094d2a82b18..c6870e1fec2 100644 --- a/Firestore/Example/Tests/Integration/API/FIRBundlesTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRBundlesTests.mm @@ -147,52 +147,6 @@ - (void)testLoadWithDocumentsThatAreAlreadyPulledFromBackend { [registration remove]; } -- (void)testLoadWithDocumentsWhileListeningToCache { - [self writeDocumentRef:[self.db documentWithPath:@"coll-1/a"] data:@{@"bar" : @"newValueA"}]; - [self writeDocumentRef:[self.db documentWithPath:@"coll-1/b"] data:@{@"bar" : @"newValueB"}]; - - // Finishing receiving backend event. - FIRCollectionReference* collection = [self.db collectionWithPath:@"coll-1"]; - FIRSnapshotListenOptions* options = [[FIRSnapshotListenOptions alloc] init]; - options = [options optionsWithSource:FIRListenSourceCache]; - id registration = - [collection addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; - [self.eventAccumulator awaitRemoteEvent]; - - // We should see no more snapshots from loading the bundle, because the data there is older. - [self.eventAccumulator assertNoAdditionalEvents]; - - auto bundle = [self defaultBundle]; - NSMutableArray* progresses = [[NSMutableArray alloc] init]; - __block FIRLoadBundleTaskProgress* result; - XCTestExpectation* expectation = [self expectationWithDescription:@"loading complete"]; - FIRLoadBundleTask* task = - [self.db loadBundle:[MakeNSString(bundle) dataUsingEncoding:NSUTF8StringEncoding] - completion:^(FIRLoadBundleTaskProgress* progress, NSError* error) { - result = progress; - XCTAssertNil(error); - [expectation fulfill]; - }]; - [task addObserver:^(FIRLoadBundleTaskProgress* progress) { - [progresses addObject:progress]; - }]; - - [self awaitExpectation:expectation]; - - XCTAssertEqual(4ul, progresses.count); - [self verifyProgress:progresses[0] hasLoadedDocument:0]; - [self verifyProgress:progresses[1] hasLoadedDocument:1]; - [self verifyProgress:progresses[2] hasLoadedDocument:2]; - [self verifySuccessProgress:progresses[3]]; - XCTAssertEqualObjects(progresses[3], result); - - [self verifyNamedQuery:@"limit" hasResult:@[ @{@"bar" : @"newValueB"} ]]; - [self verifyNamedQuery:@"limit-to-last" hasResult:@[ @{@"bar" : @"newValueA"} ]]; - - [registration remove]; -} - - (void)testLoadDocumentsWithProgressUpdates { NSMutableArray* progresses = [[NSMutableArray alloc] init]; auto bundle = [self defaultBundle]; diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm index de21fdf3dc5..7fe5dc5d91e 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm @@ -477,7 +477,6 @@ - (FSTOutstandingWrite *)receiveWriteError:(int)errorCode } - (TargetId)addUserListenerWithQuery:(Query)query options:(ListenOptions)options { - // TODO(dimond): Allow customizing listen options in spec tests // TODO(dimond): Change spec tests to verify isFromCache on snapshots auto listener = QueryListener::Create( query, options, [self, query](const StatusOr &maybe_snapshot) { diff --git a/Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json b/Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json index 006e70433bd..21e67e668c2 100644 --- a/Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json +++ b/Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json @@ -586,7 +586,6 @@ ] }, "Documents are cleared when listen is removed.": { - "comment": "", "describeName": "Listens source options:", "itName": "Documents are cleared when listen is removed.", "tags": [ @@ -1135,28 +1134,6 @@ ] } } - }, - { - "watchEntity": { - "docs": [ - { - "createTime": 0, - "key": "collection/a", - "value": null, - "version": 2000 - } - ], - "targets": [ - 2 - ] - } - }, - { - "watchSnapshot": { - "targetIds": [ - ], - "version": 2000 - } } ] }, @@ -4860,9 +4837,9 @@ } ] }, - "Query is executed by primary client even if it only includes listeners sourced from cache": { + "Query is executed by primary client even if primary client only has listeners sourced from cache": { "describeName": "Listens source options:", - "itName": "Query is executed by primary client even if it only includes listeners sourced from cache", + "itName": "Query is executed by primary client even if primary client only has listeners sourced from cache", "tags": [ "multi-client" ], diff --git a/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json b/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json index 617c50f55b6..7370a0cd675 100644 --- a/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json +++ b/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json @@ -13270,10 +13270,7 @@ "describeName": "Listens:", "itName": "Secondary client advances query state with global snapshot from primary", "tags": [ - "multi-client", - "no-web", - "no-ios", - "no-android" + "multi-client" ], "config": { "numClients": 2, diff --git a/Firestore/Source/API/FIRSnapshotListenOptions.mm b/Firestore/Source/API/FIRSnapshotListenOptions.mm index a194bb8c227..c56f5a22a15 100644 --- a/Firestore/Source/API/FIRSnapshotListenOptions.mm +++ b/Firestore/Source/API/FIRSnapshotListenOptions.mm @@ -1,5 +1,5 @@ /* - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ @implementation FIRSnapshotListenOptions -// private method +// private constructor method - (instancetype)initPrivateWithSource:(FIRListenSource)source includeMetadataChanges:(BOOL)includeMetadataChanges { self = [self init]; diff --git a/Firestore/core/src/api/listen_source.h b/Firestore/core/src/api/listen_source.h index 8fbfd92afbf..44387f77128 100644 --- a/Firestore/core/src/api/listen_source.h +++ b/Firestore/core/src/api/listen_source.h @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google + * Copyright 2024 Google * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/Firestore/core/src/core/event_manager.cc b/Firestore/core/src/core/event_manager.cc index 7db9f19935b..3e1d419a349 100644 --- a/Firestore/core/src/core/event_manager.cc +++ b/Firestore/core/src/core/event_manager.cc @@ -37,12 +37,24 @@ EventManager::EventManager(QueryEventSource* query_event_source) model::TargetId EventManager::AddQueryListener( std::shared_ptr listener) { const Query& query = listener->query(); + ListenerSetupAction listenerAction = + ListenerSetupAction::NoSetupActionRequired; auto inserted = queries_.emplace(query, QueryListenersInfo{}); bool first_listen = inserted.second; QueryListenersInfo& query_info = inserted.first->second; - bool first_listen_to_remote_store = - !query_info.has_remote_listeners() && listener->listens_to_remote_store(); + + if (first_listen) { + listenerAction = listener->listens_to_remote_store() + ? ListenerSetupAction:: + InitializeLocalListenAndRequireWatchConnection + : ListenerSetupAction::InitializeLocalListenOnly; + } else if (!query_info.has_remote_listeners() && + listener->listens_to_remote_store()) { + // Query has been listening to local cache, and tries to add a new listener + // sourced from watch. + listenerAction = ListenerSetupAction::RequireWatchConnectionOnly; + } query_info.listeners.push_back(listener); @@ -58,11 +70,20 @@ model::TargetId EventManager::AddQueryListener( } } - if (first_listen) { - query_info.target_id = - query_event_source_->Listen(query, first_listen_to_remote_store); - } else if (first_listen_to_remote_store) { - query_event_source_->ListenToRemoteStore(query); + switch (listenerAction) { + case ListenerSetupAction::InitializeLocalListenAndRequireWatchConnection: + query_info.target_id = + query_event_source_->Listen(query, /** enableRemoteListen= */ true); + break; + case ListenerSetupAction::InitializeLocalListenOnly: + query_info.target_id = + query_event_source_->Listen(query, /** enableRemoteListen= */ false); + break; + case ListenerSetupAction::RequireWatchConnectionOnly: + query_event_source_->ListenToRemoteStore(query); + break; + default: + break; } return query_info.target_id; } @@ -70,23 +91,41 @@ model::TargetId EventManager::AddQueryListener( void EventManager::RemoveQueryListener( std::shared_ptr listener) { const Query& query = listener->query(); - bool last_listen = false; - bool last_listen_to_remote_store = false; + ListenerRemovalAction listenerAction = + ListenerRemovalAction::NoRemovalActionRequired; auto found_iter = queries_.find(query); if (found_iter != queries_.end()) { QueryListenersInfo& query_info = found_iter->second; query_info.Erase(listener); - last_listen = query_info.listeners.empty(); - last_listen_to_remote_store = !query_info.has_remote_listeners() && - listener->listens_to_remote_store(); + + if (query_info.listeners.empty()) { + listenerAction = + listener->listens_to_remote_store() + ? ListenerRemovalAction:: + TerminateLocalListenAndRequireWatchDisconnection + : ListenerRemovalAction::TerminateLocalListenOnly; + } else if (!query_info.has_remote_listeners() && + listener->listens_to_remote_store()) { + // The removed listener is the last one that sourced from watch. + listenerAction = ListenerRemovalAction::RequireWatchDisconnectionOnly; + } } - if (last_listen) { - queries_.erase(found_iter); - query_event_source_->StopListening(query, last_listen_to_remote_store); - } else if (last_listen_to_remote_store) { - query_event_source_->StopListeningToRemoteStore(query); + switch (listenerAction) { + case ListenerRemovalAction:: + TerminateLocalListenAndRequireWatchDisconnection: + queries_.erase(found_iter); + return query_event_source_->StopListening( + query, /** disableRemoteListen= */ true); + case ListenerRemovalAction::TerminateLocalListenOnly: + queries_.erase(found_iter); + return query_event_source_->StopListening( + query, /** disableRemoteListen= */ true); + case ListenerRemovalAction::RequireWatchDisconnectionOnly: + return query_event_source_->StopListeningToRemoteStore(query); + default: + return; } } diff --git a/Firestore/core/src/core/event_manager.h b/Firestore/core/src/core/event_manager.h index ea521976bca..9ee783a85bd 100644 --- a/Firestore/core/src/core/event_manager.h +++ b/Firestore/core/src/core/event_manager.h @@ -112,6 +112,20 @@ class EventManager : public SyncEngineCallback { absl::optional snapshot_; }; + enum ListenerSetupAction { + InitializeLocalListenAndRequireWatchConnection = 0, + InitializeLocalListenOnly = 1, + RequireWatchConnectionOnly = 2, + NoSetupActionRequired = 3 + }; + + enum ListenerRemovalAction { + TerminateLocalListenAndRequireWatchDisconnection = 0, + TerminateLocalListenOnly = 1, + RequireWatchDisconnectionOnly = 2, + NoRemovalActionRequired = 3 + }; + QueryEventSource* query_event_source_ = nullptr; model::OnlineState online_state_ = model::OnlineState::Unknown; std::unordered_map queries_; From 753a9fa44eee7d4b3f45e2fd5a7989f2c42a0c07 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 15 Feb 2024 14:38:45 -0500 Subject: [PATCH 08/17] update integration tests --- .../Firestore.xcodeproj/project.pbxproj | 144 ------------- .../API/FIRSnapshotListenerSourceTests.mm | 197 ++++++++++-------- Firestore/core/src/core/event_manager.cc | 5 +- 3 files changed, 113 insertions(+), 233 deletions(-) diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 3c84debef5d..b7a33bfff9e 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -144,7 +144,6 @@ 13ED75EFC2F6917951518A4B /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3D050936A2D52257FD17FB6E /* md5_test.cc */; }; 143FBD21E02C709E3E6E8993 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */; }; 1465E362F7BA7A3D063E61C7 /* database_id_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB71064B201FA60300344F18 /* database_id_test.cc */; }; - 14664F6DD55A8ADE13524FEF /* CMakeConfigureLog.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 6739481496019FB93579846B /* CMakeConfigureLog.yaml */; }; 146C140B254F3837A4DD7AE8 /* bits_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D01201BC69F00D97691 /* bits_test.cc */; }; 152543FD706D5E8851C8DA92 /* precondition_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */; }; 153DBBCAF6D4FFA8ABC2EBDF /* leveldb_query_engine_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DB1F1E1B1ED15E8D042144B1 /* leveldb_query_engine_test.cc */; }; @@ -272,7 +271,6 @@ 26C4E52128C8E7B5B96BECC4 /* defer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8ABAC2E0402213D837F73DC3 /* defer_test.cc */; }; 26C577D159CFFD73E24D543C /* memory_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 74FBEFA4FE4B12C435011763 /* memory_mutation_queue_test.cc */; }; 26CB3D7C871BC56456C6021E /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; }; - 26FE3A7AC99693E1029BC437 /* rules.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 086F5AEE493AF6473EF134D0 /* rules.ninja */; }; 276A563D546698B6AAC20164 /* annotations.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */; }; 27AF4C4BAFE079892D4F5341 /* Validation_BloomFilterTest_MD5_50000_1_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 4B3E4A77493524333133C5DC /* Validation_BloomFilterTest_MD5_50000_1_bloom_filter_proto.json */; }; 27E46C94AAB087C80A97FF7F /* FIRServerTimestampTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06E202154D600B64F25 /* FIRServerTimestampTests.mm */; }; @@ -281,13 +279,11 @@ 284A5280F868B2B4B5A1C848 /* leveldb_target_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = E76F0CDF28E5FA62D21DE648 /* leveldb_target_cache_test.cc */; }; 28691225046DF9DF181B3350 /* ordered_code_benchmark.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0473AFFF5567E667A125347B /* ordered_code_benchmark.cc */; }; 28E4B4A53A739AE2C9CF4159 /* FIRDocumentSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04B202154AA00B64F25 /* FIRDocumentSnapshotTests.mm */; }; - 28E7535BEFC7BB66FB58AA97 /* build.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 50E21D4A2B87A0A80E248614 /* build.ninja */; }; 29243A4BBB2E2B1530A62C59 /* leveldb_transaction_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 88CF09277CFA45EE1273E3BA /* leveldb_transaction_test.cc */; }; 292BCC76AF1B916752764A8F /* leveldb_bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8E9CD82E60893DDD7757B798 /* leveldb_bundle_cache_test.cc */; }; 297DC2B3C1EB136D58F4BA9C /* byte_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5342CDDB137B4E93E2E85CCA /* byte_string_test.cc */; }; 298E0F8F6EB27AA36BA1CE76 /* FIRQueryUnitTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = FF73B39D04D1760190E6B84A /* FIRQueryUnitTests.mm */; }; 29954A3172DDFE5133D91E24 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; - 2A08DEF81996D661D3FE48BC /* CMakeCXXCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = 5CE650FC4B2A48B9C8BA9C88 /* CMakeCXXCompilerId.o */; }; 2A0925323776AD50C1105BC0 /* counting_query_engine.cc in Sources */ = {isa = PBXBuildFile; fileRef = 99434327614FEFF7F7DC88EC /* counting_query_engine.cc */; }; 2A365DB6DF32631964FE690A /* stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5B5414D28802BC76FDADABD6 /* stream_test.cc */; }; 2A499CFB2831612A045977CD /* message_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CE37875365497FFA8687B745 /* message_test.cc */; }; @@ -313,7 +309,6 @@ 2E0BBA7E627EB240BA11B0D0 /* exponential_backoff_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D1B68420E2AB1A00B35856 /* exponential_backoff_test.cc */; }; 2E169CF1E9E499F054BB873A /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; 2E373EA9D5FF8C6DE2507675 /* field_index_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BF76A8DA34B5B67B4DD74666 /* field_index_test.cc */; }; - 2E3DC61BEF1F7ED477C0B216 /* CMakeCXXCompilerId.cpp in Resources */ = {isa = PBXBuildFile; fileRef = C1396907A38E6E67313C0D74 /* CMakeCXXCompilerId.cpp */; }; 2E5758FE6CFE753B04D50F89 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */; }; 2E76BC76BBCE5FCDDCF5EEBE /* leveldb_bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8E9CD82E60893DDD7757B798 /* leveldb_bundle_cache_test.cc */; }; 2E7CAC076447970DE881E703 /* aggregate_query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AF924C79F49F793992A84879 /* aggregate_query_test.cc */; }; @@ -373,7 +368,6 @@ 37EC6C6EA9169BB99078CA96 /* reference_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 132E32997D781B896672D30A /* reference_set_test.cc */; }; 380A137B785A5A6991BEDF4B /* leveldb_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5FF903AEFA7A3284660FA4C5 /* leveldb_local_store_test.cc */; }; 380E543B7BC6F648BBB250B4 /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3D050936A2D52257FD17FB6E /* md5_test.cc */; }; - 38159A77164B60443D556932 /* clion-environment.txt in Resources */ = {isa = PBXBuildFile; fileRef = B075588AAD5B99791B537B51 /* clion-environment.txt */; }; 38208AC761FF994BA69822BE /* async_queue_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */; }; 3887E1635B31DCD7BC0922BD /* existence_filter_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */; }; 38C37F0CE0AB18F1AAE6E67C /* FSTExceptionCatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B8BFD9B37D1029D238BDD71E /* FSTExceptionCatcher.m */; }; @@ -400,7 +394,6 @@ 3C5D441E7D5C140F0FB14D91 /* bloom_filter_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A2E6F09AD1EE0A6A452E9A08 /* bloom_filter_test.cc */; }; 3CFFA6F016231446367E3A69 /* listen_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A01F315EE100DD57A1 /* listen_spec_test.json */; }; 3D22F56C0DE7C7256C75DC06 /* tree_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4D20A36DBB00BCEB75 /* tree_sorted_map_test.cc */; }; - 3D916BCFBE8366532F4592D8 /* cmake_install.cmake in Resources */ = {isa = PBXBuildFile; fileRef = B03849DC3F7474A592D71754 /* cmake_install.cmake */; }; 3D9619906F09108E34FF0C95 /* FSTSmokeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */; }; 3DBB48F077C97200F32B51A0 /* value_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 40F9D09063A07F710811A84F /* value_util_test.cc */; }; 3DBBC644BE08B140BCC23BD5 /* string_apple_benchmark.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */; }; @@ -418,9 +411,7 @@ 40431BF2A368D0C891229F6E /* FSTMemorySpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */; }; 409B29C81132718B36BF2497 /* Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C8582DFD74E8060C7072104B /* Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json */; }; 409C0F2BFC2E1BECFFAC4D32 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; - 40EADA93E01E6DAF55143DD4 /* TargetDirectories.txt in Resources */ = {isa = PBXBuildFile; fileRef = ACD733FAB49C8DDEB1F9165B /* TargetDirectories.txt */; }; 412BE974741729A6683C386F /* aggregate_query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AF924C79F49F793992A84879 /* aggregate_query_test.cc */; }; - 4130140DE4E5043E3E000721 /* CMakeDetermineCompilerABI_C.bin in Resources */ = {isa = PBXBuildFile; fileRef = CCB165E99909ED6EE0F882D3 /* CMakeDetermineCompilerABI_C.bin */; }; 4173B61CB74EB4CD1D89EE68 /* latlng.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9220B89AAC00B5BCE7 /* latlng.pb.cc */; }; 4194B7BB8B0352E1AC5D69B9 /* precondition_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */; }; 41C1C67BD1A10F2A8D1F5316 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BD051DBE754950FEAC7A446 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json */; }; @@ -449,7 +440,6 @@ 46EAC2828CD942F27834F497 /* persistence_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9113B6F513D0473AEABBAF1F /* persistence_testing.cc */; }; 470A37727BBF516B05ED276A /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; 47136EEB53CF80D7C8436F38 /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = B0520A41251254B3C24024A3 /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json */; }; - 472D630C4885EF2A051D0236 /* CMakeCCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 595B30E8CC49F13B0C750D3D /* CMakeCCompiler.cmake */; }; 4747A986288114C2B7CD179E /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; 474DF520B9859479845C8A4D /* bundle_builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4F5B96F3ABCD2CA901DB1CD4 /* bundle_builder.cc */; }; 475FE2D34C6555A54D77A054 /* empty_credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8FA60B08D59FEA0D6751E87F /* empty_credentials_provider_test.cc */; }; @@ -485,7 +475,6 @@ 4C4D780CA9367DBA324D97FF /* load_bundle_task_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8F1A7B4158D9DD76EE4836BF /* load_bundle_task_test.cc */; }; 4C5292BF643BF14FA2AC5DB1 /* settings_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD12BC1DB2480886D2FB0005 /* settings_test.cc */; }; 4C66806697D7BCA730FA3697 /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D221C2DDC800EFB9CC /* common.pb.cc */; }; - 4C8AA63E619E23AB2A122DCA /* CMakeConfigureLog.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 6739481496019FB93579846B /* CMakeConfigureLog.yaml */; }; 4CC78CA0E9E03F5DCF13FEBD /* Pods_Firestore_Tests_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7DF4A6F740086A2D8C0E28E /* Pods_Firestore_Tests_tvOS.framework */; }; 4CDFF1AE3D639AA89C5C4411 /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; 4D1775B7916D4CDAD1BF1876 /* bundle.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = A366F6AE1A5A77548485C091 /* bundle.pb.cc */; }; @@ -657,11 +646,9 @@ 55E84644D385A70E607A0F91 /* leveldb_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5FF903AEFA7A3284660FA4C5 /* leveldb_local_store_test.cc */; }; 568EC1C0F68A7B95E57C8C6C /* leveldb_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */; }; 56D85436D3C864B804851B15 /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; - 5706E3A2894546779918907A /* CMakeDetermineCompilerABI_CXX.bin in Resources */ = {isa = PBXBuildFile; fileRef = F62829EC3A07509E3C54247E /* CMakeDetermineCompilerABI_CXX.bin */; }; 57171BD004A1691B19A76453 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */; }; 5778E5F1FABEFA450B8CF4BC /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = B0520A41251254B3C24024A3 /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json */; }; 5785C7190F8F8DDE879F16B7 /* Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 478DC75A0DCA6249A616DD30 /* Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json */; }; - 578B89F99DEF39417976162D /* rules.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 086F5AEE493AF6473EF134D0 /* rules.ninja */; }; 57F0E1A1F2B614BA74961D9A /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */; }; 583DF65751B7BBD0A222CAB4 /* byte_stream_cpp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 01D10113ECC5B446DB35E96D /* byte_stream_cpp_test.cc */; }; 58693C153EC597BC25EE9648 /* firebase_auth_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F869D85E900E5AF6CD02E2FC /* firebase_auth_credentials_provider_test.mm */; }; @@ -675,7 +662,6 @@ 59E95B64C460C860E2BC7464 /* load_bundle_task_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8F1A7B4158D9DD76EE4836BF /* load_bundle_task_test.cc */; }; 59F3A9669E0AF835E62D6674 /* Validation_BloomFilterTest_MD5_1_1_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 3FDD0050CA08C8302400C5FB /* Validation_BloomFilterTest_MD5_1_1_bloom_filter_proto.json */; }; 59F512D155DE361095A04ED4 /* FIRSnapshotMetadataTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04D202154AA00B64F25 /* FIRSnapshotMetadataTests.mm */; }; - 59F6CEADB513CBB5F4738161 /* CMakeDetermineCompilerABI_CXX.bin in Resources */ = {isa = PBXBuildFile; fileRef = F62829EC3A07509E3C54247E /* CMakeDetermineCompilerABI_CXX.bin */; }; 5A080105CCBFDB6BF3F3772D /* path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 403DBF6EFB541DFD01582AA3 /* path_test.cc */; }; 5A44725457D6B7805FD66EEB /* bundle_loader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A853C81A6A5A51C9D0389EDA /* bundle_loader_test.cc */; }; 5ACF26A3B0A33784CC525FB0 /* aggregate_query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AF924C79F49F793992A84879 /* aggregate_query_test.cc */; }; @@ -736,7 +722,6 @@ 60985657831B8DDE2C65AC8B /* FIRFieldsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */; }; 60C72F86D2231B1B6592A5E6 /* filesystem_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F51859B394D01C0C507282F1 /* filesystem_test.cc */; }; 6105A1365831B79A7DEEA4F3 /* path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 403DBF6EFB541DFD01582AA3 /* path_test.cc */; }; - 6117E9BC3F39DF2175392D2D /* CMakeCXXCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 255C95F5F18BBB1E6BF77B62 /* CMakeCXXCompiler.cmake */; }; 6141D3FDF5728FCE9CC1DBFA /* bundle_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 79EAA9F7B1B9592B5F053923 /* bundle_spec_test.json */; }; 6156C6A837D78D49ED8B8812 /* index_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 8C7278B604B8799F074F4E8C /* index_spec_test.json */; }; 6161B5032047140C00A99DBB /* FIRFirestoreSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */; }; @@ -765,7 +750,6 @@ 62EC5F7FB416BA124A2B4604 /* byte_stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 432C71959255C5DBDF522F52 /* byte_stream_test.cc */; }; 62F86BBE7DDA5B295B57C8DA /* string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0EE5300F8233D14025EF0456 /* string_apple_test.mm */; }; 6300709ECDE8E0B5A8645F8D /* time_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5497CB76229DECDE000FB92F /* time_testing.cc */; }; - 630A50115EB50102D3DF359B /* CMakeCache.txt in Resources */ = {isa = PBXBuildFile; fileRef = A1A5A15FBB6B8FBF90E12B31 /* CMakeCache.txt */; }; 6325D0E43A402BC5866C9C0E /* defer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8ABAC2E0402213D837F73DC3 /* defer_test.cc */; }; 6359EA7D5C76D462BD31B5E5 /* watch_change_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2D7472BC70C024D736FF74D9 /* watch_change_test.cc */; }; 6380CACCF96A9B26900983DC /* leveldb_target_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = E76F0CDF28E5FA62D21DE648 /* leveldb_target_cache_test.cc */; }; @@ -832,7 +816,6 @@ 6F256C06FCBA46378EC35D72 /* leveldb_overlay_migration_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D8A6D52723B1BABE1B7B8D8F /* leveldb_overlay_migration_manager_test.cc */; }; 6F3CAC76D918D6B0917EDF92 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; 6F45846C159D3C063DBD3CBE /* FirestoreEncoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1235769422B86E65007DDFA9 /* FirestoreEncoderTests.swift */; }; - 6F4DBC1B2FAEEDDAAFC1FBD3 /* build.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 50E21D4A2B87A0A80E248614 /* build.ninja */; }; 6F511ABFD023AEB81F92DB12 /* maybe_document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7E20B89AAC00B5BCE7 /* maybe_document.pb.cc */; }; 6F67601562343B63B8996F7A /* FSTTestingHooks.mm in Sources */ = {isa = PBXBuildFile; fileRef = D85AC18C55650ED230A71B82 /* FSTTestingHooks.mm */; }; 6F914209F46E6552B5A79570 /* async_queue_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */; }; @@ -863,7 +846,6 @@ 73866AA12082B0A5009BB4FF /* FIRArrayTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */; }; 7394B5C29C6E524C2AF964E6 /* counting_query_engine.cc in Sources */ = {isa = PBXBuildFile; fileRef = 99434327614FEFF7F7DC88EC /* counting_query_engine.cc */; }; 73E42D984FB36173A2BDA57C /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; - 73FB09EAD9B8C22EF03A1702 /* CMakeSystem.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 658741EEE206F2C8C4159412 /* CMakeSystem.cmake */; }; 73FE5066020EF9B2892C86BF /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; }; 743DF2DF38CE289F13F44043 /* status_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3CAA33F964042646FDDAF9F9 /* status_testing.cc */; }; 7495E3BAE536CD839EE20F31 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; @@ -898,9 +880,7 @@ 7A8DF35E7DB4278E67E6BDB3 /* snapshot_version_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */; }; 7AA8771FE1F048D012E5E317 /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; 7ACA8D967438B5CD9DA4C884 /* memory_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F6CA0C5638AB6627CB5B4CF4 /* memory_local_store_test.cc */; }; - 7ACBA78419279E4BAAE67234 /* CMakeConfigureLog.yaml in Resources */ = {isa = PBXBuildFile; fileRef = 6739481496019FB93579846B /* CMakeConfigureLog.yaml */; }; 7AD020FC27493FF8E659436C /* existence_filter_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */; }; - 7AF62F2F7C186028303D7AEC /* cmake_install.cmake in Resources */ = {isa = PBXBuildFile; fileRef = B03849DC3F7474A592D71754 /* cmake_install.cmake */; }; 7B0EA399F899537ACCC84E53 /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; 7B0F073BDB6D0D6E542E23D4 /* query.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D621C2DDC800EFB9CC /* query.pb.cc */; }; 7B74447D211586D9D1CC82BB /* datastore_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3167BD972EFF8EC636530E59 /* datastore_test.cc */; }; @@ -924,7 +904,6 @@ 7DED491019248CE9B9E9EB50 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; 7E1B1335B2EC566FB25B710C /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C8FB22BCB9F454DA44BA80C8 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json */; }; 7E82D412BB56728BEBB7EF46 /* bundle_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B5C2A94EE24E60543F62CC35 /* bundle_serializer_test.cc */; }; - 7E8D4C5AF3649DCD4F736ECA /* CMakeCXXCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = 5CE650FC4B2A48B9C8BA9C88 /* CMakeCXXCompilerId.o */; }; 7E97B0F04E25610FF37E9259 /* memory_target_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2286F308EFB0534B1BDE05B9 /* memory_target_cache_test.cc */; }; 7EAB3129A58368EE4BD449ED /* leveldb_migrations_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = EF83ACD5E1E9F25845A9ACED /* leveldb_migrations_test.cc */; }; 7EF540911720DAAF516BEDF0 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; @@ -932,7 +911,6 @@ 7F5501F917A11DE4E11F5CC7 /* Validation_BloomFilterTest_MD5_50000_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 3841925AA60E13A027F565E6 /* Validation_BloomFilterTest_MD5_50000_1_membership_test_result.json */; }; 7F6199159E24E19E2A3F5601 /* schedule_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B0B005A79E765AF02793DCE /* schedule_test.cc */; }; 7F771EB980D9CFAAB4764233 /* view_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = A5466E7809AD2871FFDE6C76 /* view_testing.cc */; }; - 7F87A0D114D0C629532853D8 /* CMakeCXXCompilerId.cpp in Resources */ = {isa = PBXBuildFile; fileRef = C1396907A38E6E67313C0D74 /* CMakeCXXCompilerId.cpp */; }; 7F9CE96304D413F7E7AA0DA0 /* memory_target_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2286F308EFB0534B1BDE05B9 /* memory_target_cache_test.cc */; }; 7FF39B8BD834F8267BDCBCC6 /* status_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5493A423225F9990006DE7BA /* status_apple_test.mm */; }; 804B0C6CCE3933CF3948F249 /* grpc_streaming_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964922154AB8F00EB9CFB /* grpc_streaming_reader_test.cc */; }; @@ -941,7 +919,6 @@ 80D7FEBB1056E489F24C6C8F /* firebase_app_check_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F119BDDF2F06B3C0883B8297 /* firebase_app_check_credentials_provider_test.mm */; }; 80D8B7D6FFFEA12AF10E4E2B /* leveldb_overlay_migration_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D8A6D52723B1BABE1B7B8D8F /* leveldb_overlay_migration_manager_test.cc */; }; 814724DE70EFC3DDF439CD78 /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; - 8150ED535D52D9F06922DD7A /* TargetDirectories.txt in Resources */ = {isa = PBXBuildFile; fileRef = ACD733FAB49C8DDEB1F9165B /* TargetDirectories.txt */; }; 816E8E62DC163649BA96951C /* EncodableFieldValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1235769122B7E915007DDFA9 /* EncodableFieldValueTests.swift */; }; 81A6B241E63540900F205817 /* view_snapshot_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CC572A9168BBEF7B83E4BBC5 /* view_snapshot_test.cc */; }; 81AD038D81C1A8C2074B98B1 /* Validation_BloomFilterTest_MD5_5000_0001_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 728F617782600536F2561463 /* Validation_BloomFilterTest_MD5_5000_0001_bloom_filter_proto.json */; }; @@ -958,7 +935,6 @@ 8388418F43042605FB9BFB92 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; 839D8B502026706419FE09D6 /* leveldb_index_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 166CE73C03AB4366AAC5201C /* leveldb_index_manager_test.cc */; }; 83A9CD3B6E791A860CE81FA1 /* async_queue_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */; }; - 83C76AE4AD8EA09842C5E3F0 /* CMakeDetermineCompilerABI_C.bin in Resources */ = {isa = PBXBuildFile; fileRef = CCB165E99909ED6EE0F882D3 /* CMakeDetermineCompilerABI_C.bin */; }; 8403D519C916C72B9C7F2FA1 /* FIRValidationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06D202154D600B64F25 /* FIRValidationTests.mm */; }; 8405FF2BFBB233031A887398 /* event_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6F57521E161450FAF89075ED /* event_manager_test.cc */; }; 8413BD9958F6DD52C466D70F /* sorted_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */; }; @@ -967,9 +943,7 @@ 8460C97C9209D7DAF07090BD /* FIRFieldsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */; }; 84E75527F3739131C09BEAA5 /* target_index_matcher_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 63136A2371C0C013EC7A540C /* target_index_matcher_test.cc */; }; 851346D66DEC223E839E3AA9 /* memory_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 74FBEFA4FE4B12C435011763 /* memory_mutation_queue_test.cc */; }; - 852C67A7F19AECE97AEDC367 /* CMakeCache.txt in Resources */ = {isa = PBXBuildFile; fileRef = A1A5A15FBB6B8FBF90E12B31 /* CMakeCache.txt */; }; 856A1EAAD674ADBDAAEDAC37 /* bundle_builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4F5B96F3ABCD2CA901DB1CD4 /* bundle_builder.cc */; }; - 85A036436686AA6D71FDF073 /* cmake.check_cache in Resources */ = {isa = PBXBuildFile; fileRef = 1B9C7CBE95D2497D4E0B5741 /* cmake.check_cache */; }; 85A33A9CE33207C2333DDD32 /* FIRTransactionOptionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CF39ECA1293D21A0A2AB2626 /* FIRTransactionOptionsTests.mm */; }; 85B8918FC8C5DC62482E39C3 /* resource_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2B02024FFD70028D6BE /* resource_path_test.cc */; }; 85BC2AB572A400114BF59255 /* limbo_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */; }; @@ -982,12 +956,10 @@ 86494278BE08F10A8AAF9603 /* iterator_adaptors_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */; }; 867B370BF2DF84B6AB94B874 /* filesystem_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA02DA2FCD0001CFC6EB08DA /* filesystem_testing.cc */; }; 8683BBC3AC7B01937606A83B /* firestore.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D421C2DDC800EFB9CC /* firestore.pb.cc */; }; - 86A3EBF677D4C9FDE5562C5B /* CMakeDetermineCompilerABI_CXX.bin in Resources */ = {isa = PBXBuildFile; fileRef = F62829EC3A07509E3C54247E /* CMakeDetermineCompilerABI_CXX.bin */; }; 86B413EC49E3BBBEBF1FB7A0 /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AB49283E544497A9C5A0E59 /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json */; }; 86E6FC2B7657C35B342E1436 /* sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */; }; 8705C4856498F66E471A0997 /* FIRWriteBatchTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; - 87AA0B64034604D5461100A8 /* cmake_install.cmake in Resources */ = {isa = PBXBuildFile; fileRef = B03849DC3F7474A592D71754 /* cmake_install.cmake */; }; 87B5972F1C67CB8D53ADA024 /* object_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 214877F52A705012D6720CA0 /* object_value_test.cc */; }; 87B5AC3EBF0E83166B142FA4 /* string_apple_benchmark.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */; }; 881E55152AB34465412F8542 /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; @@ -1005,13 +977,11 @@ 8B0EC945E74A03BD3ED8F9AA /* status_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3CAA33F964042646FDDAF9F9 /* status_testing.cc */; }; 8B2921C75DB7DD912AE14B8F /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = D8E530B27D5641B9C26A452C /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json */; }; 8B31F63673F3B5238DE95AFB /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; }; - 8B35131E57699519EF23EE7E /* CMakeCXXCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 255C95F5F18BBB1E6BF77B62 /* CMakeCXXCompiler.cmake */; }; 8B3EB33933D11CF897EAF4C3 /* leveldb_index_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 166CE73C03AB4366AAC5201C /* leveldb_index_manager_test.cc */; }; 8C39F6D4B3AA9074DF00CFB8 /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CFC201A2EE200D97691 /* string_util_test.cc */; }; 8C602DAD4E8296AB5EFB962A /* firestore.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D421C2DDC800EFB9CC /* firestore.pb.cc */; }; 8C82D4D3F9AB63E79CC52DC8 /* Pods_Firestore_IntegrationTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */; }; 8D0EF43F1B7B156550E65C20 /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; }; - 8D43DF691E6ABA6A35EEE059 /* clion-Debug-log.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3C3B362DACE291EF501F4533 /* clion-Debug-log.txt */; }; 8DBA8DC55722ED9D3A1BB2C9 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 1A7D48A017ECB54FD381D126 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json */; }; 8E103A426D6E650DC338F281 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C8FB22BCB9F454DA44BA80C8 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json */; }; 8E41D53C77C30372840B0367 /* Validation_BloomFilterTest_MD5_5000_0001_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 728F617782600536F2561463 /* Validation_BloomFilterTest_MD5_5000_0001_bloom_filter_proto.json */; }; @@ -1024,7 +994,6 @@ 900D0E9F18CE3DB954DD0D1E /* async_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB467B208E9A8200554BA2 /* async_queue_test.cc */; }; 9012B0E121B99B9C7E54160B /* query_engine_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B8A853940305237AFDA8050B /* query_engine_test.cc */; }; 9016EF298E41456060578C90 /* field_transform_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7515B47C92ABEEC66864B55C /* field_transform_test.cc */; }; - 9066CD3ABC6357B97DF264C4 /* build.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 50E21D4A2B87A0A80E248614 /* build.ninja */; }; 906DB5C85F57EFCBD2027E60 /* grpc_unary_call_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964942163E63900EB9CFB /* grpc_unary_call_test.cc */; }; 907DF0E63248DBF0912CC56D /* filesystem_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA02DA2FCD0001CFC6EB08DA /* filesystem_testing.cc */; }; 90B9302B082E6252AF4E7DC7 /* leveldb_migrations_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = EF83ACD5E1E9F25845A9ACED /* leveldb_migrations_test.cc */; }; @@ -1041,7 +1010,6 @@ 925BE64990449E93242A00A2 /* memory_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 74FBEFA4FE4B12C435011763 /* memory_mutation_queue_test.cc */; }; 92D7081085679497DC112EDB /* persistence_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9113B6F513D0473AEABBAF1F /* persistence_testing.cc */; }; 92EFF0CC2993B43CBC7A61FF /* grpc_streaming_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964922154AB8F00EB9CFB /* grpc_streaming_reader_test.cc */; }; - 9366DC0A832BD2EE267BF61F /* CMakeCCompilerId.c in Sources */ = {isa = PBXBuildFile; fileRef = 987319851B67BC36AF095879 /* CMakeCCompilerId.c */; }; 9382BE7190E7750EE7CCCE7C /* write_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A51F315EE100DD57A1 /* write_spec_test.json */; }; 938F2AF6EC5CD0B839300DB0 /* query.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D621C2DDC800EFB9CC /* query.pb.cc */; }; 939C898FE9D129F6A2EA259C /* FSTHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03A2021401F00B64F25 /* FSTHelpers.mm */; }; @@ -1082,7 +1050,6 @@ 9B2CD4CBB1DFE8BC3C81A335 /* async_queue_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4680208EA0BE00554BA2 /* async_queue_libdispatch_test.mm */; }; 9B9BFC16E26BDE4AE0CDFF4B /* firebase_auth_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F869D85E900E5AF6CD02E2FC /* firebase_auth_credentials_provider_test.mm */; }; 9BEC62D59EB2C68342F493CD /* credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2F4FA4576525144C5069A7A5 /* credentials_provider_test.cc */; }; - 9BF2FDA87D5A6D44C93903C4 /* CMakeCCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 595B30E8CC49F13B0C750D3D /* CMakeCCompiler.cmake */; }; 9C1F25177DC5753B075DCF65 /* existence_filter_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */; }; 9C366448F9BA7A4AC0821AF7 /* bundle_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 79EAA9F7B1B9592B5F053923 /* bundle_spec_test.json */; }; 9C86EEDEA131BFD50255EEF1 /* comparison_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB928200D59F600E00ABC /* comparison_test.cc */; }; @@ -1098,7 +1065,6 @@ 9F9244225BE2EC88AA0CE4EF /* sorted_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */; }; A05BC6BDA2ABE405009211A9 /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; A06FBB7367CDD496887B86F8 /* leveldb_opener_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 75860CD13AF47EB1EA39EC2F /* leveldb_opener_test.cc */; }; - A0B6EA2529C0090CF0AE7F09 /* CMakeCCompilerId.c in Sources */ = {isa = PBXBuildFile; fileRef = 987319851B67BC36AF095879 /* CMakeCCompilerId.c */; }; A0C6C658DFEE58314586907B /* offline_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A11F315EE100DD57A1 /* offline_spec_test.json */; }; A0D61250F959BC52CEFF9467 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C8FB22BCB9F454DA44BA80C8 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json */; }; A0E1C7F5C7093A498F65C5CF /* memory_bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB4AB1388538CD3CB19EB028 /* memory_bundle_cache_test.cc */; }; @@ -1132,7 +1098,6 @@ A61BB461F3E5822175F81719 /* memory_remote_document_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1CA9800A53669EFBFFB824E3 /* memory_remote_document_cache_test.cc */; }; A6A916A7DEA41EE29FD13508 /* watch_change_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2D7472BC70C024D736FF74D9 /* watch_change_test.cc */; }; A6A9946A006AA87240B37E31 /* defer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8ABAC2E0402213D837F73DC3 /* defer_test.cc */; }; - A6AD059A06E650993296D300 /* CMakeCache.txt in Resources */ = {isa = PBXBuildFile; fileRef = A1A5A15FBB6B8FBF90E12B31 /* CMakeCache.txt */; }; A6BDA28DBC85BC1BAB7061F4 /* leveldb_document_overlay_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AE89CFF09C6804573841397F /* leveldb_document_overlay_cache_test.cc */; }; A6D57EC3A0BF39060705ED29 /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; A6E236CE8B3A47BE32254436 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; @@ -1149,7 +1114,6 @@ A9206FF8FF8834347E9C7DDB /* leveldb_overlay_migration_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D8A6D52723B1BABE1B7B8D8F /* leveldb_overlay_migration_manager_test.cc */; }; A97ED2BAAEDB0F765BBD5F98 /* local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 307FF03D0297024D59348EBD /* local_store_test.cc */; }; A9A9994FB8042838671E8506 /* view_snapshot_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CC572A9168BBEF7B83E4BBC5 /* view_snapshot_test.cc */; }; - AA09E52B7BB746E17C507297 /* cmake.check_cache in Resources */ = {isa = PBXBuildFile; fileRef = 1B9C7CBE95D2497D4E0B5741 /* cmake.check_cache */; }; AA13B6E1EF0AD9E9857AAE1C /* byte_stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 432C71959255C5DBDF522F52 /* byte_stream_test.cc */; }; AA859F27A9098D6886B222A8 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BD051DBE754950FEAC7A446 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json */; }; AAC15E7CCAE79619B2ABB972 /* XCTestCase+Await.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */; }; @@ -1231,9 +1195,7 @@ B510921E4CD441289F6B2B78 /* FSTExceptionCatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B8BFD9B37D1029D238BDD71E /* FSTExceptionCatcher.m */; }; B513F723728E923DFF34F60F /* leveldb_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */; }; B54BA1E76636C0C93334271B /* settings_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD12BC1DB2480886D2FB0005 /* settings_test.cc */; }; - B55BEC44BFBC266607C9AC3A /* CMakeDetermineCompilerABI_C.bin in Resources */ = {isa = PBXBuildFile; fileRef = CCB165E99909ED6EE0F882D3 /* CMakeDetermineCompilerABI_C.bin */; }; B576823475FBCA5EFA583F9C /* leveldb_migrations_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = EF83ACD5E1E9F25845A9ACED /* leveldb_migrations_test.cc */; }; - B58E1F10A44829D192B75390 /* clion-Debug-log.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3C3B362DACE291EF501F4533 /* clion-Debug-log.txt */; }; B592DB7DB492B1C1D5E67D01 /* write.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D921C2DDC800EFB9CC /* write.pb.cc */; }; B5AEF7E4EBC29653DEE856A2 /* strerror_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */; }; B60BAF9ED610F9D4E245EEB3 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 1A7D48A017ECB54FD381D126 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json */; }; @@ -1263,7 +1225,6 @@ B6FB4690208F9BB300554BA2 /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; B6FDE6F91D3F81D045E962A0 /* bits_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D01201BC69F00D97691 /* bits_test.cc */; }; B743F4E121E879EF34536A51 /* leveldb_index_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 166CE73C03AB4366AAC5201C /* leveldb_index_manager_test.cc */; }; - B752A44092D47887759B3046 /* TargetDirectories.txt in Resources */ = {isa = PBXBuildFile; fileRef = ACD733FAB49C8DDEB1F9165B /* TargetDirectories.txt */; }; B7DD5FC63A78FF00E80332C0 /* grpc_stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6BBE42F21262CF400C6A53E /* grpc_stream_test.cc */; }; B8062EBDB8E5B680E46A6DD1 /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; }; B81B6F327B5E3FE820DC3FB3 /* aggregation_result.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = D872D754B8AD88E28AF28B28 /* aggregation_result.pb.cc */; }; @@ -1282,7 +1243,6 @@ BA3C0BA8082A6FB2546E47AC /* CodableTimestampTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B65C996438B84DBC7616640 /* CodableTimestampTests.swift */; }; BA9A65BD6D993B2801A3C768 /* grpc_connection_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D9649021544D4F00EB9CFB /* grpc_connection_test.cc */; }; BAB43C839445782040657239 /* executor_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4687208F9B9100554BA2 /* executor_std_test.cc */; }; - BACB779C724E629322DF1788 /* CMakeCCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = A19043284553A1757B6D52DD /* CMakeCCompilerId.o */; }; BACBBF4AF2F5455673AEAB35 /* leveldb_migrations_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = EF83ACD5E1E9F25845A9ACED /* leveldb_migrations_test.cc */; }; BB15588CC1622904CF5AD210 /* sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */; }; BB1A6F7D8F06E74FB6E525C5 /* document_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6152AD5202A5385000E5744 /* document_key_test.cc */; }; @@ -1303,13 +1263,11 @@ BDDAE67000DBF10E9EA7FED0 /* nanopb_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6F5B6C1399F92FD60F2C582B /* nanopb_util_test.cc */; }; BDF3A6C121F2773BB3A347A7 /* counting_query_engine.cc in Sources */ = {isa = PBXBuildFile; fileRef = 99434327614FEFF7F7DC88EC /* counting_query_engine.cc */; }; BE1D7C7E413449AFFBA21BCB /* overlay_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = E1459FA70B8FC18DE4B80D0D /* overlay_test.cc */; }; - BE36D35215706DAD0706F226 /* CMakeCXXCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = 5CE650FC4B2A48B9C8BA9C88 /* CMakeCXXCompilerId.o */; }; BE767D2312D2BE84484309A0 /* event_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6F57521E161450FAF89075ED /* event_manager_test.cc */; }; BE92E16A9B9B7AD5EB072919 /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; BEE0294A23AB993E5DE0E946 /* leveldb_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 332485C4DCC6BA0DBB5E31B7 /* leveldb_util_test.cc */; }; BEF0365AD2718B8B70715978 /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; BEF35ECEE80F9F5161E7743A /* filter_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F02F734F272C3C70D1307076 /* filter_test.cc */; }; - BF624DA8E6F60FD86BC36D90 /* CMakeCCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 595B30E8CC49F13B0C750D3D /* CMakeCCompiler.cmake */; }; BFBE4732E93E38317B110778 /* index_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 8C7278B604B8799F074F4E8C /* index_spec_test.json */; }; BFCDC78CD851F109EB7A1422 /* Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 57F8EE51B5EFC9FAB185B66C /* Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json */; }; BFEAC4151D3AA8CE1F92CC2D /* FSTSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03020213FFC00B64F25 /* FSTSpecTests.mm */; }; @@ -1317,7 +1275,6 @@ C06E54352661FCFB91968640 /* mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3068AA9DFBBA86C1FE2A946E /* mutation_queue_test.cc */; }; C09BDBA73261578F9DA74CEE /* firebase_auth_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F869D85E900E5AF6CD02E2FC /* firebase_auth_credentials_provider_test.mm */; }; C0AD8DB5A84CAAEE36230899 /* status_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352C20A3B3D7003E0143 /* status_test.cc */; }; - C0ECFC49885BFC559FDD0E8A /* CMakeCXXCompiler.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 255C95F5F18BBB1E6BF77B62 /* CMakeCXXCompiler.cmake */; }; C0EFC5FB79517679C377C252 /* schedule_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B0B005A79E765AF02793DCE /* schedule_test.cc */; }; C10417B067155BE78E19807D /* FIRIndexingTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 795AA8FC31D2AF6864B07D39 /* FIRIndexingTests.mm */; }; C1237EE2A74F174A3DF5978B /* memory_target_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2286F308EFB0534B1BDE05B9 /* memory_target_cache_test.cc */; }; @@ -1403,7 +1360,6 @@ D156B9F19B5B29E77664FDFC /* logic_utils_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 28B45B2104E2DAFBBF86DBB7 /* logic_utils_test.cc */; }; D1690214781198276492442D /* event_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6F57521E161450FAF89075ED /* event_manager_test.cc */; }; D18DBCE3FE34BF5F14CF8ABD /* mutation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = C8522DE226C467C54E6788D8 /* mutation_test.cc */; }; - D1B0B9ED583F83E027C49A9B /* CMakeSystem.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 658741EEE206F2C8C4159412 /* CMakeSystem.cmake */; }; D1BCDAEACF6408200DFB9870 /* overlay_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = E1459FA70B8FC18DE4B80D0D /* overlay_test.cc */; }; D21060F8115A5F48FC3BF335 /* local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 307FF03D0297024D59348EBD /* local_store_test.cc */; }; D22B96C19A0F3DE998D4320C /* delayed_constructor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */; }; @@ -1418,7 +1374,6 @@ D3CB03747E34D7C0365638F1 /* transform_operation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 33607A3AE91548BD219EC9C6 /* transform_operation_test.cc */; }; D4572060A0FD4D448470D329 /* leveldb_transaction_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 88CF09277CFA45EE1273E3BA /* leveldb_transaction_test.cc */; }; D4D8BA32ACC5C2B1B29711C0 /* memory_lru_garbage_collector_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9765D47FA12FA283F4EFAD02 /* memory_lru_garbage_collector_test.cc */; }; - D4EC6FAED8E00499D598CC55 /* rules.ninja in Resources */ = {isa = PBXBuildFile; fileRef = 086F5AEE493AF6473EF134D0 /* rules.ninja */; }; D4F85AEACD2FD03C738D1052 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 5C68EE4CB94C0DD6E333F546 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json */; }; D50232D696F19C2881AC01CE /* token_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A082AFDD981B07B5AD78FDE8 /* token_test.cc */; }; D550446303227FB1B381133C /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; @@ -1441,9 +1396,6 @@ D73BBA4AB42940AB187169E3 /* listen_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A01F315EE100DD57A1 /* listen_spec_test.json */; }; D756A1A63E626572EE8DF592 /* firestore.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D421C2DDC800EFB9CC /* firestore.pb.cc */; }; D77941FD93DBE862AEF1F623 /* FSTTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */; }; - D7E1009A9C7C84F6BE8804A5 /* CMakeSystem.cmake in Resources */ = {isa = PBXBuildFile; fileRef = 658741EEE206F2C8C4159412 /* CMakeSystem.cmake */; }; - D871697BED043E498873CEFD /* CMakeCCompilerId.c in Sources */ = {isa = PBXBuildFile; fileRef = 987319851B67BC36AF095879 /* CMakeCCompilerId.c */; }; - D90E6FE567D20E897A0B1201 /* CMakeCCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = A19043284553A1757B6D52DD /* CMakeCCompilerId.o */; }; D91D86B29B86A60C05879A48 /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; }; D9366A834BFF13246DC3AF9E /* field_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2AD2023DDB20028D6BE /* field_path_test.cc */; }; D94A1862B8FB778225DB54A1 /* filesystem_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F51859B394D01C0C507282F1 /* filesystem_test.cc */; }; @@ -1469,7 +1421,6 @@ DC1C711290E12F8EF3601151 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; DC48407370E87F2233D7AB7E /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; DC6804424FC8F7B3044DD0BB /* random_access_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 014C60628830D95031574D15 /* random_access_queue_test.cc */; }; - DC7F5C18D65040E505E580A9 /* clion-environment.txt in Resources */ = {isa = PBXBuildFile; fileRef = B075588AAD5B99791B537B51 /* clion-environment.txt */; }; DCC8F3D4AA87C81AB3FD9491 /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3D050936A2D52257FD17FB6E /* md5_test.cc */; }; DCD83C545D764FB15FD88B02 /* counting_query_engine.cc in Sources */ = {isa = PBXBuildFile; fileRef = 99434327614FEFF7F7DC88EC /* counting_query_engine.cc */; }; DD04F7FE7A1ADE230A247DBC /* byte_stream_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7628664347B9C96462D4BF17 /* byte_stream_apple_test.mm */; }; @@ -1515,7 +1466,6 @@ E3319DC1804B69F0ED1FFE02 /* memory_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 74FBEFA4FE4B12C435011763 /* memory_mutation_queue_test.cc */; }; E375FBA0632EFB4D14C4E5A9 /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; }; E37C52277CD00C57E5848A0E /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 5C68EE4CB94C0DD6E333F546 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json */; }; - E432BC38147553374D70AE62 /* CMakeCXXCompilerId.cpp in Resources */ = {isa = PBXBuildFile; fileRef = C1396907A38E6E67313C0D74 /* CMakeCXXCompilerId.cpp */; }; E434ACDF63F219F3031F292E /* ConditionalConformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3228F51DCDC2E90D5C58F97 /* ConditionalConformanceTests.swift */; }; E435450184AEB51EE8435F66 /* write.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D921C2DDC800EFB9CC /* write.pb.cc */; }; E441A53D035479C53C74A0E6 /* recovery_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 9C1AFCC9E616EC33D6E169CF /* recovery_spec_test.json */; }; @@ -1539,14 +1489,12 @@ E72A77095FF6814267DF0F6D /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = E2E39422953DE1D3C7B97E77 /* md5_testing.cc */; }; E74D6C1056DE29969B5C4C62 /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3D050936A2D52257FD17FB6E /* md5_test.cc */; }; E764F0F389E7119220EB212C /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; - E789BD9F16671F4AA48E9A52 /* CMakeCCompilerId.o in Resources */ = {isa = PBXBuildFile; fileRef = A19043284553A1757B6D52DD /* CMakeCCompilerId.o */; }; E7CE4B1ECD008983FAB90F44 /* string_format_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54131E9620ADE678001DF3FF /* string_format_test.cc */; }; E7D415B8717701B952C344E5 /* executor_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4687208F9B9100554BA2 /* executor_std_test.cc */; }; E827A3B15D6C8C1298A7BC72 /* Validation_BloomFilterTest_MD5_5000_1_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 4375BDCDBCA9938C7F086730 /* Validation_BloomFilterTest_MD5_5000_1_bloom_filter_proto.json */; }; E82F8EBBC8CC37299A459E73 /* hashing_test_apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = B69CF3F02227386500B281C8 /* hashing_test_apple.mm */; }; E8495A8D1E11C0844339CCA3 /* database_info_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D92E20235D22000A432D /* database_info_test.cc */; }; E8608D40B683938C6D785627 /* credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2F4FA4576525144C5069A7A5 /* credentials_provider_test.cc */; }; - E86B903869E65E81DA7ED5C6 /* clion-Debug-log.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3C3B362DACE291EF501F4533 /* clion-Debug-log.txt */; }; E884336B43BBD1194C17E3C4 /* status_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3CAA33F964042646FDDAF9F9 /* status_testing.cc */; }; E8AB8024B70F6C960D8C7530 /* document_overlay_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = FFCA39825D9678A03D1845D0 /* document_overlay_cache_test.cc */; }; E8BA7055EDB8B03CC99A528F /* recovery_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 9C1AFCC9E616EC33D6E169CF /* recovery_spec_test.json */; }; @@ -1596,7 +1544,6 @@ F05B277F16BDE6A47FE0F943 /* local_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F8043813A5D16963EC02B182 /* local_serializer_test.cc */; }; F08DA55D31E44CB5B9170CCE /* limbo_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */; }; F091532DEE529255FB008E25 /* snapshot_version_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */; }; - F0B0A9C420B3429807FFAF33 /* clion-environment.txt in Resources */ = {isa = PBXBuildFile; fileRef = B075588AAD5B99791B537B51 /* clion-environment.txt */; }; F0C8EB1F4FB56401CFA4F374 /* object_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 214877F52A705012D6720CA0 /* object_value_test.cc */; }; F0EA84FB66813F2BC164EF7C /* token_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A082AFDD981B07B5AD78FDE8 /* token_test.cc */; }; F10A3E4E164A5458DFF7EDE6 /* leveldb_remote_document_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0840319686A223CC4AD3FAB1 /* leveldb_remote_document_cache_test.cc */; }; @@ -1613,7 +1560,6 @@ F3261CBFC169DB375A0D9492 /* FSTMockDatastore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02D20213FFC00B64F25 /* FSTMockDatastore.mm */; }; F3DEF2DB11FADAABDAA4C8BB /* bundle_builder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4F5B96F3ABCD2CA901DB1CD4 /* bundle_builder.cc */; }; F3F09BC931A717CEFF4E14B9 /* FIRFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04A202154AA00B64F25 /* FIRFieldValueTests.mm */; }; - F424CBF1A655EA030F5A2FD3 /* cmake.check_cache in Resources */ = {isa = PBXBuildFile; fileRef = 1B9C7CBE95D2497D4E0B5741 /* cmake.check_cache */; }; F481368DB694B3B4D0C8E4A2 /* query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C261C26C5D311E1E3C0CB9 /* query_test.cc */; }; F4F00BF4E87D7F0F0F8831DB /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; F4FAC5A7D40A0A9A3EA77998 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; @@ -1735,7 +1681,6 @@ 0473AFFF5567E667A125347B /* ordered_code_benchmark.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = ordered_code_benchmark.cc; sourceTree = ""; }; 062072B62773A055001655D7 /* AsyncAwaitIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncAwaitIntegrationTests.swift; sourceTree = ""; }; 0840319686A223CC4AD3FAB1 /* leveldb_remote_document_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_remote_document_cache_test.cc; sourceTree = ""; }; - 086F5AEE493AF6473EF134D0 /* rules.ninja */ = {isa = PBXFileReference; includeInIndex = 1; name = rules.ninja; path = "cmake-build-debug/CMakeFiles/rules.ninja"; sourceTree = ""; }; 0D964D4936953635AC7E0834 /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json; sourceTree = ""; }; 0EE5300F8233D14025EF0456 /* string_apple_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = string_apple_test.mm; sourceTree = ""; }; 11984BA0A99D7A7ABA5B0D90 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig"; sourceTree = ""; }; @@ -1750,7 +1695,6 @@ 1A7D48A017ECB54FD381D126 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json; sourceTree = ""; }; 1A8141230C7E3986EACEF0B6 /* thread_safe_memoizer_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = thread_safe_memoizer_test.cc; sourceTree = ""; }; 1B342370EAE3AA02393E33EB /* cc_compilation_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = cc_compilation_test.cc; path = api/cc_compilation_test.cc; sourceTree = ""; }; - 1B9C7CBE95D2497D4E0B5741 /* cmake.check_cache */ = {isa = PBXFileReference; includeInIndex = 1; name = cmake.check_cache; path = "cmake-build-debug/CMakeFiles/cmake.check_cache"; sourceTree = ""; }; 1B9F95EC29FAD3F100EEC075 /* FIRAggregateQueryUnitTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRAggregateQueryUnitTests.mm; sourceTree = ""; }; 1C01D8CE367C56BB2624E299 /* index.pb.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = index.pb.h; path = admin/index.pb.h; sourceTree = ""; }; 1C3F7302BF4AE6CBC00ECDD0 /* resource.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = resource.pb.cc; sourceTree = ""; }; @@ -1761,7 +1705,6 @@ 2220F583583EFC28DE792ABE /* Pods_Firestore_IntegrationTests_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2286F308EFB0534B1BDE05B9 /* memory_target_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_target_cache_test.cc; sourceTree = ""; }; 230BC40D8C715CABFB10BBA9 /* FIRSnapshotListenerSourceTests.mm */ = {isa = PBXFileReference; includeInIndex = 1; path = FIRSnapshotListenerSourceTests.mm; sourceTree = ""; }; - 255C95F5F18BBB1E6BF77B62 /* CMakeCXXCompiler.cmake */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCXXCompiler.cmake; path = "cmake-build-debug/CMakeFiles/3.26.4/CMakeCXXCompiler.cmake"; sourceTree = ""; }; 277EAACC4DD7C21332E8496A /* lru_garbage_collector_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = lru_garbage_collector_test.cc; sourceTree = ""; }; 28B45B2104E2DAFBBF86DBB7 /* logic_utils_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = logic_utils_test.cc; sourceTree = ""; }; 29D9C76922DAC6F710BC1EF4 /* memory_document_overlay_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_document_overlay_cache_test.cc; sourceTree = ""; }; @@ -1788,7 +1731,6 @@ 397FB002E298B780F1E223E2 /* Pods-Firestore_Tests_macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_macOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_macOS/Pods-Firestore_Tests_macOS.release.xcconfig"; sourceTree = ""; }; 39B832380209CC5BAF93BC52 /* Pods_Firestore_IntegrationTests_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = remote_store_spec_test.json; sourceTree = ""; }; - 3C3B362DACE291EF501F4533 /* clion-Debug-log.txt */ = {isa = PBXFileReference; includeInIndex = 1; name = "clion-Debug-log.txt"; path = "cmake-build-debug/CMakeFiles/clion-Debug-log.txt"; sourceTree = ""; }; 3C81DE3772628FE297055662 /* Pods-Firestore_Example_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.debug.xcconfig"; sourceTree = ""; }; 3CAA33F964042646FDDAF9F9 /* status_testing.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = status_testing.cc; sourceTree = ""; }; 3D050936A2D52257FD17FB6E /* md5_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = md5_test.cc; sourceTree = ""; }; @@ -1810,7 +1752,6 @@ 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = string_apple_benchmark.mm; sourceTree = ""; }; 4D9E51DA7A275D8B1CAEAEB2 /* listen_source_spec_test.json */ = {isa = PBXFileReference; includeInIndex = 1; path = listen_source_spec_test.json; sourceTree = ""; }; 4F5B96F3ABCD2CA901DB1CD4 /* bundle_builder.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bundle_builder.cc; sourceTree = ""; }; - 50E21D4A2B87A0A80E248614 /* build.ninja */ = {isa = PBXFileReference; includeInIndex = 1; name = build.ninja; path = "cmake-build-debug/build.ninja"; sourceTree = ""; }; 526D755F65AC676234F57125 /* target_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = target_test.cc; sourceTree = ""; }; 52756B7624904C36FBB56000 /* fake_target_metadata_provider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = fake_target_metadata_provider.h; sourceTree = ""; }; 5342CDDB137B4E93E2E85CCA /* byte_string_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = byte_string_test.cc; path = nanopb/byte_string_test.cc; sourceTree = ""; }; @@ -1916,14 +1857,12 @@ 57F8EE51B5EFC9FAB185B66C /* Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_5000_01_bloom_filter_proto.json; sourceTree = ""; }; 584AE2C37A55B408541A6FF3 /* remote_event_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = remote_event_test.cc; sourceTree = ""; }; 5918805E993304321A05E82B /* Pods_Firestore_Example_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 595B30E8CC49F13B0C750D3D /* CMakeCCompiler.cmake */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCCompiler.cmake; path = "cmake-build-debug/CMakeFiles/3.26.4/CMakeCCompiler.cmake"; sourceTree = ""; }; 5B5414D28802BC76FDADABD6 /* stream_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = stream_test.cc; sourceTree = ""; }; 5B96CC29E9946508F022859C /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json; sourceTree = ""; }; 5C68EE4CB94C0DD6E333F546 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_1_01_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_1_01_membership_test_result.json; sourceTree = ""; }; 5C7942B6244F4C416B11B86C /* leveldb_mutation_queue_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_mutation_queue_test.cc; sourceTree = ""; }; 5CAE131920FFFED600BE9A4A /* Firestore_Benchmarks_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_Benchmarks_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5CAE131D20FFFED600BE9A4A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 5CE650FC4B2A48B9C8BA9C88 /* CMakeCXXCompilerId.o */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCXXCompilerId.o; path = "cmake-build-debug/CMakeFiles/3.26.4/CompilerIdCXX/CMakeCXXCompilerId.o"; sourceTree = ""; }; 5E19B9B2105BA618DA9EE99C /* query_engine_test.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = query_engine_test.h; sourceTree = ""; }; 5FF903AEFA7A3284660FA4C5 /* leveldb_local_store_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_local_store_test.cc; sourceTree = ""; }; 6003F58A195388D20070C39A /* Firestore_Example_iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Firestore_Example_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1965,9 +1904,7 @@ 62E54B832A9E910A003347C8 /* IndexingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndexingTests.swift; sourceTree = ""; }; 63136A2371C0C013EC7A540C /* target_index_matcher_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = target_index_matcher_test.cc; sourceTree = ""; }; 64AA92CFA356A2360F3C5646 /* filesystem_testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = filesystem_testing.h; sourceTree = ""; }; - 658741EEE206F2C8C4159412 /* CMakeSystem.cmake */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeSystem.cmake; path = "cmake-build-debug/CMakeFiles/3.26.4/CMakeSystem.cmake"; sourceTree = ""; }; 65AF0AB593C3AD81A1F1A57E /* FIRCompositeIndexQueryTests.mm */ = {isa = PBXFileReference; includeInIndex = 1; path = FIRCompositeIndexQueryTests.mm; sourceTree = ""; }; - 6739481496019FB93579846B /* CMakeConfigureLog.yaml */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeConfigureLog.yaml; path = "cmake-build-debug/CMakeFiles/CMakeConfigureLog.yaml"; sourceTree = ""; }; 67786C62C76A740AEDBD8CD3 /* FSTTestingHooks.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = FSTTestingHooks.h; sourceTree = ""; }; 69E6C311558EC77729A16CF1 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; sourceTree = ""; }; 6A7A30A2DB3367E08939E789 /* bloom_filter.pb.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = bloom_filter.pb.h; sourceTree = ""; }; @@ -2030,15 +1967,12 @@ 9765D47FA12FA283F4EFAD02 /* memory_lru_garbage_collector_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_lru_garbage_collector_test.cc; sourceTree = ""; }; 97C492D2524E92927C11F425 /* Pods-Firestore_FuzzTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_FuzzTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS.release.xcconfig"; sourceTree = ""; }; 98366480BD1FD44A1FEDD982 /* Pods-Firestore_Example_macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_macOS/Pods-Firestore_Example_macOS.debug.xcconfig"; sourceTree = ""; }; - 987319851B67BC36AF095879 /* CMakeCCompilerId.c */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCCompilerId.c; path = "cmake-build-debug/CMakeFiles/3.26.4/CompilerIdC/CMakeCCompilerId.c"; sourceTree = ""; }; 99434327614FEFF7F7DC88EC /* counting_query_engine.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = counting_query_engine.cc; sourceTree = ""; }; 9B0B005A79E765AF02793DCE /* schedule_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = schedule_test.cc; sourceTree = ""; }; 9C1AFCC9E616EC33D6E169CF /* recovery_spec_test.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; path = recovery_spec_test.json; sourceTree = ""; }; 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = string_format_apple_test.mm; sourceTree = ""; }; A002425BC4FC4E805F4175B6 /* testing_hooks_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = testing_hooks_test.cc; sourceTree = ""; }; A082AFDD981B07B5AD78FDE8 /* token_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = token_test.cc; path = credentials/token_test.cc; sourceTree = ""; }; - A19043284553A1757B6D52DD /* CMakeCCompilerId.o */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCCompilerId.o; path = "cmake-build-debug/CMakeFiles/3.26.4/CompilerIdC/CMakeCCompilerId.o"; sourceTree = ""; }; - A1A5A15FBB6B8FBF90E12B31 /* CMakeCache.txt */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeCache.txt; path = "cmake-build-debug/CMakeCache.txt"; sourceTree = ""; }; A20BAA3D2F994384279727EC /* md5_testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = md5_testing.h; sourceTree = ""; }; A2E6F09AD1EE0A6A452E9A08 /* bloom_filter_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bloom_filter_test.cc; sourceTree = ""; }; A366F6AE1A5A77548485C091 /* bundle.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bundle.pb.cc; sourceTree = ""; }; @@ -2060,13 +1994,10 @@ AB7BAB332012B519001E0872 /* geo_point_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = geo_point_test.cc; sourceTree = ""; }; ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = snapshot_version_test.cc; sourceTree = ""; }; ABF6506B201131F8005F2C74 /* timestamp_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = timestamp_test.cc; sourceTree = ""; }; - ACD733FAB49C8DDEB1F9165B /* TargetDirectories.txt */ = {isa = PBXFileReference; includeInIndex = 1; name = TargetDirectories.txt; path = "cmake-build-debug/CMakeFiles/TargetDirectories.txt"; sourceTree = ""; }; AE4A9E38D65688EE000EE2A1 /* index_manager_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = index_manager_test.cc; sourceTree = ""; }; AE89CFF09C6804573841397F /* leveldb_document_overlay_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_document_overlay_cache_test.cc; sourceTree = ""; }; AF924C79F49F793992A84879 /* aggregate_query_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = aggregate_query_test.cc; path = api/aggregate_query_test.cc; sourceTree = ""; }; - B03849DC3F7474A592D71754 /* cmake_install.cmake */ = {isa = PBXFileReference; includeInIndex = 1; name = cmake_install.cmake; path = "cmake-build-debug/cmake_install.cmake"; sourceTree = ""; }; B0520A41251254B3C24024A3 /* Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_5000_01_membership_test_result.json; sourceTree = ""; }; - B075588AAD5B99791B537B51 /* clion-environment.txt */ = {isa = PBXFileReference; includeInIndex = 1; name = "clion-environment.txt"; path = "cmake-build-debug/CMakeFiles/clion-environment.txt"; sourceTree = ""; }; B3F5B3AAE791A5911B9EAA82 /* Pods-Firestore_Tests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS.release.xcconfig"; sourceTree = ""; }; B5C2A94EE24E60543F62CC35 /* bundle_serializer_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = bundle_serializer_test.cc; path = bundle/bundle_serializer_test.cc; sourceTree = ""; }; B5C37696557C81A6C2B7271A /* target_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = target_cache_test.cc; sourceTree = ""; }; @@ -2105,7 +2036,6 @@ BD01F0E43E4E2A07B8B05099 /* Pods-Firestore_Tests_macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_macOS/Pods-Firestore_Tests_macOS.debug.xcconfig"; sourceTree = ""; }; BF76A8DA34B5B67B4DD74666 /* field_index_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = field_index_test.cc; sourceTree = ""; }; C0C7C8977C94F9F9AFA4DB00 /* local_store_test.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = local_store_test.h; sourceTree = ""; }; - C1396907A38E6E67313C0D74 /* CMakeCXXCompilerId.cpp */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = CMakeCXXCompilerId.cpp; path = "cmake-build-debug/CMakeFiles/3.26.4/CompilerIdCXX/CMakeCXXCompilerId.cpp"; sourceTree = ""; }; C7429071B33BDF80A7FA2F8A /* view_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = view_test.cc; sourceTree = ""; }; C8522DE226C467C54E6788D8 /* mutation_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = mutation_test.cc; sourceTree = ""; }; C8582DFD74E8060C7072104B /* Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_5000_0001_membership_test_result.json; sourceTree = ""; }; @@ -2113,7 +2043,6 @@ C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json; sourceTree = ""; }; CB7B2D4691C380DE3EB59038 /* lru_garbage_collector_test.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = lru_garbage_collector_test.h; sourceTree = ""; }; CC572A9168BBEF7B83E4BBC5 /* view_snapshot_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = view_snapshot_test.cc; sourceTree = ""; }; - CCB165E99909ED6EE0F882D3 /* CMakeDetermineCompilerABI_C.bin */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeDetermineCompilerABI_C.bin; path = "cmake-build-debug/CMakeFiles/3.26.4/CMakeDetermineCompilerABI_C.bin"; sourceTree = ""; }; CCC9BD953F121B9E29F9AA42 /* user_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = user_test.cc; path = credentials/user_test.cc; sourceTree = ""; }; CD422AF3E4515FB8E9BE67A0 /* equals_tester.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = equals_tester.h; sourceTree = ""; }; CE37875365497FFA8687B745 /* message_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; name = message_test.cc; path = nanopb/message_test.cc; sourceTree = ""; }; @@ -2165,7 +2094,6 @@ F119BDDF2F06B3C0883B8297 /* firebase_app_check_credentials_provider_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; name = firebase_app_check_credentials_provider_test.mm; path = credentials/firebase_app_check_credentials_provider_test.mm; sourceTree = ""; }; F354C0FE92645B56A6C6FD44 /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS.release.xcconfig"; sourceTree = ""; }; F51859B394D01C0C507282F1 /* filesystem_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = filesystem_test.cc; sourceTree = ""; }; - F62829EC3A07509E3C54247E /* CMakeDetermineCompilerABI_CXX.bin */ = {isa = PBXFileReference; includeInIndex = 1; name = CMakeDetermineCompilerABI_CXX.bin; path = "cmake-build-debug/CMakeFiles/3.26.4/CMakeDetermineCompilerABI_CXX.bin"; sourceTree = ""; }; F694C3CE4B77B3C0FA4BBA53 /* Pods_Firestore_Benchmarks_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Benchmarks_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F6CA0C5638AB6627CB5B4CF4 /* memory_local_store_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_local_store_test.cc; sourceTree = ""; }; F7FC06E0A47D393DE1759AE1 /* bundle_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bundle_cache_test.cc; sourceTree = ""; }; @@ -2322,11 +2250,6 @@ children = ( DBEB6B536E39F1EC1027C339 /* CompilerIdC */, 69F99B85B5E15C19EFF6A21F /* CompilerIdCXX */, - 595B30E8CC49F13B0C750D3D /* CMakeCCompiler.cmake */, - 255C95F5F18BBB1E6BF77B62 /* CMakeCXXCompiler.cmake */, - CCB165E99909ED6EE0F882D3 /* CMakeDetermineCompilerABI_C.bin */, - F62829EC3A07509E3C54247E /* CMakeDetermineCompilerABI_CXX.bin */, - 658741EEE206F2C8C4159412 /* CMakeSystem.cmake */, ); name = 3.26.4; sourceTree = ""; @@ -2834,8 +2757,6 @@ 69F99B85B5E15C19EFF6A21F /* CompilerIdCXX */ = { isa = PBXGroup; children = ( - C1396907A38E6E67313C0D74 /* CMakeCXXCompilerId.cpp */, - 5CE650FC4B2A48B9C8BA9C88 /* CMakeCXXCompilerId.o */, ); name = CompilerIdCXX; sourceTree = ""; @@ -3000,8 +2921,6 @@ DBEB6B536E39F1EC1027C339 /* CompilerIdC */ = { isa = PBXGroup; children = ( - 987319851B67BC36AF095879 /* CMakeCCompilerId.c */, - A19043284553A1757B6D52DD /* CMakeCCompilerId.o */, ); name = CompilerIdC; sourceTree = ""; @@ -3010,9 +2929,6 @@ isa = PBXGroup; children = ( E3277B52449F042F11FDA06A /* CMakeFiles */, - A1A5A15FBB6B8FBF90E12B31 /* CMakeCache.txt */, - 50E21D4A2B87A0A80E248614 /* build.ninja */, - B03849DC3F7474A592D71754 /* cmake_install.cmake */, ); name = "cmake-build-debug"; sourceTree = ""; @@ -3150,12 +3066,6 @@ isa = PBXGroup; children = ( 475693833C55E80B2C4CA461 /* 3.26.4 */, - 6739481496019FB93579846B /* CMakeConfigureLog.yaml */, - ACD733FAB49C8DDEB1F9165B /* TargetDirectories.txt */, - 3C3B362DACE291EF501F4533 /* clion-Debug-log.txt */, - B075588AAD5B99791B537B51 /* clion-environment.txt */, - 1B9C7CBE95D2497D4E0B5741 /* cmake.check_cache */, - 086F5AEE493AF6473EF134D0 /* rules.ninja */, ); name = CMakeFiles; sourceTree = ""; @@ -3598,17 +3508,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 472D630C4885EF2A051D0236 /* CMakeCCompiler.cmake in Resources */, - E789BD9F16671F4AA48E9A52 /* CMakeCCompilerId.o in Resources */, - 6117E9BC3F39DF2175392D2D /* CMakeCXXCompiler.cmake in Resources */, - 2E3DC61BEF1F7ED477C0B216 /* CMakeCXXCompilerId.cpp in Resources */, - 7E8D4C5AF3649DCD4F736ECA /* CMakeCXXCompilerId.o in Resources */, - 630A50115EB50102D3DF359B /* CMakeCache.txt in Resources */, - 7ACBA78419279E4BAAE67234 /* CMakeConfigureLog.yaml in Resources */, - 83C76AE4AD8EA09842C5E3F0 /* CMakeDetermineCompilerABI_C.bin in Resources */, - 86A3EBF677D4C9FDE5562C5B /* CMakeDetermineCompilerABI_CXX.bin in Resources */, - D7E1009A9C7C84F6BE8804A5 /* CMakeSystem.cmake in Resources */, - 8150ED535D52D9F06922DD7A /* TargetDirectories.txt in Resources */, 8242BB61FBF44B9F5CAC35A7 /* Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json in Resources */, 2E5758FE6CFE753B04D50F89 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */, 2FAE0BCBE559ED7214AEFEB7 /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json in Resources */, @@ -3633,12 +3532,7 @@ AC44D6363F57CEAAB291ED49 /* Validation_BloomFilterTest_MD5_500_01_membership_test_result.json in Resources */, 8B2921C75DB7DD912AE14B8F /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json in Resources */, B491EF0E70DC0542644F623E /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json in Resources */, - 9066CD3ABC6357B97DF264C4 /* build.ninja in Resources */, 4B5FA86D9568ECE20C6D3AD1 /* bundle_spec_test.json in Resources */, - E86B903869E65E81DA7ED5C6 /* clion-Debug-log.txt in Resources */, - F0B0A9C420B3429807FFAF33 /* clion-environment.txt in Resources */, - AA09E52B7BB746E17C507297 /* cmake.check_cache in Resources */, - 3D916BCFBE8366532F4592D8 /* cmake_install.cmake in Resources */, 08839E1CEAAC07E350257E9D /* collection_spec_test.json in Resources */, 9C1F25177DC5753B075DCF65 /* existence_filter_spec_test.json in Resources */, 0DDCAC7C7CA55CF10AE0E809 /* garbage_collection_spec_test.json in Resources */, @@ -3655,7 +3549,6 @@ E8BA7055EDB8B03CC99A528F /* recovery_spec_test.json in Resources */, E21D819A06D9691A4B313440 /* remote_store_spec_test.json in Resources */, B43014A0517F31246419E08A /* resume_token_spec_test.json in Resources */, - D4EC6FAED8E00499D598CC55 /* rules.ninja in Resources */, 9382BE7190E7750EE7CCCE7C /* write_spec_test.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3664,17 +3557,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - BF624DA8E6F60FD86BC36D90 /* CMakeCCompiler.cmake in Resources */, - BACB779C724E629322DF1788 /* CMakeCCompilerId.o in Resources */, - 8B35131E57699519EF23EE7E /* CMakeCXXCompiler.cmake in Resources */, - E432BC38147553374D70AE62 /* CMakeCXXCompilerId.cpp in Resources */, - 2A08DEF81996D661D3FE48BC /* CMakeCXXCompilerId.o in Resources */, - 852C67A7F19AECE97AEDC367 /* CMakeCache.txt in Resources */, - 14664F6DD55A8ADE13524FEF /* CMakeConfigureLog.yaml in Resources */, - B55BEC44BFBC266607C9AC3A /* CMakeDetermineCompilerABI_C.bin in Resources */, - 5706E3A2894546779918907A /* CMakeDetermineCompilerABI_CXX.bin in Resources */, - D1B0B9ED583F83E027C49A9B /* CMakeSystem.cmake in Resources */, - B752A44092D47887759B3046 /* TargetDirectories.txt in Resources */, 0C9887A2F6728CB9E8A4C3CA /* Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json in Resources */, 57F0E1A1F2B614BA74961D9A /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */, F2876F16CF689FD7FFBA9DFA /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json in Resources */, @@ -3699,12 +3581,7 @@ F1F8FB9254E9A5107161A7B2 /* Validation_BloomFilterTest_MD5_500_01_membership_test_result.json in Resources */, 34B62A40BB56F9574B87B28B /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json in Resources */, C1C3369C7ECE069B76A84AD1 /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json in Resources */, - 6F4DBC1B2FAEEDDAAFC1FBD3 /* build.ninja in Resources */, 0B002E2E2012B32EB801C6D5 /* bundle_spec_test.json in Resources */, - 8D43DF691E6ABA6A35EEE059 /* clion-Debug-log.txt in Resources */, - 38159A77164B60443D556932 /* clion-environment.txt in Resources */, - 85A036436686AA6D71FDF073 /* cmake.check_cache in Resources */, - 7AF62F2F7C186028303D7AEC /* cmake_install.cmake in Resources */, 009CDC6F03AC92F3E345085E /* collection_spec_test.json in Resources */, 7AD020FC27493FF8E659436C /* existence_filter_spec_test.json in Resources */, 1BB0C34B2E8D8BCC5882430A /* garbage_collection_spec_test.json in Resources */, @@ -3721,7 +3598,6 @@ 34E866DB52AAB7DB76B69A91 /* recovery_spec_test.json in Resources */, AF81B6A91987826426F18647 /* remote_store_spec_test.json in Resources */, CC94A33318F983907E9ED509 /* resume_token_spec_test.json in Resources */, - 578B89F99DEF39417976162D /* rules.ninja in Resources */, 2DB56B6DED2C93014AE5C51A /* write_spec_test.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3816,17 +3692,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9BF2FDA87D5A6D44C93903C4 /* CMakeCCompiler.cmake in Resources */, - D90E6FE567D20E897A0B1201 /* CMakeCCompilerId.o in Resources */, - C0ECFC49885BFC559FDD0E8A /* CMakeCXXCompiler.cmake in Resources */, - 7F87A0D114D0C629532853D8 /* CMakeCXXCompilerId.cpp in Resources */, - BE36D35215706DAD0706F226 /* CMakeCXXCompilerId.o in Resources */, - A6AD059A06E650993296D300 /* CMakeCache.txt in Resources */, - 4C8AA63E619E23AB2A122DCA /* CMakeConfigureLog.yaml in Resources */, - 4130140DE4E5043E3E000721 /* CMakeDetermineCompilerABI_C.bin in Resources */, - 59F6CEADB513CBB5F4738161 /* CMakeDetermineCompilerABI_CXX.bin in Resources */, - 73FB09EAD9B8C22EF03A1702 /* CMakeSystem.cmake in Resources */, - 40EADA93E01E6DAF55143DD4 /* TargetDirectories.txt in Resources */, 7A7DB86955670B85B4514A1F /* Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json in Resources */, 06E0914D76667F1345EC17F5 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */, 37461AF1ACC2E64DF1709736 /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json in Resources */, @@ -3851,12 +3716,7 @@ 662E94803D6FABE56F0D22C9 /* Validation_BloomFilterTest_MD5_500_01_membership_test_result.json in Resources */, 2618255E63631038B64DF3BB /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json in Resources */, 42A98512D4C9EC6722334FE6 /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json in Resources */, - 28E7535BEFC7BB66FB58AA97 /* build.ninja in Resources */, 32030FA5B4BE6ABDFF2F974E /* bundle_spec_test.json in Resources */, - B58E1F10A44829D192B75390 /* clion-Debug-log.txt in Resources */, - DC7F5C18D65040E505E580A9 /* clion-environment.txt in Resources */, - F424CBF1A655EA030F5A2FD3 /* cmake.check_cache in Resources */, - 87AA0B64034604D5461100A8 /* cmake_install.cmake in Resources */, 46B104DEE6014D881F7ED169 /* collection_spec_test.json in Resources */, 3887E1635B31DCD7BC0922BD /* existence_filter_spec_test.json in Resources */, 47B8ED6737A24EF96B1ED318 /* garbage_collection_spec_test.json in Resources */, @@ -3873,7 +3733,6 @@ E441A53D035479C53C74A0E6 /* recovery_spec_test.json in Resources */, 022BA1619A576F6818B212C5 /* remote_store_spec_test.json in Resources */, 9EE81B1FB9B7C664B7B0A904 /* resume_token_spec_test.json in Resources */, - 26FE3A7AC99693E1029BC437 /* rules.ninja in Resources */, E6688C8E524770A3C6EBB33A /* write_spec_test.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -4717,7 +4576,6 @@ EF6C286F29E6D22200A7D4F1 /* AggregationIntegrationTests.swift in Sources */, 062072B92773A055001655D7 /* AsyncAwaitIntegrationTests.swift in Sources */, 733AFC467B600967536BD70F /* BasicCompileTests.swift in Sources */, - D871697BED043E498873CEFD /* CMakeCCompilerId.c in Sources */, 79987AF2DF1FCE799008B846 /* CodableGeoPointTests.swift in Sources */, 1C79AE3FBFC91800E30D092C /* CodableIntegrationTests.swift in Sources */, BA3C0BA8082A6FB2546E47AC /* CodableTimestampTests.swift in Sources */, @@ -4959,7 +4817,6 @@ EF6C286E29E6D22200A7D4F1 /* AggregationIntegrationTests.swift in Sources */, 062072B82773A055001655D7 /* AsyncAwaitIntegrationTests.swift in Sources */, B896E5DE1CC27347FAC009C3 /* BasicCompileTests.swift in Sources */, - A0B6EA2529C0090CF0AE7F09 /* CMakeCCompilerId.c in Sources */, 722F9A798F39F7D1FE7CF270 /* CodableGeoPointTests.swift in Sources */, CF5DE1ED21DD0A9783383A35 /* CodableIntegrationTests.swift in Sources */, 32B0739404FA588608E1F41A /* CodableTimestampTests.swift in Sources */, @@ -5448,7 +5305,6 @@ EF6C286D29E6D22200A7D4F1 /* AggregationIntegrationTests.swift in Sources */, 062072B72773A055001655D7 /* AsyncAwaitIntegrationTests.swift in Sources */, F731A0CCD0220B370BC1BE8B /* BasicCompileTests.swift in Sources */, - 9366DC0A832BD2EE267BF61F /* CMakeCCompilerId.c in Sources */, 7C5E017689012489AAB7718D /* CodableGeoPointTests.swift in Sources */, 54C3242322D3B627000FE6DD /* CodableIntegrationTests.swift in Sources */, 70AB665EB6A473FF6C4CFD31 /* CodableTimestampTests.swift in Sources */, diff --git a/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm b/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm index d303cca7407..29cc2e85325 100644 --- a/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm @@ -41,19 +41,17 @@ - (FIRSnapshotListenOptions *)optionsWithSourceFromCacheAndIncludeMetadataChange } - (void)testCanRaiseSnapshotFromCacheForQuery { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a"}}]; [self readDocumentSetForRef:collRef]; // populate the cache. FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; id registration = - [query addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; + [collRef addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a"} ])); XCTAssertEqual(querySnap.metadata.isFromCache, YES); [self.eventAccumulator assertNoAdditionalEvents]; @@ -62,7 +60,7 @@ - (void)testCanRaiseSnapshotFromCacheForQuery { - (void)testCanRaiseSnapshotFromCacheForDocumentReference { FIRDocumentReference *docRef = [self documentRef]; - [docRef setData:@{@"k" : @"a", @"sort" : @0}]; + [docRef setData:@{@"k" : @"a"}]; [self readDocumentForRef:docRef]; // populate the cache. @@ -72,7 +70,7 @@ - (void)testCanRaiseSnapshotFromCacheForDocumentReference { listener:self.eventAccumulator.valueEventHandler]; FIRDocumentSnapshot *docSnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(docSnap.data, (@{@"k" : @"a", @"sort" : @0L})); + XCTAssertEqualObjects(docSnap.data, (@{@"k" : @"a"})); XCTAssertEqual(docSnap.metadata.isFromCache, YES); [self.eventAccumulator assertNoAdditionalEvents]; @@ -80,19 +78,17 @@ - (void)testCanRaiseSnapshotFromCacheForDocumentReference { } - (void)testListenToCacheShouldNotBeAffectedByOnlineStatusChange { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a"}}]; [self readDocumentSetForRef:collRef]; // populate the cache. FIRSnapshotListenOptions *options = [self optionsWithSourceFromCacheAndIncludeMetadataChanges]; id registration = - [query addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; + [collRef addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a"} ])); XCTAssertEqual(querySnap.metadata.isFromCache, YES); [self disableNetwork]; @@ -103,13 +99,15 @@ - (void)testListenToCacheShouldNotBeAffectedByOnlineStatusChange { } - (void)testMultipleListenersSourcedFromCacheCanWorkIndependently { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; - + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0}, + @"b" : @{@"k" : @"b", @"sort" : @1} + }]; [self readDocumentSetForRef:collRef]; // populate the cache. + FIRQuery *query = [[collRef queryWhereField:@"sort" + isGreaterThan:@0] queryOrderedByField:@"sort"]; + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; id registration1 = [query addSnapshotListenerWithOptions:options @@ -119,34 +117,36 @@ - (void)testMultipleListenersSourcedFromCacheCanWorkIndependently { listener:self.eventAccumulator.valueEventHandler]; FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + NSArray *expected = @[ @{@"k" : @"b", @"sort" : @1L} ]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); XCTAssertEqual(querySnap.metadata.isFromCache, YES); querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); XCTAssertEqual(querySnap.metadata.isFromCache, YES); // Do a local mutation - [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @1}]; + [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @2}]; querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), - (@[ @{@"k" : @"a", @"sort" : @0L}, @{@"k" : @"b", @"sort" : @1L} ])); + expected = @[ @{@"k" : @"b", @"sort" : @1L}, @{@"k" : @"c", @"sort" : @2L} ]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); XCTAssertEqual(querySnap.metadata.isFromCache, YES); + querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), - (@[ @{@"k" : @"a", @"sort" : @0L}, @{@"k" : @"b", @"sort" : @1L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); XCTAssertEqual(querySnap.metadata.isFromCache, YES); // Detach one listener, and do a local mutation. The other listener // should not be affected. [registration1 remove]; - [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @2}]; + [self addDocumentRef:collRef data:@{@"k" : @"d", @"sort" : @3}]; querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects( - FIRQuerySnapshotGetData(querySnap), (@[ - @{@"k" : @"a", @"sort" : @0L}, @{@"k" : @"b", @"sort" : @1L}, @{@"k" : @"c", @"sort" : @2L} - ])); + expected = @[ + @{@"k" : @"b", @"sort" : @1L}, @{@"k" : @"c", @"sort" : @2L}, @{@"k" : @"d", @"sort" : @3L} + ]; + + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); XCTAssertEqual(querySnap.metadata.isFromCache, YES); [self.eventAccumulator assertNoAdditionalEvents]; @@ -167,11 +167,11 @@ - (void)testListenUnlistenRelistenToMirrorQueriesFromCache { }]; [self readDocumentSetForRef:collRef]; // populate the cache. - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; // Setup a `limit` query. FIRQuery *limit = [[collRef queryOrderedByField:@"sort" descending:NO] queryLimitedTo:2]; FSTEventAccumulator *limitAccumulator = [FSTEventAccumulator accumulatorForTest:self]; + FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; id limitRegistration = [limit addSnapshotListenerWithOptions:options listener:limitAccumulator.valueEventHandler]; @@ -191,7 +191,7 @@ - (void)testListenUnlistenRelistenToMirrorQueriesFromCache { expected = @[ @{@"k" : @"b", @"sort" : @1}, @{@"k" : @"a", @"sort" : @0} ]; XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); - // Unlisten then re-listen to the limit query. + // Un-listen then re-listen to the limit query. [limitRegistration remove]; limitRegistration = [limit addSnapshotListenerWithOptions:options listener:limitAccumulator.valueEventHandler]; @@ -213,7 +213,7 @@ - (void)testListenUnlistenRelistenToMirrorQueriesFromCache { XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); XCTAssertEqual(snapshot.metadata.hasPendingWrites, YES); - // Unlisten to limitToLast, update a doc, then relisten to limitToLast + // Un-listen to limitToLast, update a doc, then re-listen to limitToLast [limitToLastRegistration remove]; [self updateDocumentRef:[collRef documentWithPath:@"a"] data:@{@"k" : @"a", @"sort" : @-2}]; limitToLastRegistration = @@ -234,16 +234,19 @@ - (void)testListenUnlistenRelistenToMirrorQueriesFromCache { } - (void)testCanListenToDefaultSourceFirstAndThenCache { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0}, + @"b" : @{@"k" : @"b", @"sort" : @1} + }]; + FIRQuery *query = [[collRef queryWhereField:@"sort" + isGreaterThanOrEqualTo:@1] queryOrderedByField:@"sort"]; // Listen to the query with default options, which will also populates the cache FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; id defaultRegistration = [query addSnapshotListener:defaultAccumulator.valueEventHandler]; FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); XCTAssertEqual(querySnap.metadata.isFromCache, NO); // Listen to the same query from cache @@ -252,7 +255,7 @@ - (void)testCanListenToDefaultSourceFirstAndThenCache { id cacheRegistration = [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); // The metadata is sync with server due to the default listener XCTAssertEqual(querySnap.metadata.isFromCache, NO); @@ -263,9 +266,12 @@ - (void)testCanListenToDefaultSourceFirstAndThenCache { } - (void)testCanListenToCacheSourceFirstAndThenDefault { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0}, + @"b" : @{@"k" : @"b", @"sort" : @1} + }]; + FIRQuery *query = [[collRef queryWhereField:@"sort" isNotEqualTo:@0] queryOrderedByField:@"sort"]; + // Listen to the cache FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; @@ -281,12 +287,12 @@ - (void)testCanListenToCacheSourceFirstAndThenDefault { id defaultRegistration = [query addSnapshotListener:defaultAccumulator.valueEventHandler]; querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); XCTAssertEqual(querySnap.metadata.isFromCache, NO); // Default listener updates the cache, whish triggers cache listener to raise snapshot. querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); // The metadata is sync with server due to the default listener XCTAssertEqual(querySnap.metadata.isFromCache, NO); @@ -297,27 +303,29 @@ - (void)testCanListenToCacheSourceFirstAndThenDefault { } - (void)testWillNotGetMetadataOnlyUpdatesIfListeningToCacheOnly { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0}, + @"b" : @{@"k" : @"b", @"sort" : @1} + }]; [self readDocumentSetForRef:collRef]; // populate the cache. + FIRQuery *query = [[collRef queryWhereField:@"sort" isNotEqualTo:@0] queryOrderedByField:@"sort"]; FIRSnapshotListenOptions *options = [self optionsWithSourceFromCacheAndIncludeMetadataChanges]; id registration = [query addSnapshotListenerWithOptions:options listener:self.eventAccumulator.valueEventHandler]; FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); XCTAssertEqual(querySnap.metadata.isFromCache, YES); // Do a local mutation - [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @1}]; + [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @2}]; querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), - (@[ @{@"k" : @"a", @"sort" : @0L}, @{@"k" : @"b", @"sort" : @1L} ])); + (@[ @{@"k" : @"b", @"sort" : @1L}, @{@"k" : @"c", @"sort" : @2L} ])); XCTAssertEqual(querySnap.metadata.isFromCache, YES); XCTAssertEqual(querySnap.metadata.hasPendingWrites, YES); @@ -328,19 +336,22 @@ - (void)testWillNotGetMetadataOnlyUpdatesIfListeningToCacheOnly { } - (void)testWillHaveSynceMetadataUpdatesWhenListeningToBothCacheAndDefaultSource { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; - + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0}, + @"b" : @{@"k" : @"b", @"sort" : @1} + }]; [self readDocumentSetForRef:collRef]; // populate the cache. + FIRQuery *query = [[collRef queryWhereField:@"sort" isNotEqualTo:@0] queryOrderedByField:@"sort"]; + // Listen to the cache FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; FIRSnapshotListenOptions *options = [self optionsWithSourceFromCacheAndIncludeMetadataChanges]; id cacheRegistration = [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; FIRQuerySnapshot *querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + NSArray *expected = @[ @{@"k" : @"b", @"sort" : @1L} ]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); XCTAssertEqual(querySnap.metadata.isFromCache, YES); // Listen to the same query from server @@ -349,7 +360,7 @@ - (void)testWillHaveSynceMetadataUpdatesWhenListeningToBothCacheAndDefaultSource [query addSnapshotListenerWithIncludeMetadataChanges:YES listener:defaultAccumulator.valueEventHandler]; querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); // First snapshot will be raised from cache. XCTAssertEqual(querySnap.metadata.isFromCache, YES); querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; @@ -363,10 +374,10 @@ - (void)testWillHaveSynceMetadataUpdatesWhenListeningToBothCacheAndDefaultSource XCTAssertEqual(querySnap.metadata.isFromCache, NO); // Do a local mutation - [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @1}]; + [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @2}]; // snapshot gets triggered by local mutation - NSArray *expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"b", @"sort" : @1} ]; + expected = @[ @{@"k" : @"b", @"sort" : @1}, @{@"k" : @"c", @"sort" : @2} ]; querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); XCTAssertEqual(querySnap.metadata.hasPendingWrites, YES); @@ -391,9 +402,11 @@ - (void)testWillHaveSynceMetadataUpdatesWhenListeningToBothCacheAndDefaultSource } - (void)testCanUnlistenToDefaultSourceWhileStillListeningToCache { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0}, + @"b" : @{@"k" : @"b", @"sort" : @1} + }]; + FIRQuery *query = [[collRef queryWhereField:@"sort" isNotEqualTo:@0] queryOrderedByField:@"sort"]; // Listen to the query with both source options FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; @@ -410,21 +423,23 @@ - (void)testCanUnlistenToDefaultSourceWhileStillListeningToCache { [defaultRegistration remove]; // Add a document and verify listener to cache works as expected - [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @-1}]; + [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @-1}]; [defaultAccumulator assertNoAdditionalEvents]; FIRQuerySnapshot *querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), - (@[ @{@"k" : @"b", @"sort" : @-1L}, @{@"k" : @"a", @"sort" : @0L} ])); + (@[ @{@"k" : @"c", @"sort" : @-1L}, @{@"k" : @"b", @"sort" : @1L} ])); [cacheAccumulator assertNoAdditionalEvents]; [cacheRegistration remove]; } - (void)testCanUnlistenToCacheSourceWhileStillListeningToServer { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0}, + @"b" : @{@"k" : @"b", @"sort" : @1} + }]; + FIRQuery *query = [[collRef queryWhereField:@"sort" isNotEqualTo:@0] queryOrderedByField:@"sort"]; // Listen to the query with both source options FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; @@ -441,28 +456,33 @@ - (void)testCanUnlistenToCacheSourceWhileStillListeningToServer { [cacheRegistration remove]; // Add a document and verify listener to server works as expected. - [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @-1}]; + [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @-1}]; [cacheAccumulator assertNoAdditionalEvents]; FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), - (@[ @{@"k" : @"b", @"sort" : @-1L}, @{@"k" : @"a", @"sort" : @0L} ])); + (@[ @{@"k" : @"c", @"sort" : @-1L}, @{@"k" : @"b", @"sort" : @1L} ])); [defaultAccumulator assertNoAdditionalEvents]; [defaultRegistration remove]; } - (void)testCanListenUnlistenRelistenToSameQueryWithDifferentSourceOptions { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0}, + @"b" : @{@"k" : @"b", @"sort" : @1} + }]; + FIRQuery *query = [[collRef queryWhereField:@"sort" + isGreaterThan:@0] queryOrderedByField:@"sort"]; // Listen to the query with default options, which will also populates the cache FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; id defaultRegistration = [query addSnapshotListener:defaultAccumulator.valueEventHandler]; FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + NSArray *expected = @[ @{@"k" : @"b", @"sort" : @1L} ]; + + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); // Listen to the same query from cache FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; @@ -470,14 +490,14 @@ - (void)testCanListenUnlistenRelistenToSameQueryWithDifferentSourceOptions { id cacheRegistration = [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); // Un-listen to the default listener, add a doc and re-listen. [defaultRegistration remove]; - [self addDocumentRef:collRef data:@{@"k" : @"b", @"sort" : @1}]; - NSArray *expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"b", @"sort" : @1} ]; + [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @2}]; querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; + expected = @[ @{@"k" : @"b", @"sort" : @1}, @{@"k" : @"c", @"sort" : @2} ]; XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); defaultRegistration = [query addSnapshotListener:defaultAccumulator.valueEventHandler]; @@ -486,10 +506,10 @@ - (void)testCanListenUnlistenRelistenToSameQueryWithDifferentSourceOptions { // Un-listen to cache, update a doc, then re-listen to cache. [cacheRegistration remove]; - [self updateDocumentRef:[collRef documentWithPath:@"a"] data:@{@"k" : @"a", @"sort" : @2}]; - expected = @[ @{@"k" : @"b", @"sort" : @1}, @{@"k" : @"a", @"sort" : @2} ]; + [self updateDocumentRef:[collRef documentWithPath:@"b"] data:@{@"k" : @"b", @"sort" : @3}]; querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; + expected = @[ @{@"k" : @"c", @"sort" : @2}, @{@"k" : @"b", @"sort" : @3} ]; XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); cacheRegistration = [query addSnapshotListenerWithOptions:options @@ -504,8 +524,10 @@ - (void)testCanListenUnlistenRelistenToSameQueryWithDifferentSourceOptions { } - (void)testCanListenToCompositeIndexQueriesFromCache { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0}, + @"b" : @{@"k" : @"b", @"sort" : @1} + }]; [self readDocumentSetForRef:collRef]; // populate the cache. FIRQuery *query = [[collRef queryWhereField:@"k" isLessThanOrEqualTo:@"a"] queryWhereField:@"sort" @@ -570,16 +592,19 @@ - (void)testWillNotBeTriggeredByTransactionsWhileListeningToCache { } - (void)testSharesServerSideUpdatesWhenListeningToBothCacheAndDefault { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - FIRQuery *query = [collRef queryOrderedByField:@"sort"]; + FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ + @"a" : @{@"k" : @"a", @"sort" : @0}, + @"b" : @{@"k" : @"b", @"sort" : @1} + }]; + FIRQuery *query = [[collRef queryWhereField:@"sort" + isGreaterThan:@0] queryOrderedByField:@"sort"]; // Listen to the query with default options, which will also populates the cache FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; id defaultRegistration = [query addSnapshotListener:defaultAccumulator.valueEventHandler]; FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); // Listen to the same query from cache FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; @@ -587,14 +612,14 @@ - (void)testSharesServerSideUpdatesWhenListeningToBothCacheAndDefault { id cacheRegistration = [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); // Use a transaction to mock server side updates FIRDocumentReference *docRef = [collRef documentWithAutoID]; // Use a transaction to perform a write without triggering any local events. [docRef.firestore runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **) { - [transaction setData:@{@"k" : @"b", @"sort" : @1} forDocument:docRef]; + [transaction setData:@{@"k" : @"c", @"sort" : @2} forDocument:docRef]; return nil; } completion:^(id, NSError *){ @@ -602,7 +627,7 @@ - (void)testSharesServerSideUpdatesWhenListeningToBothCacheAndDefault { // Default listener receives the server update querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - NSArray *expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"b", @"sort" : @1} ]; + NSArray *expected = @[ @{@"k" : @"b", @"sort" : @1}, @{@"k" : @"c", @"sort" : @2} ]; XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); XCTAssertEqual(querySnap.metadata.isFromCache, NO); diff --git a/Firestore/core/src/core/event_manager.cc b/Firestore/core/src/core/event_manager.cc index 3e1d419a349..91cc847d066 100644 --- a/Firestore/core/src/core/event_manager.cc +++ b/Firestore/core/src/core/event_manager.cc @@ -41,10 +41,9 @@ model::TargetId EventManager::AddQueryListener( ListenerSetupAction::NoSetupActionRequired; auto inserted = queries_.emplace(query, QueryListenersInfo{}); - bool first_listen = inserted.second; QueryListenersInfo& query_info = inserted.first->second; - if (first_listen) { + if (inserted.second) { listenerAction = listener->listens_to_remote_store() ? ListenerSetupAction:: InitializeLocalListenAndRequireWatchConnection @@ -121,7 +120,7 @@ void EventManager::RemoveQueryListener( case ListenerRemovalAction::TerminateLocalListenOnly: queries_.erase(found_iter); return query_event_source_->StopListening( - query, /** disableRemoteListen= */ true); + query, /** disableRemoteListen= */ false); case ListenerRemovalAction::RequireWatchDisconnectionOnly: return query_event_source_->StopListeningToRemoteStore(query); default: From 35446a03c2895e7f15bd203a33fab299d7aa4ea7 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 15 Feb 2024 14:45:39 -0500 Subject: [PATCH 09/17] add changelog --- Firestore/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index c05338877fd..eba15b5f166 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,3 +1,6 @@ +# Unreleased +- [feature] Enable snapshot listeners to source from local cache. (#12370) + # 10.21.0 - Add an error when trying to build Firestore's binary SPM distribution for visionOS (#12279). See Firestore's 10.12.0 release note for a supported From e076d7a787b5a5588a35882d2ccf7693106c8886 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 20 Feb 2024 16:19:52 -0500 Subject: [PATCH 10/17] add comments --- .../FIRSnapshotListenOptions.h | 15 ------- Firestore/CHANGELOG.md | 2 +- .../Tests/SpecTests/FSTSyncEngineTestDriver.h | 1 + .../FirebaseFirestore/FIRDocumentReference.h | 4 +- .../Public/FirebaseFirestore/FIRQuery.h | 4 +- .../FIRSnapshotListenOptions.h | 44 +++++++++++++++++++ Firestore/core/src/api/listen_source.h | 4 +- Firestore/core/src/core/event_manager.cc | 34 +++++++------- Firestore/core/src/core/listen_options.h | 4 +- Firestore/core/src/core/sync_engine.cc | 29 ++++++++++-- Firestore/core/src/core/sync_engine.h | 26 ++++++++--- 11 files changed, 115 insertions(+), 52 deletions(-) delete mode 100644 FirebaseFirestoreInternal/FirebaseFirestore/FIRSnapshotListenOptions.h diff --git a/FirebaseFirestoreInternal/FirebaseFirestore/FIRSnapshotListenOptions.h b/FirebaseFirestoreInternal/FirebaseFirestore/FIRSnapshotListenOptions.h deleted file mode 100644 index a98dcc88a8a..00000000000 --- a/FirebaseFirestoreInternal/FirebaseFirestore/FIRSnapshotListenOptions.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import \ No newline at end of file diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index fa813ca42f2..e9ffd0feaa8 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,5 +1,5 @@ # Unreleased -- [feature] Enable snapshot listeners to source from local cache. (#12370) +- [feature] Enable snapshot listener option to retrieve data from local cache only. (#12370) - [fixed] Fix the flaky offline behaviour when using `arrayRemove` on `Map` object. (#12378) # 10.21.0 diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h index 62794f2609a..978ae28a4e5 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h @@ -146,6 +146,7 @@ typedef std:: * Resulting events are captured and made available via the capturedEventsSinceLastCall method. * * @param query A valid query to execute against the backend. + * @param options A listen option to configure snapshot listener. * @return The target ID assigned by the system to track the query. */ - (model::TargetId)addUserListenerWithQuery:(core::Query)query options:(core::ListenOptions)options; diff --git a/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h b/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h index edbbb113ac3..b6f87450cb3 100644 --- a/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h +++ b/Firestore/Source/Public/FirebaseFirestore/FIRDocumentReference.h @@ -274,8 +274,8 @@ addSnapshotListenerWithIncludeMetadataChanges:(BOOL)includeMetadataChanges /** * Attaches a listener for `DocumentSnapshot` events. * - * @param options Sets snapshot listen options, including whether metadata-only changes should - * trigger snapshot events, the source that listens to, the executor to use to call the + * @param options Sets snapshot listener options, including whether metadata-only changes should + * trigger snapshot events, the source to listen to, the executor to use to call the * listener, or the activity to scope the listener to. * @param listener The listener to attach. * diff --git a/Firestore/Source/Public/FirebaseFirestore/FIRQuery.h b/Firestore/Source/Public/FirebaseFirestore/FIRQuery.h index c60f2d65438..c75952876a2 100644 --- a/Firestore/Source/Public/FirebaseFirestore/FIRQuery.h +++ b/Firestore/Source/Public/FirebaseFirestore/FIRQuery.h @@ -107,8 +107,8 @@ NS_SWIFT_NAME(Query) /** * Attaches a listener for `QuerySnapshot` events. - * @param options Sets snapshot listen options, including whether metadata-only changes should - * trigger snapshot events, the source that listens to, the executor to use to call the + * @param options Sets snapshot listener options, including whether metadata-only changes should + * trigger snapshot events, the source to listen to, the executor to use to call the * listener, or the activity to scope the listener to. * @param listener The listener to attach. * diff --git a/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h b/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h index cdf0d31d129..17c359256f1 100644 --- a/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h +++ b/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h @@ -18,20 +18,64 @@ NS_ASSUME_NONNULL_BEGIN +/** + * The source that snapshot listener retrieves data from. + */ typedef NS_ENUM(NSUInteger, FIRListenSource) { + /** + * The default behavior. The listener attempts to return initial snapshot from cache and retrieve + * up-to-date snapshots from the Firestore server. Snapshot events will be triggered on local + * mutations and server side updates. + */ FIRListenSourceDefault, + /** + * The listener retrieves data and listens to updates from the local Firestore cache only. If the + * cache is empty, an empty snapshot will be returned. Snapshot events will be triggered on cache + * updates, like local mutations or load bundles. + * + * Note that the data might be stale if the cache hasn't synchronized with recent server-side + * changes. + */ FIRListenSourceCache } NS_SWIFT_NAME(ListenSource); +/** + * Options to configure the behavior of `Firestore.addSnapshotListenerWithOptions()`. Instances + * of this class control settings like whether metadata-only changes trigger events and the + * preferred data source. + */ NS_SWIFT_NAME(SnapshotListenOptions) @interface FIRSnapshotListenOptions : NSObject +/** The source that snapshot listener retrieves data from. */ @property(nonatomic, readonly) FIRListenSource source; +/** Indicates whether metadata-only changes should trigger snapshot events. */ @property(nonatomic, readonly) BOOL includeMetadataChanges; +/** + * Creates and returns a new `SnapshotListenOptions` object with all properties initialized to their + * default values. + * + * @return The created `SnapshotListenOptions` object. + */ - (instancetype)init NS_DESIGNATED_INITIALIZER; +/** + * Creates and returns a new `SnapshotListenOptions` object with with all properties of the current + * `SnapshotListenOptions` object and the new configuration of whether metadata-only changes should + * trigger snapshot events. + * + * @return The created `SnapshotListenOptions` object. + */ - (FIRSnapshotListenOptions *)optionsWithIncludeMetadataChanges:(BOOL)includeMetadataChanges; + +/** + * Creates and returns a new `SnapshotListenOptions` object with with all properties of the current + * `SnapshotListenOptions` object and the new configuration of the source that snapshot listener + * listens to. + * + * @return The created `SnapshotListenOptions` object. + */ - (FIRSnapshotListenOptions *)optionsWithSource:(FIRListenSource)source; @end diff --git a/Firestore/core/src/api/listen_source.h b/Firestore/core/src/api/listen_source.h index 44387f77128..2c062f84c52 100644 --- a/Firestore/core/src/api/listen_source.h +++ b/Firestore/core/src/api/listen_source.h @@ -22,8 +22,8 @@ namespace firestore { namespace api { /** - * An enum that configures the source of snapshot listeners listening to. By - * providing a source enum, listener raises snapshot from local cache changes + * An enum that configures the source of snapshot listeners listens to. By + * providing a source enum, snapshot events triggered by local cache changes * only, or from both local cache and watch changes(which is the default). * * See `FIRFirestoreListenSource` for more details. diff --git a/Firestore/core/src/core/event_manager.cc b/Firestore/core/src/core/event_manager.cc index 91cc847d066..4fd9f2899f3 100644 --- a/Firestore/core/src/core/event_manager.cc +++ b/Firestore/core/src/core/event_manager.cc @@ -37,22 +37,22 @@ EventManager::EventManager(QueryEventSource* query_event_source) model::TargetId EventManager::AddQueryListener( std::shared_ptr listener) { const Query& query = listener->query(); - ListenerSetupAction listenerAction = + ListenerSetupAction listener_action = ListenerSetupAction::NoSetupActionRequired; auto inserted = queries_.emplace(query, QueryListenersInfo{}); QueryListenersInfo& query_info = inserted.first->second; if (inserted.second) { - listenerAction = listener->listens_to_remote_store() - ? ListenerSetupAction:: - InitializeLocalListenAndRequireWatchConnection - : ListenerSetupAction::InitializeLocalListenOnly; + listener_action = listener->listens_to_remote_store() + ? ListenerSetupAction:: + InitializeLocalListenAndRequireWatchConnection + : ListenerSetupAction::InitializeLocalListenOnly; } else if (!query_info.has_remote_listeners() && listener->listens_to_remote_store()) { // Query has been listening to local cache, and tries to add a new listener // sourced from watch. - listenerAction = ListenerSetupAction::RequireWatchConnectionOnly; + listener_action = ListenerSetupAction::RequireWatchConnectionOnly; } query_info.listeners.push_back(listener); @@ -69,14 +69,14 @@ model::TargetId EventManager::AddQueryListener( } } - switch (listenerAction) { + switch (listener_action) { case ListenerSetupAction::InitializeLocalListenAndRequireWatchConnection: - query_info.target_id = - query_event_source_->Listen(query, /** enableRemoteListen= */ true); + query_info.target_id = query_event_source_->Listen( + query, /** should_listen_to_remote= */ true); break; case ListenerSetupAction::InitializeLocalListenOnly: - query_info.target_id = - query_event_source_->Listen(query, /** enableRemoteListen= */ false); + query_info.target_id = query_event_source_->Listen( + query, /** should_listen_to_remote= */ false); break; case ListenerSetupAction::RequireWatchConnectionOnly: query_event_source_->ListenToRemoteStore(query); @@ -90,7 +90,7 @@ model::TargetId EventManager::AddQueryListener( void EventManager::RemoveQueryListener( std::shared_ptr listener) { const Query& query = listener->query(); - ListenerRemovalAction listenerAction = + ListenerRemovalAction listener_action = ListenerRemovalAction::NoRemovalActionRequired; auto found_iter = queries_.find(query); @@ -99,7 +99,7 @@ void EventManager::RemoveQueryListener( query_info.Erase(listener); if (query_info.listeners.empty()) { - listenerAction = + listener_action = listener->listens_to_remote_store() ? ListenerRemovalAction:: TerminateLocalListenAndRequireWatchDisconnection @@ -107,20 +107,20 @@ void EventManager::RemoveQueryListener( } else if (!query_info.has_remote_listeners() && listener->listens_to_remote_store()) { // The removed listener is the last one that sourced from watch. - listenerAction = ListenerRemovalAction::RequireWatchDisconnectionOnly; + listener_action = ListenerRemovalAction::RequireWatchDisconnectionOnly; } } - switch (listenerAction) { + switch (listener_action) { case ListenerRemovalAction:: TerminateLocalListenAndRequireWatchDisconnection: queries_.erase(found_iter); return query_event_source_->StopListening( - query, /** disableRemoteListen= */ true); + query, /** should_unlisten_to_remote= */ true); case ListenerRemovalAction::TerminateLocalListenOnly: queries_.erase(found_iter); return query_event_source_->StopListening( - query, /** disableRemoteListen= */ false); + query, /** should_unlisten_to_remote= */ false); case ListenerRemovalAction::RequireWatchDisconnectionOnly: return query_event_source_->StopListeningToRemoteStore(query); default: diff --git a/Firestore/core/src/core/listen_options.h b/Firestore/core/src/core/listen_options.h index 76177b74a9e..2499b75e224 100644 --- a/Firestore/core/src/core/listen_options.h +++ b/Firestore/core/src/core/listen_options.h @@ -56,7 +56,7 @@ class ListenOptions { * documents changes. * @param wait_for_sync_when_online Wait for a sync with the server when * online, but still raise events while offline. - * @param source sets the source snapshot listeners listen to. + * @param source sets the source a snapshot listener listens to. */ ListenOptions(bool include_query_metadata_changes, bool include_document_metadata_changes, @@ -94,7 +94,7 @@ class ListenOptions { } /** - * Creates a ListenOptions which sets the source snapshot listeners listen to. + * Creates a ListenOptions which sets the source snapshot listener listens to. */ static ListenOptions FromOptions(bool include_metadata_changes, ListenSource source) { diff --git a/Firestore/core/src/core/sync_engine.cc b/Firestore/core/src/core/sync_engine.cc index c1e5a96e241..24db4031dd0 100644 --- a/Firestore/core/src/core/sync_engine.cc +++ b/Firestore/core/src/core/sync_engine.cc @@ -104,7 +104,7 @@ void SyncEngine::AssertCallbackExists(absl::string_view source) { "Tried to call '%s' before callback was registered.", source); } -TargetId SyncEngine::Listen(Query query, bool shouldListenToRemote) { +TargetId SyncEngine::Listen(Query query, bool should_listen_to_remote) { AssertCallbackExists("Listen"); HARD_ASSERT(query_views_by_query_.find(query) == query_views_by_query_.end(), @@ -121,7 +121,7 @@ TargetId SyncEngine::Listen(Query query, bool shouldListenToRemote) { snapshots.push_back(std::move(view_snapshot)); sync_engine_callback_->OnViewSnapshots(std::move(snapshots)); - if (shouldListenToRemote) { + if (should_listen_to_remote) { remote_store_->Listen(std::move(target_data)); } return target_id; @@ -170,7 +170,7 @@ void SyncEngine::ListenToRemoteStore(Query query) { } void SyncEngine::StopListening(const Query& query, - bool shouldUnlistenToRemote) { + bool should_unlisten_to_remote) { AssertCallbackExists("StopListening"); auto query_view = query_views_by_query_[query]; @@ -185,7 +185,7 @@ void SyncEngine::StopListening(const Query& query, if (queries.empty()) { local_store_->ReleaseTarget(target_id); - if (shouldUnlistenToRemote) { + if (should_unlisten_to_remote) { remote_store_->StopListening(target_id); } RemoveAndCleanupTarget(target_id, Status::OK()); @@ -207,6 +207,27 @@ void SyncEngine::StopListeningToRemoteStore(const Query& query) { remote_store_->StopListening(target_id); } } +// void SyncEngine::StopListeningAndReleaseTarget(const Query& query, bool +// should_stop_listening, bool should_release_target) { +// +// auto query_view = query_views_by_query_[query]; +// HARD_ASSERT(query_view, "Trying to stop listening to a query not found"); +// +// query_views_by_query_.erase(query); +// +// TargetId target_id = query_view->target_id(); +// auto& queries = queries_by_target_[target_id]; +// queries.erase(std::remove(queries.begin(), queries.end(), query), +// queries.end()); +// +// if (queries.empty()) { +// local_store_->ReleaseTarget(target_id); +// if (should_unlisten_to_remote) { +// remote_store_->StopListening(target_id); +// } +// RemoveAndCleanupTarget(target_id, Status::OK()); +// } +// } void SyncEngine::RemoveAndCleanupTarget(TargetId target_id, Status status) { for (const Query& query : queries_by_target_.at(target_id)) { diff --git a/Firestore/core/src/core/sync_engine.h b/Firestore/core/src/core/sync_engine.h index dda14569e19..44e1d8b4f25 100644 --- a/Firestore/core/src/core/sync_engine.h +++ b/Firestore/core/src/core/sync_engine.h @@ -70,20 +70,32 @@ class QueryEventSource { /** * Initiates a new listen. The LocalStore will be queried for initial data - * and the listen will be sent to the `RemoteStore` to get remote data. The - * registered SyncEngineCallback will be notified of resulting view + * and the listen will be sent to the RemoteStore if the query is listening to + * watch. The registered SyncEngineCallback will be notified of resulting view * snapshots and/or listen errors. * * @return the target ID assigned to the query. */ - virtual model::TargetId Listen(Query query, bool shouldListenToRemote) = 0; + virtual model::TargetId Listen(Query query, bool should_listen_to_remote) = 0; + /** + * Sends the listen to the RemoteStore to get remote data. Invoked when a + * Query starts listening to the remote store, while already listening to the + * cache. + */ virtual void ListenToRemoteStore(Query query) = 0; - /** Stops listening to a query previously listened to via `Listen`. */ + /** + * Stops listening to a query previously listened to via `Listen`. Un-listen + * to remote store if there is a watch connection established and stayed open. + */ virtual void StopListening(const Query& query, - bool shouldUnlistenToRemote) = 0; + bool should_unlisten_to_remote) = 0; + /** + * Stops listening to a query from watch. Invoked when a Query stops listening + * to the remote store, while still listening to the cache. + */ virtual void StopListeningToRemoteStore(const Query& query) = 0; }; @@ -113,10 +125,10 @@ class SyncEngine : public remote::RemoteStoreCallback, public QueryEventSource { sync_engine_callback_ = callback; } model::TargetId Listen(Query query, - bool shouldListenToRemote = true) override; + bool should_listen_to_remote = true) override; void ListenToRemoteStore(Query query) override; void StopListening(const Query& query, - bool shouldUnlistenToRemote = true) override; + bool should_unlisten_to_remote = true) override; void StopListeningToRemoteStore(const Query& query) override; /** From f39b8af151e38ce33395947de96e4fb619419d8e Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:18:07 -0500 Subject: [PATCH 11/17] Update project.pbxproj --- .../Firestore.xcodeproj/project.pbxproj | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index b7a33bfff9e..77f1423ac8e 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -2245,15 +2245,6 @@ name = admin; sourceTree = ""; }; - 475693833C55E80B2C4CA461 /* 3.26.4 */ = { - isa = PBXGroup; - children = ( - DBEB6B536E39F1EC1027C339 /* CompilerIdC */, - 69F99B85B5E15C19EFF6A21F /* CompilerIdCXX */, - ); - name = 3.26.4; - sourceTree = ""; - }; 543B4F0520A91E4B001F506D /* App */ = { isa = PBXGroup; children = ( @@ -2754,13 +2745,6 @@ path = rpc; sourceTree = ""; }; - 69F99B85B5E15C19EFF6A21F /* CompilerIdCXX */ = { - isa = PBXGroup; - children = ( - ); - name = CompilerIdCXX; - sourceTree = ""; - }; 6EA39FDC20FE81DD008D461F /* FuzzingTargets */ = { isa = PBXGroup; children = ( @@ -2918,21 +2902,6 @@ path = macOS; sourceTree = ""; }; - DBEB6B536E39F1EC1027C339 /* CompilerIdC */ = { - isa = PBXGroup; - children = ( - ); - name = CompilerIdC; - sourceTree = ""; - }; - DC6A402639629C05EF451D8C /* cmake-build-debug */ = { - isa = PBXGroup; - children = ( - E3277B52449F042F11FDA06A /* CMakeFiles */, - ); - name = "cmake-build-debug"; - sourceTree = ""; - }; DE2EF06E1F3D07D7003D0CDC /* Immutable */ = { isa = PBXGroup; children = ( @@ -3062,14 +3031,6 @@ path = API; sourceTree = ""; }; - E3277B52449F042F11FDA06A /* CMakeFiles */ = { - isa = PBXGroup; - children = ( - 475693833C55E80B2C4CA461 /* 3.26.4 */, - ); - name = CMakeFiles; - sourceTree = ""; - }; F7BA529161F1713BDF685C65 /* bundle */ = { isa = PBXGroup; children = ( From 88f586b6eb43b0f76b6f03fea6086bc6f4e17eea Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:19:15 -0500 Subject: [PATCH 12/17] Update project.pbxproj --- Firestore/Example/Firestore.xcodeproj/project.pbxproj | 1 - 1 file changed, 1 deletion(-) diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 77f1423ac8e..a8af14c2d5c 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -2998,7 +2998,6 @@ isa = PBXGroup; children = ( DE51B1BC1F0D48AC0013853F /* API */, - DC6A402639629C05EF451D8C /* cmake-build-debug */, 5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */, 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */, 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */, From 6abda8bd7e491b4463134448781c9009b4b5a728 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 26 Feb 2024 13:32:06 -0500 Subject: [PATCH 13/17] resolve comments --- Firestore/core/src/core/event_manager.cc | 11 ++-- Firestore/core/src/core/sync_engine.cc | 66 +++++++------------ Firestore/core/src/core/sync_engine.h | 11 ++-- .../core/test/unit/core/event_manager_test.cc | 30 ++++++++- 4 files changed, 66 insertions(+), 52 deletions(-) diff --git a/Firestore/core/src/core/event_manager.cc b/Firestore/core/src/core/event_manager.cc index 4fd9f2899f3..d5c3f3542b9 100644 --- a/Firestore/core/src/core/event_manager.cc +++ b/Firestore/core/src/core/event_manager.cc @@ -41,9 +41,12 @@ model::TargetId EventManager::AddQueryListener( ListenerSetupAction::NoSetupActionRequired; auto inserted = queries_.emplace(query, QueryListenersInfo{}); + // If successfully inserted, it means we haven't listened to this query + // before. + bool first_listen = inserted.second; QueryListenersInfo& query_info = inserted.first->second; - if (inserted.second) { + if (first_listen) { listener_action = listener->listens_to_remote_store() ? ListenerSetupAction:: InitializeLocalListenAndRequireWatchConnection @@ -116,13 +119,13 @@ void EventManager::RemoveQueryListener( TerminateLocalListenAndRequireWatchDisconnection: queries_.erase(found_iter); return query_event_source_->StopListening( - query, /** should_unlisten_to_remote= */ true); + query, /** should_stop_remote_listening= */ true); case ListenerRemovalAction::TerminateLocalListenOnly: queries_.erase(found_iter); return query_event_source_->StopListening( - query, /** should_unlisten_to_remote= */ false); + query, /** should_stop_remote_listening= */ false); case ListenerRemovalAction::RequireWatchDisconnectionOnly: - return query_event_source_->StopListeningToRemoteStore(query); + return query_event_source_->StopListeningToRemoteStoreOnly(query); default: return; } diff --git a/Firestore/core/src/core/sync_engine.cc b/Firestore/core/src/core/sync_engine.cc index 24db4031dd0..77223cb1fed 100644 --- a/Firestore/core/src/core/sync_engine.cc +++ b/Firestore/core/src/core/sync_engine.cc @@ -170,64 +170,44 @@ void SyncEngine::ListenToRemoteStore(Query query) { } void SyncEngine::StopListening(const Query& query, - bool should_unlisten_to_remote) { + bool should_stop_remote_listening) { AssertCallbackExists("StopListening"); - - auto query_view = query_views_by_query_[query]; - HARD_ASSERT(query_view, "Trying to stop listening to a query not found"); - - query_views_by_query_.erase(query); - - TargetId target_id = query_view->target_id(); - auto& queries = queries_by_target_[target_id]; - queries.erase(std::remove(queries.begin(), queries.end(), query), - queries.end()); - - if (queries.empty()) { - local_store_->ReleaseTarget(target_id); - if (should_unlisten_to_remote) { - remote_store_->StopListening(target_id); - } - RemoveAndCleanupTarget(target_id, Status::OK()); - } + StopListeningAndReleaseTarget(query, /** last_listen= */ true, + should_stop_remote_listening); } -void SyncEngine::StopListeningToRemoteStore(const Query& query) { - AssertCallbackExists("stopListeningToRemoteStore"); +void SyncEngine::StopListeningToRemoteStoreOnly(const Query& query) { + AssertCallbackExists("StopListeningToRemoteStoreOnly"); + StopListeningAndReleaseTarget(query, /** last_listen= */ false, + /** should_stop_remote_listening= */ true); +} +void SyncEngine::StopListeningAndReleaseTarget( + const Query& query, bool last_listen, bool should_stop_remote_listening) { auto query_view = query_views_by_query_[query]; HARD_ASSERT(query_view, "Trying to stop listening to a query not found"); + if (last_listen) { + query_views_by_query_.erase(query); + } + + // One target could have multiple queries mapped to it. TargetId target_id = query_view->target_id(); auto& queries = queries_by_target_[target_id]; queries.erase(std::remove(queries.begin(), queries.end(), query), queries.end()); - if (queries.empty()) { + if (!queries.empty()) return; + + if (should_stop_remote_listening) { remote_store_->StopListening(target_id); } + + if (last_listen) { + local_store_->ReleaseTarget(target_id); + RemoveAndCleanupTarget(target_id, Status::OK()); + } } -// void SyncEngine::StopListeningAndReleaseTarget(const Query& query, bool -// should_stop_listening, bool should_release_target) { -// -// auto query_view = query_views_by_query_[query]; -// HARD_ASSERT(query_view, "Trying to stop listening to a query not found"); -// -// query_views_by_query_.erase(query); -// -// TargetId target_id = query_view->target_id(); -// auto& queries = queries_by_target_[target_id]; -// queries.erase(std::remove(queries.begin(), queries.end(), query), -// queries.end()); -// -// if (queries.empty()) { -// local_store_->ReleaseTarget(target_id); -// if (should_unlisten_to_remote) { -// remote_store_->StopListening(target_id); -// } -// RemoveAndCleanupTarget(target_id, Status::OK()); -// } -// } void SyncEngine::RemoveAndCleanupTarget(TargetId target_id, Status status) { for (const Query& query : queries_by_target_.at(target_id)) { diff --git a/Firestore/core/src/core/sync_engine.h b/Firestore/core/src/core/sync_engine.h index 44e1d8b4f25..bcf930fdd0c 100644 --- a/Firestore/core/src/core/sync_engine.h +++ b/Firestore/core/src/core/sync_engine.h @@ -90,13 +90,13 @@ class QueryEventSource { * to remote store if there is a watch connection established and stayed open. */ virtual void StopListening(const Query& query, - bool should_unlisten_to_remote) = 0; + bool should_stop_remote_listening) = 0; /** * Stops listening to a query from watch. Invoked when a Query stops listening * to the remote store, while still listening to the cache. */ - virtual void StopListeningToRemoteStore(const Query& query) = 0; + virtual void StopListeningToRemoteStoreOnly(const Query& query) = 0; }; /** @@ -128,8 +128,8 @@ class SyncEngine : public remote::RemoteStoreCallback, public QueryEventSource { bool should_listen_to_remote = true) override; void ListenToRemoteStore(Query query) override; void StopListening(const Query& query, - bool should_unlisten_to_remote = true) override; - void StopListeningToRemoteStore(const Query& query) override; + bool should_stop_remote_listening = true) override; + void StopListeningToRemoteStoreOnly(const Query& query) override; /** * Initiates the write of local mutation batch which involves adding the @@ -265,6 +265,9 @@ class SyncEngine : public remote::RemoteStoreCallback, public QueryEventSource { nanopb::ByteString resume_token); void RemoveAndCleanupTarget(model::TargetId target_id, util::Status status); + void StopListeningAndReleaseTarget(const Query& query, + bool should_stop_remote_listening, + bool last_listen); void RemoveLimboTarget(const model::DocumentKey& key); diff --git a/Firestore/core/test/unit/core/event_manager_test.cc b/Firestore/core/test/unit/core/event_manager_test.cc index 2f9966e78ab..5d48a9aae0c 100644 --- a/Firestore/core/test/unit/core/event_manager_test.cc +++ b/Firestore/core/test/unit/core/event_manager_test.cc @@ -57,13 +57,21 @@ std::shared_ptr NoopQueryListener(core::Query query) { NoopViewSnapshotHandler()); } +std::shared_ptr NoopQueryCacheListener(core::Query query) { + return QueryListener::Create( + std::move(query), + ListenOptions::FromOptions(/** include_metadata_changes= */ false, + ListenSource::Cache), + NoopViewSnapshotHandler()); +} + class MockEventSource : public core::QueryEventSource { public: MOCK_METHOD1(SetCallback, void(core::SyncEngineCallback*)); MOCK_METHOD2(Listen, model::TargetId(core::Query, bool)); MOCK_METHOD1(ListenToRemoteStore, void(core::Query)); MOCK_METHOD2(StopListening, void(const core::Query&, bool)); - MOCK_METHOD1(StopListeningToRemoteStore, void(const core::Query&)); + MOCK_METHOD1(StopListeningToRemoteStoreOnly, void(const core::Query&)); }; TEST(EventManagerTest, HandlesManyListnersPerQuery) { @@ -86,6 +94,26 @@ TEST(EventManagerTest, HandlesManyListnersPerQuery) { event_manager.RemoveQueryListener(listener1); } +TEST(EventManagerTest, HandlesManyCacheListnersPerQuery) { + core::Query query = Query("foo/bar"); + auto listener1 = NoopQueryCacheListener(query); + auto listener2 = NoopQueryCacheListener(query); + + StrictMock mock_event_source; + EXPECT_CALL(mock_event_source, SetCallback(_)); + EventManager event_manager(&mock_event_source); + + EXPECT_CALL(mock_event_source, Listen(query, false)); + event_manager.AddQueryListener(listener1); + + // Expecting no activity from mock_event_source. + event_manager.AddQueryListener(listener2); + event_manager.RemoveQueryListener(listener2); + + EXPECT_CALL(mock_event_source, StopListening(query, false)); + event_manager.RemoveQueryListener(listener1); +} + TEST(EventManagerTest, HandlesUnlistenOnUnknownListenerGracefully) { core::Query query = Query("foo/bar"); auto listener = NoopQueryListener(query); From 3b10dd431fd72a052051348a21357bef1cbf34b7 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:06:49 -0500 Subject: [PATCH 14/17] move objective c tests to swift --- .../Firestore.xcodeproj/project.pbxproj | 16 +- .../API/FIRSnapshotListenerSourceTests.mm | 645 ----------------- .../Tests/Util/FSTIntegrationTestCase.h | 5 + .../Tests/Util/FSTIntegrationTestCase.mm | 16 + Firestore/Swift/Tests/BridgingHeader.h | 1 + .../SnapshotListenerSourceTests.swift | 684 ++++++++++++++++++ 6 files changed, 714 insertions(+), 653 deletions(-) delete mode 100644 Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm create mode 100644 Firestore/Swift/Tests/Integration/SnapshotListenerSourceTests.swift diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index a8af14c2d5c..99f967e3ccd 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -41,7 +41,6 @@ 056542AD1D0F78E29E22EFA9 /* grpc_connection_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D9649021544D4F00EB9CFB /* grpc_connection_test.cc */; }; 0575F3004B896D94456A74CE /* status_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3CAA33F964042646FDDAF9F9 /* status_testing.cc */; }; 0595B5EBEB8F09952B72C883 /* logic_utils_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 28B45B2104E2DAFBBF86DBB7 /* logic_utils_test.cc */; }; - 05BAC479CA3AB13B7A5047E7 /* FIRSnapshotListenerSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 230BC40D8C715CABFB10BBA9 /* FIRSnapshotListenerSourceTests.mm */; }; 05D99904EA713414928DD920 /* query_listener_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7C3F995E040E9E9C5E8514BB /* query_listener_test.cc */; }; 062072B72773A055001655D7 /* AsyncAwaitIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062072B62773A055001655D7 /* AsyncAwaitIntegrationTests.swift */; }; 062072B82773A055001655D7 /* AsyncAwaitIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062072B62773A055001655D7 /* AsyncAwaitIntegrationTests.swift */; }; @@ -206,6 +205,7 @@ 1CC56DCA513B98CE39A6ED45 /* memory_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F6CA0C5638AB6627CB5B4CF4 /* memory_local_store_test.cc */; }; 1CC9BABDD52B2A1E37E2698D /* mutation_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = C8522DE226C467C54E6788D8 /* mutation_test.cc */; }; 1CEEB0E7FBBB974224BBA557 /* bloom_filter_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A2E6F09AD1EE0A6A452E9A08 /* bloom_filter_test.cc */; }; + 1CFBD4563960D8A20C4679A3 /* SnapshotListenerSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D65F6E69993611D47DC8E7C /* SnapshotListenerSourceTests.swift */; }; 1D618761796DE311A1707AA2 /* database_id_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB71064B201FA60300344F18 /* database_id_test.cc */; }; 1D71CA6BBA1E3433F243188E /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D221C2DDC800EFB9CC /* common.pb.cc */; }; 1D76DDBE57A4D66C64C00B65 /* FIRFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04A202154AA00B64F25 /* FIRFieldValueTests.mm */; }; @@ -402,7 +402,6 @@ 3E101CE56C70F06BA2FDD56C /* Validation_BloomFilterTest_MD5_50000_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 3841925AA60E13A027F565E6 /* Validation_BloomFilterTest_MD5_50000_1_membership_test_result.json */; }; 3E38E4B33855DD6CF7526225 /* bundle_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B5C2A94EE24E60543F62CC35 /* bundle_serializer_test.cc */; }; 3E5FD39FE7442883AB3CE1F2 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 5C68EE4CB94C0DD6E333F546 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json */; }; - 3EC384D0D86A71653799DDD1 /* FIRSnapshotListenerSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 230BC40D8C715CABFB10BBA9 /* FIRSnapshotListenerSourceTests.mm */; }; 3F3C2DAD9F9326BF789B1C96 /* serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 61F72C5520BC48FD001A68CB /* serializer_test.cc */; }; 3F4B6300198FD78E7B19BC5A /* strerror_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 358C3B5FE573B1D60A4F7592 /* strerror_test.cc */; }; 3F6C9F8A993CF4B0CD51E7F0 /* lru_garbage_collector_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 277EAACC4DD7C21332E8496A /* lru_garbage_collector_test.cc */; }; @@ -1065,6 +1064,7 @@ 9F9244225BE2EC88AA0CE4EF /* sorted_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */; }; A05BC6BDA2ABE405009211A9 /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; A06FBB7367CDD496887B86F8 /* leveldb_opener_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 75860CD13AF47EB1EA39EC2F /* leveldb_opener_test.cc */; }; + A0BC30D482B0ABD1A3A24CDC /* SnapshotListenerSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D65F6E69993611D47DC8E7C /* SnapshotListenerSourceTests.swift */; }; A0C6C658DFEE58314586907B /* offline_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A11F315EE100DD57A1 /* offline_spec_test.json */; }; A0D61250F959BC52CEFF9467 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C8FB22BCB9F454DA44BA80C8 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json */; }; A0E1C7F5C7093A498F65C5CF /* memory_bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB4AB1388538CD3CB19EB028 /* memory_bundle_cache_test.cc */; }; @@ -1163,6 +1163,7 @@ AFCA3C24AA751B5B2D3E6FEF /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 0D964D4936953635AC7E0834 /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json */; }; AFE84E7B0C356CD2A113E56E /* status_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3CAA33F964042646FDDAF9F9 /* status_testing.cc */; }; AFF7D2CF35B51656E4744164 /* bloom_filter_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A2E6F09AD1EE0A6A452E9A08 /* bloom_filter_test.cc */; }; + B00F8D1819EE20C45B660940 /* SnapshotListenerSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D65F6E69993611D47DC8E7C /* SnapshotListenerSourceTests.swift */; }; B03F286F3AEC3781C386C646 /* FIRNumericTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */; }; B04E4FE20930384DF3A402F9 /* aggregate_query_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AF924C79F49F793992A84879 /* aggregate_query_test.cc */; }; B0B779769926304268200015 /* query_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 731541602214AFFA0037F4DC /* query_spec_test.json */; }; @@ -1235,7 +1236,6 @@ B896E5DE1CC27347FAC009C3 /* BasicCompileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE0761F61F2FE68D003233AF /* BasicCompileTests.swift */; }; B921A4F35B58925D958DD9A6 /* reference_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 132E32997D781B896672D30A /* reference_set_test.cc */; }; B9706A5CD29195A613CF4147 /* bundle_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6ECAF7DE28A19C69DF386D88 /* bundle_reader_test.cc */; }; - B970C9A35BDDB6B01AB0454C /* FIRSnapshotListenerSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 230BC40D8C715CABFB10BBA9 /* FIRSnapshotListenerSourceTests.mm */; }; B99452AB7E16B72D1C01FBBC /* datastore_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3167BD972EFF8EC636530E59 /* datastore_test.cc */; }; B998971CE6D0D1DD2AD9250A /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 5B96CC29E9946508F022859C /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json */; }; BA0BB02821F1949783C8AA50 /* FIRCollectionReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */; }; @@ -1704,7 +1704,6 @@ 214877F52A705012D6720CA0 /* object_value_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = object_value_test.cc; sourceTree = ""; }; 2220F583583EFC28DE792ABE /* Pods_Firestore_IntegrationTests_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2286F308EFB0534B1BDE05B9 /* memory_target_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_target_cache_test.cc; sourceTree = ""; }; - 230BC40D8C715CABFB10BBA9 /* FIRSnapshotListenerSourceTests.mm */ = {isa = PBXFileReference; includeInIndex = 1; path = FIRSnapshotListenerSourceTests.mm; sourceTree = ""; }; 277EAACC4DD7C21332E8496A /* lru_garbage_collector_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = lru_garbage_collector_test.cc; sourceTree = ""; }; 28B45B2104E2DAFBBF86DBB7 /* logic_utils_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = logic_utils_test.cc; sourceTree = ""; }; 29D9C76922DAC6F710BC1EF4 /* memory_document_overlay_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_document_overlay_cache_test.cc; sourceTree = ""; }; @@ -1750,6 +1749,7 @@ 4B59C0A7B2A4548496ED4E7D /* Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json; sourceTree = ""; }; 4BD051DBE754950FEAC7A446 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json; sourceTree = ""; }; 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = string_apple_benchmark.mm; sourceTree = ""; }; + 4D65F6E69993611D47DC8E7C /* SnapshotListenerSourceTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SnapshotListenerSourceTests.swift; sourceTree = ""; }; 4D9E51DA7A275D8B1CAEAEB2 /* listen_source_spec_test.json */ = {isa = PBXFileReference; includeInIndex = 1; path = listen_source_spec_test.json; sourceTree = ""; }; 4F5B96F3ABCD2CA901DB1CD4 /* bundle_builder.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bundle_builder.cc; sourceTree = ""; }; 526D755F65AC676234F57125 /* target_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = target_test.cc; sourceTree = ""; }; @@ -2232,6 +2232,7 @@ 3355BE9391CC4857AF0BDAE3 /* DatabaseTests.swift */, 62E54B832A9E910A003347C8 /* IndexingTests.swift */, 621D620928F9CE7400D2FA26 /* QueryIntegrationTests.swift */, + 4D65F6E69993611D47DC8E7C /* SnapshotListenerSourceTests.swift */, ); path = Integration; sourceTree = ""; @@ -3022,7 +3023,6 @@ D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */, 5492E069202154D500B64F25 /* FIRQueryTests.mm */, 5492E06E202154D600B64F25 /* FIRServerTimestampTests.mm */, - 230BC40D8C715CABFB10BBA9 /* FIRSnapshotListenerSourceTests.mm */, 5492E071202154D600B64F25 /* FIRTypeTests.mm */, 5492E06D202154D600B64F25 /* FIRValidationTests.mm */, 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */, @@ -4566,7 +4566,6 @@ 9A29D572C64CA1FA62F591D4 /* FIRQueryTests.mm in Sources */, 9A8B01AF6F19D248202FBC0A /* FIRQueryUnitTests.mm in Sources */, FA7837C5CDFB273DE447E447 /* FIRServerTimestampTests.mm in Sources */, - B970C9A35BDDB6B01AB0454C /* FIRSnapshotListenerSourceTests.mm in Sources */, 67BC2B77C1CC47388E79D774 /* FIRSnapshotMetadataTests.mm in Sources */, 041CF73F67F6A22BF317625A /* FIRTimestampTest.m in Sources */, 58B84B550725D9812729C7F7 /* FIRTransactionOptionsTests.mm in Sources */, @@ -4592,6 +4591,7 @@ 3B1E27D951407FD237E64D07 /* FirestoreEncoderTests.swift in Sources */, 62E54B862A9E910B003347C8 /* IndexingTests.swift in Sources */, 621D620C28F9CE7400D2FA26 /* QueryIntegrationTests.swift in Sources */, + 1CFBD4563960D8A20C4679A3 /* SnapshotListenerSourceTests.swift in Sources */, 4D42E5C756229C08560DD731 /* XCTestCase+Await.mm in Sources */, 09BE8C01EC33D1FD82262D5D /* aggregate_query_test.cc in Sources */, 0EC3921AE220410F7394729B /* aggregation_result.pb.cc in Sources */, @@ -4807,7 +4807,6 @@ 6A4F6B42C628D55CCE0C311F /* FIRQueryTests.mm in Sources */, 298E0F8F6EB27AA36BA1CE76 /* FIRQueryUnitTests.mm in Sources */, 27E46C94AAB087C80A97FF7F /* FIRServerTimestampTests.mm in Sources */, - 05BAC479CA3AB13B7A5047E7 /* FIRSnapshotListenerSourceTests.mm in Sources */, 59F512D155DE361095A04ED4 /* FIRSnapshotMetadataTests.mm in Sources */, FE1C0263F6570DAC54A60F5C /* FIRTimestampTest.m in Sources */, 339D4DD13E1518BA79FF12EA /* FIRTransactionOptionsTests.mm in Sources */, @@ -4833,6 +4832,7 @@ 5E89B1A5A5430713C79C4854 /* FirestoreEncoderTests.swift in Sources */, 62E54B852A9E910B003347C8 /* IndexingTests.swift in Sources */, 621D620B28F9CE7400D2FA26 /* QueryIntegrationTests.swift in Sources */, + A0BC30D482B0ABD1A3A24CDC /* SnapshotListenerSourceTests.swift in Sources */, 736C4E82689F1CA1859C4A3F /* XCTestCase+Await.mm in Sources */, 412BE974741729A6683C386F /* aggregate_query_test.cc in Sources */, DF983A9C1FBF758AF3AF110D /* aggregation_result.pb.cc in Sources */, @@ -5295,7 +5295,6 @@ 5492E072202154D600B64F25 /* FIRQueryTests.mm in Sources */, 82E3634FCF4A882948B81839 /* FIRQueryUnitTests.mm in Sources */, 5492E077202154D600B64F25 /* FIRServerTimestampTests.mm in Sources */, - 3EC384D0D86A71653799DDD1 /* FIRSnapshotListenerSourceTests.mm in Sources */, 716289F99B5316B3CC5E5CE9 /* FIRSnapshotMetadataTests.mm in Sources */, 02B83EB79020AE6CBA60A410 /* FIRTimestampTest.m in Sources */, 913C2DB6951A2ED24778686C /* FIRTransactionOptionsTests.mm in Sources */, @@ -5321,6 +5320,7 @@ 6F45846C159D3C063DBD3CBE /* FirestoreEncoderTests.swift in Sources */, 62E54B842A9E910B003347C8 /* IndexingTests.swift in Sources */, 621D620A28F9CE7400D2FA26 /* QueryIntegrationTests.swift in Sources */, + B00F8D1819EE20C45B660940 /* SnapshotListenerSourceTests.swift in Sources */, 5492E0442021457E00B64F25 /* XCTestCase+Await.mm in Sources */, B04E4FE20930384DF3A402F9 /* aggregate_query_test.cc in Sources */, 1A3D8028303B45FCBB21CAD3 /* aggregation_result.pb.cc in Sources */, diff --git a/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm b/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm deleted file mode 100644 index 29cc2e85325..00000000000 --- a/Firestore/Example/Tests/Integration/API/FIRSnapshotListenerSourceTests.mm +++ /dev/null @@ -1,645 +0,0 @@ -/* - * Copyright 2024 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import - -#import - -#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h" -#import "Firestore/Example/Tests/Util/FSTHelpers.h" -#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" -#import "Firestore/Example/Tests/Util/FSTTestingHooks.h" - -#import "Firestore/Source/API/FIRAggregateQuerySnapshot+Internal.h" -#import "Firestore/Source/API/FIRQuery+Internal.h" - -@interface FIRSnapshotListenerSourceTests : FSTIntegrationTestCase -@end - -@implementation FIRSnapshotListenerSourceTests - -- (FIRSnapshotListenOptions *)optionsWithSourceFromCache { - FIRSnapshotListenOptions *options = [[FIRSnapshotListenOptions alloc] init]; - return [options optionsWithSource:FIRListenSourceCache]; -} -- (FIRSnapshotListenOptions *)optionsWithSourceFromCacheAndIncludeMetadataChanges { - FIRSnapshotListenOptions *options = [[FIRSnapshotListenOptions alloc] init]; - return [[options optionsWithSource:FIRListenSourceCache] optionsWithIncludeMetadataChanges:YES]; -} - -- (void)testCanRaiseSnapshotFromCacheForQuery { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a"}}]; - - [self readDocumentSetForRef:collRef]; // populate the cache. - - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id registration = - [collRef addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; - - FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a"} ])); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - - [self.eventAccumulator assertNoAdditionalEvents]; - [registration remove]; -} - -- (void)testCanRaiseSnapshotFromCacheForDocumentReference { - FIRDocumentReference *docRef = [self documentRef]; - [docRef setData:@{@"k" : @"a"}]; - - [self readDocumentForRef:docRef]; // populate the cache. - - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id registration = - [docRef addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; - - FIRDocumentSnapshot *docSnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(docSnap.data, (@{@"k" : @"a"})); - XCTAssertEqual(docSnap.metadata.isFromCache, YES); - - [self.eventAccumulator assertNoAdditionalEvents]; - [registration remove]; -} - -- (void)testListenToCacheShouldNotBeAffectedByOnlineStatusChange { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a"}}]; - - [self readDocumentSetForRef:collRef]; // populate the cache. - - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCacheAndIncludeMetadataChanges]; - id registration = - [collRef addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; - - FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a"} ])); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - - [self disableNetwork]; - [self enableNetwork]; - - [self.eventAccumulator assertNoAdditionalEvents]; - [registration remove]; -} - -- (void)testMultipleListenersSourcedFromCacheCanWorkIndependently { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0}, - @"b" : @{@"k" : @"b", @"sort" : @1} - }]; - [self readDocumentSetForRef:collRef]; // populate the cache. - - FIRQuery *query = [[collRef queryWhereField:@"sort" - isGreaterThan:@0] queryOrderedByField:@"sort"]; - - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id registration1 = - [query addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; - id registration2 = - [query addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; - - FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - NSArray *expected = @[ @{@"k" : @"b", @"sort" : @1L} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - - // Do a local mutation - [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @2}]; - - querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - expected = @[ @{@"k" : @"b", @"sort" : @1L}, @{@"k" : @"c", @"sort" : @2L} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - - querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - - // Detach one listener, and do a local mutation. The other listener - // should not be affected. - [registration1 remove]; - [self addDocumentRef:collRef data:@{@"k" : @"d", @"sort" : @3}]; - - querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - expected = @[ - @{@"k" : @"b", @"sort" : @1L}, @{@"k" : @"c", @"sort" : @2L}, @{@"k" : @"d", @"sort" : @3L} - ]; - - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - - [self.eventAccumulator assertNoAdditionalEvents]; - [registration2 remove]; -} - -// Two queries that mapped to the same target ID are referred to as -// "mirror queries". An example for a mirror query is a limitToLast() -// query and a limit() query that share the same backend Target ID. -// Since limitToLast() queries are sent to the backend with a modified -// orderBy() clause, they can map to the same target representation as -// limit() query, even if both queries appear separate to the user. -- (void)testListenUnlistenRelistenToMirrorQueriesFromCache { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0}, - @"b" : @{@"k" : @"b", @"sort" : @1}, - @"c" : @{@"k" : @"c", @"sort" : @1}, - }]; - - [self readDocumentSetForRef:collRef]; // populate the cache. - - // Setup a `limit` query. - FIRQuery *limit = [[collRef queryOrderedByField:@"sort" descending:NO] queryLimitedTo:2]; - FSTEventAccumulator *limitAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id limitRegistration = - [limit addSnapshotListenerWithOptions:options listener:limitAccumulator.valueEventHandler]; - - // Setup a mirroring `limitToLast` query. - FIRQuery *limitToLast = [[collRef queryOrderedByField:@"sort" - descending:YES] queryLimitedToLast:2]; - FSTEventAccumulator *limitToLastAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - id limitToLastRegistration = - [limitToLast addSnapshotListenerWithOptions:options - listener:limitToLastAccumulator.valueEventHandler]; - - // Verify both queries get expected result. - FIRQuerySnapshot *snapshot = [limitAccumulator awaitEventWithName:@"Snapshot"]; - NSArray *expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"b", @"sort" : @1} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); - snapshot = [limitToLastAccumulator awaitEventWithName:@"Snapshot"]; - expected = @[ @{@"k" : @"b", @"sort" : @1}, @{@"k" : @"a", @"sort" : @0} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); - - // Un-listen then re-listen to the limit query. - [limitRegistration remove]; - limitRegistration = [limit addSnapshotListenerWithOptions:options - listener:limitAccumulator.valueEventHandler]; - snapshot = [limitAccumulator awaitEventWithName:@"Snapshot"]; - expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"b", @"sort" : @1} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); - XCTAssertEqual(snapshot.metadata.isFromCache, YES); - - // Add a document that would change the result set. - [self addDocumentRef:collRef data:@{@"k" : @"d", @"sort" : @-1}]; - - // Verify both queries get expected result. - snapshot = [limitAccumulator awaitEventWithName:@"Snapshot"]; - expected = @[ @{@"k" : @"d", @"sort" : @-1}, @{@"k" : @"a", @"sort" : @0} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); - XCTAssertEqual(snapshot.metadata.hasPendingWrites, YES); - snapshot = [limitToLastAccumulator awaitEventWithName:@"Snapshot"]; - expected = @[ @{@"k" : @"a", @"sort" : @0}, @{@"k" : @"d", @"sort" : @-1} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); - XCTAssertEqual(snapshot.metadata.hasPendingWrites, YES); - - // Un-listen to limitToLast, update a doc, then re-listen to limitToLast - [limitToLastRegistration remove]; - [self updateDocumentRef:[collRef documentWithPath:@"a"] data:@{@"k" : @"a", @"sort" : @-2}]; - limitToLastRegistration = - [limitToLast addSnapshotListenerWithOptions:options - listener:limitToLastAccumulator.valueEventHandler]; - - // Verify both queries get expected result. - snapshot = [limitAccumulator awaitEventWithName:@"Snapshot"]; - expected = @[ @{@"k" : @"a", @"sort" : @-2}, @{@"k" : @"d", @"sort" : @-1} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); - XCTAssertEqual(snapshot.metadata.hasPendingWrites, YES); - - snapshot = [limitToLastAccumulator awaitEventWithName:@"Snapshot"]; - expected = @[ @{@"k" : @"d", @"sort" : @-1}, @{@"k" : @"a", @"sort" : @-2} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected); - // We listened to LimitToLast query after the doc update. - XCTAssertEqual(snapshot.metadata.hasPendingWrites, NO); -} - -- (void)testCanListenToDefaultSourceFirstAndThenCache { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0}, - @"b" : @{@"k" : @"b", @"sort" : @1} - }]; - FIRQuery *query = [[collRef queryWhereField:@"sort" - isGreaterThanOrEqualTo:@1] queryOrderedByField:@"sort"]; - - // Listen to the query with default options, which will also populates the cache - FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - id defaultRegistration = - [query addSnapshotListener:defaultAccumulator.valueEventHandler]; - FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - - // Listen to the same query from cache - FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id cacheRegistration = - [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; - querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); - // The metadata is sync with server due to the default listener - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - - [defaultAccumulator assertNoAdditionalEvents]; - [cacheAccumulator assertNoAdditionalEvents]; - [defaultRegistration remove]; - [cacheRegistration remove]; -} - -- (void)testCanListenToCacheSourceFirstAndThenDefault { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0}, - @"b" : @{@"k" : @"b", @"sort" : @1} - }]; - FIRQuery *query = [[collRef queryWhereField:@"sort" isNotEqualTo:@0] queryOrderedByField:@"sort"]; - - // Listen to the cache - FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id cacheRegistration = - [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; - FIRQuerySnapshot *querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - // Cache is empty - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[])); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - - // Listen to the same query from server - FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - id defaultRegistration = - [query addSnapshotListener:defaultAccumulator.valueEventHandler]; - querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - - // Default listener updates the cache, whish triggers cache listener to raise snapshot. - querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); - // The metadata is sync with server due to the default listener - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - - [defaultAccumulator assertNoAdditionalEvents]; - [cacheAccumulator assertNoAdditionalEvents]; - [defaultRegistration remove]; - [cacheRegistration remove]; -} - -- (void)testWillNotGetMetadataOnlyUpdatesIfListeningToCacheOnly { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0}, - @"b" : @{@"k" : @"b", @"sort" : @1} - }]; - - [self readDocumentSetForRef:collRef]; // populate the cache. - - FIRQuery *query = [[collRef queryWhereField:@"sort" isNotEqualTo:@0] queryOrderedByField:@"sort"]; - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCacheAndIncludeMetadataChanges]; - id registration = - [query addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; - - FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - - // Do a local mutation - [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @2}]; - - querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), - (@[ @{@"k" : @"b", @"sort" : @1L}, @{@"k" : @"c", @"sort" : @2L} ])); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - XCTAssertEqual(querySnap.metadata.hasPendingWrites, YES); - - // As we are not listening to server, the listener will not get notified - // when local mutation is acknowledged by server. - [self.eventAccumulator assertNoAdditionalEvents]; - [registration remove]; -} - -- (void)testWillHaveSynceMetadataUpdatesWhenListeningToBothCacheAndDefaultSource { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0}, - @"b" : @{@"k" : @"b", @"sort" : @1} - }]; - [self readDocumentSetForRef:collRef]; // populate the cache. - - FIRQuery *query = [[collRef queryWhereField:@"sort" isNotEqualTo:@0] queryOrderedByField:@"sort"]; - - // Listen to the cache - FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCacheAndIncludeMetadataChanges]; - id cacheRegistration = - [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; - FIRQuerySnapshot *querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - NSArray *expected = @[ @{@"k" : @"b", @"sort" : @1L} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - - // Listen to the same query from server - FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - id defaultRegistration = - [query addSnapshotListenerWithIncludeMetadataChanges:YES - listener:defaultAccumulator.valueEventHandler]; - querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); - // First snapshot will be raised from cache. - XCTAssertEqual(querySnap.metadata.isFromCache, YES); - querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - // Second snapshot will be raised from server result - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - - // As listening to metadata changes, the cache listener also gets triggered and synced - // with default listener. - querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - // The metadata is sync with server due to the default listener - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - - // Do a local mutation - [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @2}]; - - // snapshot gets triggered by local mutation - expected = @[ @{@"k" : @"b", @"sort" : @1}, @{@"k" : @"c", @"sort" : @2} ]; - querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); - XCTAssertEqual(querySnap.metadata.hasPendingWrites, YES); - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); - XCTAssertEqual(querySnap.metadata.hasPendingWrites, YES); - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - - // Local mutation gets acknowledged by the server - querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqual(querySnap.metadata.hasPendingWrites, NO); - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqual(querySnap.metadata.hasPendingWrites, NO); - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - - [defaultAccumulator assertNoAdditionalEvents]; - [cacheAccumulator assertNoAdditionalEvents]; - [defaultRegistration remove]; - [cacheRegistration remove]; -} - -- (void)testCanUnlistenToDefaultSourceWhileStillListeningToCache { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0}, - @"b" : @{@"k" : @"b", @"sort" : @1} - }]; - FIRQuery *query = [[collRef queryWhereField:@"sort" isNotEqualTo:@0] queryOrderedByField:@"sort"]; - - // Listen to the query with both source options - FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - id defaultRegistration = - [query addSnapshotListener:defaultAccumulator.valueEventHandler]; - [defaultAccumulator awaitEventWithName:@"Snapshot"]; - FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id cacheRegistration = - [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; - [cacheAccumulator awaitEventWithName:@"Snapshot"]; - - // Un-listen to the default listener. - [defaultRegistration remove]; - - // Add a document and verify listener to cache works as expected - [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @-1}]; - [defaultAccumulator assertNoAdditionalEvents]; - - FIRQuerySnapshot *querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), - (@[ @{@"k" : @"c", @"sort" : @-1L}, @{@"k" : @"b", @"sort" : @1L} ])); - - [cacheAccumulator assertNoAdditionalEvents]; - [cacheRegistration remove]; -} - -- (void)testCanUnlistenToCacheSourceWhileStillListeningToServer { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0}, - @"b" : @{@"k" : @"b", @"sort" : @1} - }]; - FIRQuery *query = [[collRef queryWhereField:@"sort" isNotEqualTo:@0] queryOrderedByField:@"sort"]; - - // Listen to the query with both source options - FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - id defaultRegistration = - [query addSnapshotListener:defaultAccumulator.valueEventHandler]; - [defaultAccumulator awaitEventWithName:@"Snapshot"]; - FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id cacheRegistration = - [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; - [cacheAccumulator awaitEventWithName:@"Snapshot"]; - - // Un-listen to cache. - [cacheRegistration remove]; - - // Add a document and verify listener to server works as expected. - [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @-1}]; - [cacheAccumulator assertNoAdditionalEvents]; - - FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), - (@[ @{@"k" : @"c", @"sort" : @-1L}, @{@"k" : @"b", @"sort" : @1L} ])); - - [defaultAccumulator assertNoAdditionalEvents]; - [defaultRegistration remove]; -} - -- (void)testCanListenUnlistenRelistenToSameQueryWithDifferentSourceOptions { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0}, - @"b" : @{@"k" : @"b", @"sort" : @1} - }]; - FIRQuery *query = [[collRef queryWhereField:@"sort" - isGreaterThan:@0] queryOrderedByField:@"sort"]; - - // Listen to the query with default options, which will also populates the cache - FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - id defaultRegistration = - [query addSnapshotListener:defaultAccumulator.valueEventHandler]; - FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - NSArray *expected = @[ @{@"k" : @"b", @"sort" : @1L} ]; - - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); - - // Listen to the same query from cache - FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id cacheRegistration = - [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; - querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (expected)); - - // Un-listen to the default listener, add a doc and re-listen. - [defaultRegistration remove]; - [self addDocumentRef:collRef data:@{@"k" : @"c", @"sort" : @2}]; - - querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - expected = @[ @{@"k" : @"b", @"sort" : @1}, @{@"k" : @"c", @"sort" : @2} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); - - defaultRegistration = [query addSnapshotListener:defaultAccumulator.valueEventHandler]; - querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); - - // Un-listen to cache, update a doc, then re-listen to cache. - [cacheRegistration remove]; - [self updateDocumentRef:[collRef documentWithPath:@"b"] data:@{@"k" : @"b", @"sort" : @3}]; - - querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - expected = @[ @{@"k" : @"c", @"sort" : @2}, @{@"k" : @"b", @"sort" : @3} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); - - cacheRegistration = [query addSnapshotListenerWithOptions:options - listener:cacheAccumulator.valueEventHandler]; - querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); - - [defaultAccumulator assertNoAdditionalEvents]; - [cacheAccumulator assertNoAdditionalEvents]; - [defaultRegistration remove]; - [cacheRegistration remove]; -} - -- (void)testCanListenToCompositeIndexQueriesFromCache { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0}, - @"b" : @{@"k" : @"b", @"sort" : @1} - }]; - [self readDocumentSetForRef:collRef]; // populate the cache. - - FIRQuery *query = [[collRef queryWhereField:@"k" isLessThanOrEqualTo:@"a"] queryWhereField:@"sort" - isGreaterThanOrEqualTo:@0]; - - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id registration = - [query addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; - FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"a", @"sort" : @0L} ])); - - [self.eventAccumulator assertNoAdditionalEvents]; - [registration remove]; -} - -- (void)testCanRaiseInitialSnapshotFromCachedEmptyResults { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{}]; - - // Populate the cache with empty query result. - FIRQuerySnapshot *querySnapshot = [self readDocumentSetForRef:collRef]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnapshot), @[]); - - // Add a snapshot listener whose first event should be raised from cache. - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id registration = - [collRef addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; - - querySnapshot = [self.eventAccumulator awaitEventWithName:@"initial event"]; - XCTAssertTrue(querySnapshot.metadata.fromCache); - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnapshot), @[]); - - [registration remove]; -} - -- (void)testWillNotBeTriggeredByTransactionsWhileListeningToCache { - FIRCollectionReference *collRef = - [self collectionRefWithDocuments:@{@"a" : @{@"k" : @"a", @"sort" : @0}}]; - - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id registration = - [collRef addSnapshotListenerWithOptions:options - listener:self.eventAccumulator.valueEventHandler]; - - FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[])); - - FIRDocumentReference *docRef = [self documentRef]; - // Use a transaction to perform a write without triggering any local events. - [docRef.firestore - runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **) { - [transaction setData:@{@"k" : @"a"} forDocument:docRef]; - return nil; - } - completion:^(id, NSError *){ - }]; - - // There should be no events raised - [self.eventAccumulator assertNoAdditionalEvents]; - [registration remove]; -} - -- (void)testSharesServerSideUpdatesWhenListeningToBothCacheAndDefault { - FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{ - @"a" : @{@"k" : @"a", @"sort" : @0}, - @"b" : @{@"k" : @"b", @"sort" : @1} - }]; - FIRQuery *query = [[collRef queryWhereField:@"sort" - isGreaterThan:@0] queryOrderedByField:@"sort"]; - - // Listen to the query with default options, which will also populates the cache - FSTEventAccumulator *defaultAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - id defaultRegistration = - [query addSnapshotListener:defaultAccumulator.valueEventHandler]; - FIRQuerySnapshot *querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); - - // Listen to the same query from cache - FSTEventAccumulator *cacheAccumulator = [FSTEventAccumulator accumulatorForTest:self]; - FIRSnapshotListenOptions *options = [self optionsWithSourceFromCache]; - id cacheRegistration = - [query addSnapshotListenerWithOptions:options listener:cacheAccumulator.valueEventHandler]; - querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), (@[ @{@"k" : @"b", @"sort" : @1L} ])); - - // Use a transaction to mock server side updates - FIRDocumentReference *docRef = [collRef documentWithAutoID]; - // Use a transaction to perform a write without triggering any local events. - [docRef.firestore - runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **) { - [transaction setData:@{@"k" : @"c", @"sort" : @2} forDocument:docRef]; - return nil; - } - completion:^(id, NSError *){ - }]; - - // Default listener receives the server update - querySnap = [defaultAccumulator awaitEventWithName:@"Snapshot"]; - NSArray *expected = @[ @{@"k" : @"b", @"sort" : @1}, @{@"k" : @"c", @"sort" : @2} ]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - - // Cache listener raises snapshot as well - querySnap = [cacheAccumulator awaitEventWithName:@"Snapshot"]; - XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), expected); - XCTAssertEqual(querySnap.metadata.isFromCache, NO); - - [defaultAccumulator assertNoAdditionalEvents]; - [cacheAccumulator assertNoAdditionalEvents]; - [defaultRegistration remove]; - [cacheRegistration remove]; -} - -@end diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h index abd965beee3..a63fd66cd1b 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h @@ -35,6 +35,7 @@ @class FIRQuery; @class FIRWriteBatch; @class FSTEventAccumulator; +@class FIRTransaction; NS_ASSUME_NONNULL_BEGIN @@ -113,6 +114,10 @@ extern "C" { - (FIRDocumentReference *)addDocumentRef:(FIRCollectionReference *)ref data:(NSDictionary *)data; +- (void)runTransaction:(FIRFirestore *)db + block:(id _Nullable (^)(FIRTransaction *, NSError **error))block + completion:(nullable void (^)(id _Nullable result, NSError *_Nullable error))completion; + - (void)mergeDocumentRef:(FIRDocumentReference *)ref data:(NSDictionary *)data; - (void)mergeDocumentRef:(FIRDocumentReference *)ref diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index 6633ed03748..f42d10bcb01 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -569,6 +569,22 @@ - (FIRDocumentReference *)addDocumentRef:(FIRCollectionReference *)ref return doc; } +- (void)runTransaction:(FIRFirestore *)db + block:(id _Nullable (^)(FIRTransaction *, NSError **error))block + completion: + (nullable void (^)(id _Nullable result, NSError *_Nullable error))completion { + XCTestExpectation *expectation = [self expectationWithDescription:@"runTransaction"]; + [db runTransactionWithOptions:nil + block:block + completion:^(id _Nullable result, NSError *_Nullable error) { + if (completion) { + completion(result, error); + } + [expectation fulfill]; + }]; + [self awaitExpectation:expectation]; +} + - (void)mergeDocumentRef:(FIRDocumentReference *)ref data:(NSDictionary *)data { XCTestExpectation *expectation = [self expectationWithDescription:@"setDataWithMerge"]; [ref setData:data merge:YES completion:[self completionForExpectation:expectation]]; diff --git a/Firestore/Swift/Tests/BridgingHeader.h b/Firestore/Swift/Tests/BridgingHeader.h index 5bfed83adf4..58165c82944 100644 --- a/Firestore/Swift/Tests/BridgingHeader.h +++ b/Firestore/Swift/Tests/BridgingHeader.h @@ -18,6 +18,7 @@ #define FIRESTORE_SWIFT_TESTS_BRIDGINGHEADER_H_ #import "Firestore/Example/Tests/API/FSTAPIHelpers.h" +#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h" #import "Firestore/Example/Tests/Util/FSTExceptionCatcher.h" #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" diff --git a/Firestore/Swift/Tests/Integration/SnapshotListenerSourceTests.swift b/Firestore/Swift/Tests/Integration/SnapshotListenerSourceTests.swift new file mode 100644 index 00000000000..22c69679224 --- /dev/null +++ b/Firestore/Swift/Tests/Integration/SnapshotListenerSourceTests.swift @@ -0,0 +1,684 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import FirebaseFirestore +import FirebaseFirestoreSwift +import Foundation + +@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) +class SnapshotListenerSourceTests: FSTIntegrationTestCase { + func assertQuerySnapshotDataEquals(_ snapshot: Any, + _ expectedData: [[String: Any]]) throws { + let extractedData = FIRQuerySnapshotGetData(snapshot as! QuerySnapshot) + guard extractedData.count == expectedData.count else { + XCTFail( + "Result count mismatch: Expected \(expectedData.count), got \(extractedData.count)" + ) + return + } + for index in 0 ..< extractedData.count { + XCTAssertTrue(areDictionariesEqual(extractedData[index], expectedData[index])) + } + } + + // TODO(swift testing): update the function to be able to check other value types as well. + func areDictionariesEqual(_ dict1: [String: Any], _ dict2: [String: Any]) -> Bool { + guard dict1.count == dict2.count + else { return false } // Check if the number of elements matches + + for (key, value1) in dict1 { + guard let value2 = dict2[key] else { return false } + + // Value Checks (Assuming consistent types after the type check) + if let str1 = value1 as? String, let str2 = value2 as? String { + if str1 != str2 { return false } + } else if let int1 = value1 as? Int, let int2 = value2 as? Int { + if int1 != int2 { return false } + } else { + // Handle other potential types or return false for mismatch + return false + } + } + return true + } + + func testCanRaiseSnapshotFromCacheForQuery() throws { + let collRef = collectionRef(withDocuments: ["a": ["k": "a"]]) + readDocumentSet(forRef: collRef) // populate the cache. + + let options = SnapshotListenOptions().withSource(ListenSource.cache) + let registration = collRef.addSnapshotListener( + options: options, + listener: eventAccumulator.valueEventHandler + ) + + let querySnap = eventAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "a"]]) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + eventAccumulator.assertNoAdditionalEvents() + registration.remove() + } + + func testCanRaiseSnapshotFromCacheForDocumentReference() throws { + let docRef = documentRef() + docRef.setData(["k": "a"]) + readDocument(forRef: docRef) // populate the cache. + + let options = SnapshotListenOptions().withSource(ListenSource.cache) + let registration = docRef.addSnapshotListener( + options: options, + listener: eventAccumulator.valueEventHandler + ) + + let docSnap = eventAccumulator.awaitEvent(withName: "snapshot") as! DocumentSnapshot + XCTAssertEqual(docSnap.data() as! [String: String], ["k": "a"]) + XCTAssertEqual(docSnap.metadata.isFromCache, true) + + eventAccumulator.assertNoAdditionalEvents() + registration.remove() + } + + func testListenToCacheShouldNotBeAffectedByOnlineStatusChange() throws { + let collRef = collectionRef(withDocuments: ["a": ["k": "a"]]) + readDocumentSet(forRef: collRef) // populate the cache. + + let options = SnapshotListenOptions().withSource(ListenSource.cache) + .withIncludeMetadataChanges(true) + let registration = collRef.addSnapshotListener( + options: options, + listener: eventAccumulator.valueEventHandler + ) + + let querySnap = eventAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "a"]]) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + disableNetwork() + enableNetwork() + + eventAccumulator.assertNoAdditionalEvents() + registration.remove() + } + + func testMultipleListenersSourcedFromCacheCanWorkIndependently() throws { + let collRef = collectionRef(withDocuments: [ + "a": ["k": "a", "sort": 0], + "b": ["k": "b", "sort": 1], + ]) + readDocumentSet(forRef: collRef) // populate the cache. + + let query = collRef.whereField("sort", isGreaterThan: 0).order(by: "sort") + + let options = SnapshotListenOptions().withSource(ListenSource.cache) + let registration1 = query.addSnapshotListener( + options: options, + listener: eventAccumulator.valueEventHandler + ) + let registration2 = query.addSnapshotListener( + options: options, + listener: eventAccumulator.valueEventHandler + ) + + var expected = [["k": "b", "sort": 1]] + var querySnap = eventAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + querySnap = eventAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + // Do a local mutation + addDocumentRef(collRef, data: ["k": "c", "sort": 2]) + + expected = [["k": "b", "sort": 1], ["k": "c", "sort": 2]] + querySnap = eventAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + querySnap = eventAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + // Detach one listener, and do a local mutation. The other listener + // should not be affected. + registration1.remove() + addDocumentRef(collRef, data: ["k": "d", "sort": 3]) + + expected = [["k": "b", "sort": 1], ["k": "c", "sort": 2], ["k": "d", "sort": 3]] + querySnap = eventAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + eventAccumulator.assertNoAdditionalEvents() + registration2.remove() + } + + // Two queries that mapped to the same target ID are referred to as + // "mirror queries". An example for a mirror query is a limitToLast() + // query and a limit() query that share the same backend Target ID. + // Since limitToLast() queries are sent to the backend with a modified + // orderBy() clause, they can map to the same target representation as + // limit() query, even if both queries appear separate to the user. + func testListenUnlistenRelistenToMirrorQueriesFromCache() throws { + let collRef = collectionRef(withDocuments: [ + "a": ["k": "a", "sort": 0], + "b": ["k": "b", "sort": 1], + "c": ["k": "c", "sort": 1], + ]) + readDocumentSet(forRef: collRef) // populate the cache. + let options = SnapshotListenOptions().withSource(ListenSource.cache) + + // Setup a `limit` query. + let limit = collRef.order(by: "sort", descending: false).limit(to: 2) + let limitAccumulator = FSTEventAccumulator.init(forTest: self) + var limitRegistration = limit.addSnapshotListener( + options: options, + listener: limitAccumulator.valueEventHandler + ) + // Setup a mirroring `limitToLast` query. + let limitToLast = collRef.order(by: "sort", descending: true).limit(toLast: 2) + let limitToLastAccumulator = FSTEventAccumulator + .init(forTest: self) + var limitToLastRegistration = limitToLast.addSnapshotListener( + options: options, + listener: limitToLastAccumulator.valueEventHandler + ) + + // Verify both queries get expected result. + var querySnap = limitAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "a", "sort": 0], ["k": "b", "sort": 1]]) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + querySnap = limitToLastAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "b", "sort": 1], ["k": "a", "sort": 0]]) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + // Un-listen then re-listen to the limit query. + limitRegistration.remove() + limitRegistration = limit.addSnapshotListener( + options: options, + listener: limitAccumulator.valueEventHandler + ) + querySnap = limitAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals( + querySnap, + [["k": "a", "sort": 0], ["k": "b", "sort": 1]] + ) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + // Add a document that would change the result set. + addDocumentRef(collRef, data: ["k": "d", "sort": -1]) + + // Verify both queries get expected result. + querySnap = limitAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "d", "sort": -1], ["k": "a", "sort": 0]]) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + XCTAssertEqual(querySnap.metadata.hasPendingWrites, true) + querySnap = limitToLastAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "a", "sort": 0], ["k": "d", "sort": -1]]) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + XCTAssertEqual(querySnap.metadata.hasPendingWrites, true) + + // Un-listen to limitToLast, update a doc, then re-listen to limitToLast + limitToLastRegistration.remove() + updateDocumentRef(collRef.document("a"), data: ["k": "a", "sort": -2]) + limitToLastRegistration = limitToLast.addSnapshotListener( + options: options, + listener: limitToLastAccumulator.valueEventHandler + ) + + // Verify both queries get expected result. + querySnap = limitAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "a", "sort": -2], ["k": "d", "sort": -1]]) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + XCTAssertEqual(querySnap.metadata.hasPendingWrites, true) + querySnap = limitToLastAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "d", "sort": -1], ["k": "a", "sort": -2]]) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + // We listened to LimitToLast query after the doc update. + XCTAssertEqual(querySnap.metadata.hasPendingWrites, false) + } + + func testCanListenToDefaultSourceFirstAndThenCache() throws { + let collRef = collectionRef(withDocuments: [ + "a": ["k": "a", "sort": 0], + "b": ["k": "b", "sort": 1], + ]) + let query = collRef.whereField("sort", isGreaterThanOrEqualTo: 1).order(by: "sort") + + // Listen to the query with default options, which will also populates the cache + let defaultAccumulator = FSTEventAccumulator.init(forTest: self) + let defaultRegistration = query.addSnapshotListener(defaultAccumulator.valueEventHandler) + + var querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "b", "sort": 1]]) + XCTAssertEqual(querySnap.metadata.isFromCache, false) + + // Listen to the same query from cache + let cacheAccumulator = FSTEventAccumulator + .init(forTest: self) + let options = SnapshotListenOptions().withSource(ListenSource.cache) + let cacheRegistration = query.addSnapshotListener( + options: options, + listener: cacheAccumulator.valueEventHandler + ) + querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "b", "sort": 1]]) + // The metadata is sync with server due to the default listener + XCTAssertEqual(querySnap.metadata.isFromCache, false) + + defaultAccumulator.assertNoAdditionalEvents() + cacheAccumulator.assertNoAdditionalEvents() + defaultRegistration.remove() + cacheRegistration.remove() + } + + func testCanListenToCacheSourceFirstAndThenDefault() throws { + let collRef = collectionRef(withDocuments: [ + "a": ["k": "a", "sort": 0], + "b": ["k": "b", "sort": 1], + ]) + let query = collRef.whereField("sort", isNotEqualTo: 0).order(by: "sort") + + // Listen to the cache + let cacheAccumulator = FSTEventAccumulator + .init(forTest: self) + let options = SnapshotListenOptions().withSource(ListenSource.cache) + let cacheRegistration = query.addSnapshotListener( + options: options, + listener: cacheAccumulator.valueEventHandler + ) + var querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + // Cache is empty + try assertQuerySnapshotDataEquals(querySnap, []) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + // Listen to the same query from server + let defaultAccumulator = FSTEventAccumulator.init(forTest: self) + let defaultRegistration = query.addSnapshotListener(defaultAccumulator.valueEventHandler) + querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "b", "sort": 1]]) + XCTAssertEqual(querySnap.metadata.isFromCache, false) + + // Default listener updates the cache, whish triggers cache listener to raise snapshot. + querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "b", "sort": 1]]) + // The metadata is sync with server due to the default listener + XCTAssertEqual(querySnap.metadata.isFromCache, false) + + defaultAccumulator.assertNoAdditionalEvents() + cacheAccumulator.assertNoAdditionalEvents() + defaultRegistration.remove() + cacheRegistration.remove() + } + + func testWillNotGetMetadataOnlyUpdatesIfListeningToCacheOnly() throws { + let collRef = collectionRef(withDocuments: [ + "a": ["k": "a", "sort": 0], + "b": ["k": "b", "sort": 1], + ]) + readDocumentSet(forRef: collRef) // populate the cache. + + let query = collRef.whereField("sort", isNotEqualTo: 0).order(by: "sort") + let options = SnapshotListenOptions().withSource(ListenSource.cache) + + let registration = query.addSnapshotListener( + options: options, + listener: eventAccumulator.valueEventHandler + ) + + var querySnap = eventAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "b", "sort": 1]]) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + // Do a local mutation + addDocumentRef(collRef, data: ["k": "c", "sort": 2]) + + querySnap = eventAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "b", "sort": 1], ["k": "c", "sort": 2]]) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + XCTAssertEqual(querySnap.metadata?.hasPendingWrites, true) + + // As we are not listening to server, the listener will not get notified + // when local mutation is acknowledged by server. + eventAccumulator.assertNoAdditionalEvents() + registration.remove() + } + + func testWillHaveSynceMetadataUpdatesWhenListeningToBothCacheAndDefaultSource() throws { + let collRef = collectionRef(withDocuments: [ + "a": ["k": "a", "sort": 0], + "b": ["k": "b", "sort": 1], + ]) + readDocumentSet(forRef: collRef) // populate the cache. + let query = collRef.whereField("sort", isNotEqualTo: 0).order(by: "sort") + + // Listen to the cache + let cacheAccumulator = FSTEventAccumulator.init(forTest: self) + let options = SnapshotListenOptions().withSource(ListenSource.cache) + .withIncludeMetadataChanges(true) + let cacheRegistration = query.addSnapshotListener( + options: options, + listener: cacheAccumulator.valueEventHandler + ) + var querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + var expected = [["k": "b", "sort": 1]] + try assertQuerySnapshotDataEquals(querySnap, expected) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + // Listen to the same query from server + let defaultAccumulator = FSTEventAccumulator.init(forTest: self) + let defaultRegistration = query.addSnapshotListener( + includeMetadataChanges: true, + listener: defaultAccumulator.valueEventHandler + ) + + querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + // First snapshot will be raised from cache. + XCTAssertEqual(querySnap.metadata.isFromCache, true) + querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + // Second snapshot will be raised from server result + XCTAssertEqual(querySnap.metadata.isFromCache, false) + + // As listening to metadata changes, the cache listener also gets triggered and synced + // with default listener. + querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + // The metadata is sync with server due to the default listener + XCTAssertEqual(querySnap.metadata.isFromCache, false) + + // Do a local mutation + addDocumentRef(collRef, data: ["k": "c", "sort": 2]) + + // snapshot gets triggered by local mutation + expected = [["k": "b", "sort": 1], ["k": "c", "sort": 2]] + querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + XCTAssertEqual(querySnap.metadata.hasPendingWrites, true) + XCTAssertEqual(querySnap.metadata.isFromCache, false) + querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + XCTAssertEqual(querySnap.metadata.hasPendingWrites, true) + XCTAssertEqual(querySnap.metadata.isFromCache, false) + + // Local mutation gets acknowledged by the server + querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + XCTAssertEqual(querySnap.metadata.hasPendingWrites, false) + XCTAssertEqual(querySnap.metadata.isFromCache, false) + querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + XCTAssertEqual(querySnap.metadata.hasPendingWrites, false) + XCTAssertEqual(querySnap.metadata.isFromCache, false) + + defaultAccumulator.assertNoAdditionalEvents() + cacheAccumulator.assertNoAdditionalEvents() + defaultRegistration.remove() + cacheRegistration.remove() + } + + func testCanUnlistenToDefaultSourceWhileStillListeningToCache() throws { + let collRef = collectionRef(withDocuments: [ + "a": ["k": "a", "sort": 0], + "b": ["k": "b", "sort": 1], + ]) + let query = collRef.whereField("sort", isNotEqualTo: 0).order(by: "sort") + + // Listen to the query with both source options + let defaultAccumulator = FSTEventAccumulator.init(forTest: self) + let defaultRegistration = query.addSnapshotListener(defaultAccumulator.valueEventHandler) + defaultAccumulator.awaitEvent(withName: "snapshot") + let cacheAccumulator = FSTEventAccumulator + .init(forTest: self) + let options = SnapshotListenOptions().withSource(ListenSource.cache) + let cacheRegistration = query.addSnapshotListener( + options: options, + listener: cacheAccumulator.valueEventHandler + ) + cacheAccumulator.awaitEvent(withName: "snapshot") + + // Un-listen to the default listener. + defaultRegistration.remove() + + // Add a document and verify listener to cache works as expected + addDocumentRef(collRef, data: ["k": "c", "sort": -1]) + defaultAccumulator.assertNoAdditionalEvents() + + let querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals( + querySnap, + [["k": "c", "sort": -1], ["k": "b", "sort": 1]] + ) + + cacheAccumulator.assertNoAdditionalEvents() + cacheRegistration.remove() + } + + func testCanUnlistenToCacheSourceWhileStillListeningToServer() throws { + let collRef = collectionRef(withDocuments: [ + "a": ["k": "a", "sort": 0], + "b": ["k": "b", "sort": 1], + ]) + let query = collRef.whereField("sort", isNotEqualTo: 0).order(by: "sort") + + // Listen to the query with both source options + let defaultAccumulator = FSTEventAccumulator.init(forTest: self) + let defaultRegistration = query.addSnapshotListener(defaultAccumulator.valueEventHandler) + defaultAccumulator.awaitEvent(withName: "snapshot") + let cacheAccumulator = FSTEventAccumulator + .init(forTest: self) + let options = SnapshotListenOptions().withSource(ListenSource.cache) + let cacheRegistration = query.addSnapshotListener( + options: options, + listener: cacheAccumulator.valueEventHandler + ) + cacheAccumulator.awaitEvent(withName: "snapshot") + + // Un-listen to cache. + cacheRegistration.remove() + + // Add a document and verify listener to server works as expected. + addDocumentRef(collRef, data: ["k": "c", "sort": -1]) + cacheAccumulator.assertNoAdditionalEvents() + + let querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals( + querySnap, + [["k": "c", "sort": -1], ["k": "b", "sort": 1]] + ) + + defaultAccumulator.assertNoAdditionalEvents() + defaultRegistration.remove() + } + + func testCanListenUnlistenRelistenToSameQueryWithDifferentSourceOptions() throws { + let collRef = collectionRef(withDocuments: [ + "a": ["k": "a", "sort": 0], + "b": ["k": "b", "sort": 1], + ]) + let query = collRef.whereField("sort", isGreaterThan: 0).order(by: "sort") + + // Listen to the query with default options, which will also populates the cache + let defaultAccumulator = FSTEventAccumulator.init(forTest: self) + var defaultRegistration = query.addSnapshotListener(defaultAccumulator.valueEventHandler) + var querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + var expected = [["k": "b", "sort": 1]] + try assertQuerySnapshotDataEquals(querySnap, expected) + + // Listen to the same query from cache + let cacheAccumulator = FSTEventAccumulator + .init(forTest: self) + let options = SnapshotListenOptions().withSource(ListenSource.cache) + var cacheRegistration = query.addSnapshotListener( + options: options, + listener: cacheAccumulator.valueEventHandler + ) + querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + + // Un-listen to the default listener, add a doc and re-listen. + defaultRegistration.remove() + addDocumentRef(collRef, data: ["k": "c", "sort": 2]) + + expected = [["k": "b", "sort": 1], ["k": "c", "sort": 2]] + querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + + defaultRegistration = query.addSnapshotListener(defaultAccumulator.valueEventHandler) + querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + + // Un-listen to cache, update a doc, then re-listen to cache. + cacheRegistration.remove() + updateDocumentRef(collRef.document("b"), data: ["k": "b", "sort": 3]) + + expected = [["k": "c", "sort": 2], ["k": "b", "sort": 3]] + querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals( + querySnap, expected + ) + + cacheRegistration = query.addSnapshotListener( + options: options, + listener: cacheAccumulator.valueEventHandler + ) + querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals( + querySnap, expected + ) + + defaultAccumulator.assertNoAdditionalEvents() + cacheAccumulator.assertNoAdditionalEvents() + defaultRegistration.remove() + cacheRegistration.remove() + } + + func testCanListenToCompositeIndexQueriesFromCache() throws { + let collRef = collectionRef(withDocuments: [ + "a": ["k": "a", "sort": 0], + "b": ["k": "b", "sort": 1], + ]) + readDocumentSet(forRef: collRef) // populate the cache. + + let query = collRef.whereField("k", isLessThanOrEqualTo: "a") + .whereField("sort", isGreaterThanOrEqualTo: 0) + + let options = SnapshotListenOptions().withSource(ListenSource.cache) + let registration = query.addSnapshotListener( + options: options, + listener: eventAccumulator.valueEventHandler + ) + + let querySnap = eventAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, [["k": "a", "sort": 0]]) + + eventAccumulator.assertNoAdditionalEvents() + registration.remove() + } + + func testCanRaiseInitialSnapshotFromCachedEmptyResults() throws { + let collRef = collectionRef() + + // Populate the cache with empty query result. + var querySnap = readDocumentSet(forRef: collRef) + try assertQuerySnapshotDataEquals(querySnap, []) + + // Add a snapshot listener whose first event should be raised from cache. + let options = SnapshotListenOptions().withSource(ListenSource.cache) + let registration = collRef.addSnapshotListener( + options: options, + listener: eventAccumulator.valueEventHandler + ) + + querySnap = eventAccumulator.awaitEvent(withName: "initial event") as! QuerySnapshot + try assertQuerySnapshotDataEquals(querySnap, []) + XCTAssertEqual(querySnap.metadata.isFromCache, true) + + eventAccumulator.assertNoAdditionalEvents() + registration.remove() + } + + func testWillNotBeTriggeredByTransactionsWhileListeningToCache() throws { + let collRef = collectionRef() + + // Add a snapshot listener whose first event should be raised from cache. + let options = SnapshotListenOptions().withSource(ListenSource.cache) + let registration = collRef.addSnapshotListener( + options: options, + listener: eventAccumulator.valueEventHandler + ) + let querySnap = eventAccumulator.awaitEvent(withName: "initial event") + try assertQuerySnapshotDataEquals(querySnap, []) + + let docRef = documentRef() + // Use a transaction to perform a write without triggering any local events. + runTransaction(docRef.firestore, block: { transaction, errorPointer -> Any? in + transaction.updateData(["K": "a"], forDocument: docRef) + return nil + }) + + // There should be no events raised + eventAccumulator.assertNoAdditionalEvents() + registration.remove() + } + + func testSharesServerSideUpdatesWhenListeningToBothCacheAndDefault() throws { + let collRef = collectionRef(withDocuments: [ + "a": ["k": "a", "sort": 0], + "b": ["k": "b", "sort": 1], + ]) + let query = collRef.whereField("sort", isGreaterThan: 0).order(by: "sort") + + // Listen to the query with default options, which will also populates the cache + let defaultAccumulator = FSTEventAccumulator.init(forTest: self) + let defaultRegistration = query.addSnapshotListener(defaultAccumulator.valueEventHandler) + var querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + var expected = [["k": "b", "sort": 1]] + try assertQuerySnapshotDataEquals(querySnap, expected) + + // Listen to the same query from cache + let cacheAccumulator = FSTEventAccumulator + .init(forTest: self) + let options = SnapshotListenOptions().withSource(ListenSource.cache) + let cacheRegistration = query.addSnapshotListener( + options: options, + listener: cacheAccumulator.valueEventHandler + ) + querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + + // Use a transaction to mock server side updates + let docRef = collRef.document() + runTransaction(docRef.firestore, block: { transaction, errorPointer -> Any? in + transaction.setData(["k": "c", "sort": 2], forDocument: docRef) + return nil + }) + + // Default listener receives the server update + querySnap = defaultAccumulator.awaitEvent(withName: "snapshot") + expected = [["k": "b", "sort": 1], ["k": "c", "sort": 2]] + try assertQuerySnapshotDataEquals(querySnap, expected) + XCTAssertEqual(querySnap.metadata.isFromCache, false) + + // Cache listener raises snapshot as well + querySnap = cacheAccumulator.awaitEvent(withName: "snapshot") + try assertQuerySnapshotDataEquals(querySnap, expected) + XCTAssertEqual(querySnap.metadata.isFromCache, false) + + defaultAccumulator.assertNoAdditionalEvents() + cacheAccumulator.assertNoAdditionalEvents() + defaultRegistration.remove() + cacheRegistration.remove() + } +} From 8c5dcaf07b25c6034f57858e17da6d7edfe2b8a0 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:00:14 -0500 Subject: [PATCH 15/17] resolve comments --- Firestore/Source/API/FIRSnapshotListenOptions.mm | 13 ++++++------- .../FirebaseFirestore/FIRSnapshotListenOptions.h | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Firestore/Source/API/FIRSnapshotListenOptions.mm b/Firestore/Source/API/FIRSnapshotListenOptions.mm index c56f5a22a15..9e22d686428 100644 --- a/Firestore/Source/API/FIRSnapshotListenOptions.mm +++ b/Firestore/Source/API/FIRSnapshotListenOptions.mm @@ -25,9 +25,8 @@ @implementation FIRSnapshotListenOptions -// private constructor method -- (instancetype)initPrivateWithSource:(FIRListenSource)source - includeMetadataChanges:(BOOL)includeMetadataChanges { +- (instancetype)initPrivate:(FIRListenSource)source + includeMetadataChanges:(BOOL)includeMetadataChanges { self = [self init]; if (self) { _source = source; @@ -47,15 +46,15 @@ - (instancetype)init { - (FIRSnapshotListenOptions *)optionsWithIncludeMetadataChanges:(BOOL)includeMetadataChanges { FIRSnapshotListenOptions *newOptions = - [[FIRSnapshotListenOptions alloc] initPrivateWithSource:self.source - includeMetadataChanges:includeMetadataChanges]; + [[FIRSnapshotListenOptions alloc] initPrivate:self.source + includeMetadataChanges:includeMetadataChanges]; return newOptions; } - (FIRSnapshotListenOptions *)optionsWithSource:(FIRListenSource)source { FIRSnapshotListenOptions *newOptions = - [[FIRSnapshotListenOptions alloc] initPrivateWithSource:source - includeMetadataChanges:self.includeMetadataChanges]; + [[FIRSnapshotListenOptions alloc] initPrivate:source + includeMetadataChanges:self.includeMetadataChanges]; return newOptions; } diff --git a/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h b/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h index 17c359256f1..7f77139a7cb 100644 --- a/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h +++ b/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h @@ -29,9 +29,9 @@ typedef NS_ENUM(NSUInteger, FIRListenSource) { */ FIRListenSourceDefault, /** - * The listener retrieves data and listens to updates from the local Firestore cache only. If the - * cache is empty, an empty snapshot will be returned. Snapshot events will be triggered on cache - * updates, like local mutations or load bundles. + * The listener retrieves data and listens to updates from the local Firestore cache without + * attempting to send the query to the server. If some documents gets updated as a result from + * other queries, they will be picked up by listeners using Cache. * * Note that the data might be stale if the cache hasn't synchronized with recent server-side * changes. From 663beef98994a0e21b12662e96530984b4eeb82d Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 6 Mar 2024 10:08:12 -0500 Subject: [PATCH 16/17] resolve comments --- .../FirebaseFirestore/FIRSnapshotListenOptions.h | 16 ++++++++-------- Firestore/core/src/api/listen_source.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h b/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h index 7f77139a7cb..13d9903abef 100644 --- a/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h +++ b/Firestore/Source/Public/FirebaseFirestore/FIRSnapshotListenOptions.h @@ -19,19 +19,19 @@ NS_ASSUME_NONNULL_BEGIN /** - * The source that snapshot listener retrieves data from. + * The source the snapshot listener retrieves data from. */ typedef NS_ENUM(NSUInteger, FIRListenSource) { /** * The default behavior. The listener attempts to return initial snapshot from cache and retrieve * up-to-date snapshots from the Firestore server. Snapshot events will be triggered on local - * mutations and server side updates. + * mutations and server-side updates. */ FIRListenSourceDefault, /** * The listener retrieves data and listens to updates from the local Firestore cache without * attempting to send the query to the server. If some documents gets updated as a result from - * other queries, they will be picked up by listeners using Cache. + * other queries, they will be picked up by listeners using the cache. * * Note that the data might be stale if the cache hasn't synchronized with recent server-side * changes. @@ -47,7 +47,7 @@ typedef NS_ENUM(NSUInteger, FIRListenSource) { NS_SWIFT_NAME(SnapshotListenOptions) @interface FIRSnapshotListenOptions : NSObject -/** The source that snapshot listener retrieves data from. */ +/** The source the snapshot listener retrieves data from. */ @property(nonatomic, readonly) FIRListenSource source; /** Indicates whether metadata-only changes should trigger snapshot events. */ @property(nonatomic, readonly) BOOL includeMetadataChanges; @@ -62,8 +62,8 @@ NS_SWIFT_NAME(SnapshotListenOptions) /** * Creates and returns a new `SnapshotListenOptions` object with with all properties of the current - * `SnapshotListenOptions` object and the new configuration of whether metadata-only changes should - * trigger snapshot events. + * `SnapshotListenOptions` object plus the new property specifying whether metadata-only changes + * should trigger snapshot events * * @return The created `SnapshotListenOptions` object. */ @@ -71,8 +71,8 @@ NS_SWIFT_NAME(SnapshotListenOptions) /** * Creates and returns a new `SnapshotListenOptions` object with with all properties of the current - * `SnapshotListenOptions` object and the new configuration of the source that snapshot listener - * listens to. + * `SnapshotListenOptions` object plus the new property specifying the source that the snapshot + * listener listens to. * * @return The created `SnapshotListenOptions` object. */ diff --git a/Firestore/core/src/api/listen_source.h b/Firestore/core/src/api/listen_source.h index 2c062f84c52..2053a92df39 100644 --- a/Firestore/core/src/api/listen_source.h +++ b/Firestore/core/src/api/listen_source.h @@ -22,8 +22,8 @@ namespace firestore { namespace api { /** - * An enum that configures the source of snapshot listeners listens to. By - * providing a source enum, snapshot events triggered by local cache changes + * An enum that configures the snapshot listener data source. Using this enum, + * specify whether snapshot events are triggered by local cache changes * only, or from both local cache and watch changes(which is the default). * * See `FIRFirestoreListenSource` for more details. From 106a0e323a68f0909036817d3b25b127b174715e Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 8 Mar 2024 13:20:23 -0500 Subject: [PATCH 17/17] update spec tests --- .../Example/Tests/SpecTests/FSTSpecTests.mm | 2 +- .../json/listen_source_spec_test.json | 60 +++++++++---------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm index acec5291ae3..f2b8ca2e4be 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm @@ -393,7 +393,7 @@ - (ListenOptions)parseOptions:(NSDictionary *)optionsSpec { if (optionsSpec != nil) { ListenSource source = - [optionsSpec[@"source"] intValue] == 1 ? ListenSource::Cache : ListenSource::Default; + [optionsSpec[@"source"] isEqual:@"cache"] ? ListenSource::Cache : ListenSource::Default; // include_metadata_changes are default to true in spec tests options = ListenOptions::FromOptions(true, source); } diff --git a/Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json b/Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json index 21e67e668c2..1912afc320f 100644 --- a/Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json +++ b/Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json @@ -126,7 +126,7 @@ "clientIndex": 0, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -247,7 +247,7 @@ "clientIndex": 1, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -474,7 +474,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -551,7 +551,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -615,7 +615,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -788,7 +788,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -867,7 +867,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -1046,7 +1046,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -1244,7 +1244,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -1412,7 +1412,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -1575,7 +1575,7 @@ "clientIndex": 1, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -1627,7 +1627,7 @@ "clientIndex": 2, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -1845,7 +1845,7 @@ "clientIndex": 0, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -1885,7 +1885,7 @@ "clientIndex": 1, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -2165,7 +2165,7 @@ "clientIndex": 1, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -2569,7 +2569,7 @@ "clientIndex": 0, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -2645,7 +2645,7 @@ "clientIndex": 1, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -3007,7 +3007,7 @@ "clientIndex": 1, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -3079,7 +3079,7 @@ "clientIndex": 1, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -3436,7 +3436,7 @@ "clientIndex": 0, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -3787,7 +3787,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -3997,7 +3997,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -4215,7 +4215,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -4425,7 +4425,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -4688,7 +4688,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -4766,7 +4766,7 @@ "clientIndex": 1, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -4862,7 +4862,7 @@ "clientIndex": 0, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -5131,7 +5131,7 @@ "clientIndex": 2, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -5462,7 +5462,7 @@ "clientIndex": 1, "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [ @@ -5720,7 +5720,7 @@ { "userListen": { "options": { - "source": 1 + "source": "cache" }, "query": { "filters": [