aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.travis.yml28
-rw-r--r--CMakeLists.txt2
-rw-r--r--Example/Core/Tests/FIROptionsTest.m48
-rw-r--r--Firebase/Core/FIROptions.m18
-rw-r--r--FirebaseFirestore.podspec1
-rw-r--r--Firestore/CMakeLists.txt50
-rw-r--r--Firestore/Example/Firestore.xcodeproj/project.pbxproj17
-rw-r--r--Firestore/Example/Tests/Local/FSTLocalSerializerTests.m2
-rw-r--r--Firestore/Example/Tests/Local/FSTQueryCacheTests.m80
-rw-r--r--Firestore/Example/Tests/Remote/FSTSerializerBetaTests.m15
-rw-r--r--Firestore/Example/Tests/SpecTests/FSTSpecTests.m1
-rw-r--r--Firestore/Source/Core/FSTListenSequence.h37
-rw-r--r--Firestore/Source/Core/FSTListenSequence.m34
-rw-r--r--Firestore/Source/Core/FSTSyncEngine.m5
-rw-r--r--Firestore/Source/Core/FSTTypes.h2
-rw-r--r--Firestore/Source/Local/FSTLevelDBQueryCache.mm15
-rw-r--r--Firestore/Source/Local/FSTLocalSerializer.m3
-rw-r--r--Firestore/Source/Local/FSTLocalStore.m12
-rw-r--r--Firestore/Source/Local/FSTMemoryQueryCache.m9
-rw-r--r--Firestore/Source/Local/FSTQueryCache.h5
-rw-r--r--Firestore/Source/Local/FSTQueryData.h4
-rw-r--r--Firestore/Source/Local/FSTQueryData.m5
-rw-r--r--Firestore/Source/Remote/FSTRemoteStore.m7
-rw-r--r--Firestore/core/CMakeLists.txt5
-rw-r--r--Firestore/core/src/firebase/firestore/model/CMakeLists.txt21
-rw-r--r--Firestore/core/src/firebase/firestore/model/field_value.cc177
-rw-r--r--Firestore/core/src/firebase/firestore/model/field_value.h130
-rw-r--r--Firestore/core/src/firebase/firestore/remote/CMakeLists.txt22
-rw-r--r--Firestore/core/src/firebase/firestore/remote/datastore.cc31
-rw-r--r--Firestore/core/src/firebase/firestore/remote/datastore.h40
-rw-r--r--Firestore/core/src/firebase/firestore/util/CMakeLists.txt92
-rw-r--r--Firestore/core/src/firebase/firestore/util/assert_stdio.cc6
-rw-r--r--Firestore/core/src/firebase/firestore/util/firebase_assert.h4
-rw-r--r--Firestore/core/src/firebase/firestore/util/string_printf.cc17
-rw-r--r--Firestore/core/src/firebase/firestore/util/string_printf.h4
-rw-r--r--Firestore/core/test/firebase/firestore/model/CMakeLists.txt21
-rw-r--r--Firestore/core/test/firebase/firestore/model/field_value_test.cc164
-rw-r--r--Firestore/core/test/firebase/firestore/remote/CMakeLists.txt21
-rw-r--r--Firestore/core/test/firebase/firestore/remote/datastore_test.cc28
-rw-r--r--Firestore/core/test/firebase/firestore/util/CMakeLists.txt35
-rw-r--r--Firestore/core/test/firebase/firestore/util/assert_test.cc3
-rw-r--r--Firestore/core/test/firebase/firestore/util/autoid_test.cc4
-rw-r--r--Firestore/core/test/firebase/firestore/util/string_printf_test.cc2
-rw-r--r--cmake/CompilerSetup.cmake92
-rw-r--r--cmake/ExternalProjectFlags.cmake71
-rw-r--r--cmake/FindGRPC.cmake142
-rw-r--r--cmake/FindLevelDB.cmake2
-rw-r--r--cmake/external/firestore.cmake27
-rw-r--r--cmake/external/googletest.cmake25
-rw-r--r--cmake/external/grpc.cmake83
-rw-r--r--cmake/external/leveldb.cmake26
-rw-r--r--cmake/utils.cmake80
-rw-r--r--cmake/xcodebuild.cmake11
-rwxr-xr-xscripts/style.sh14
-rwxr-xr-xtest.sh3
55 files changed, 1590 insertions, 213 deletions
diff --git a/.travis.yml b/.travis.yml
index 18b04e2..a2719b1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,13 +12,37 @@ before_install:
- gem install xcpretty
- bundle exec pod install --project-directory=Example --repo-update
- bundle exec pod install --project-directory=Firestore/Example --no-repo-update
+ - brew install clang-format
+ - echo "$TRAVIS_COMMIT_RANGE"
+ - echo "$TRAVIS_PULL_REQUEST"
+ - |
+ if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
+ SKIP_FIREBASE=0
+ SKIP_FIRESTORE=0
+ else
+ git diff --name-only $TRAVIS_COMMIT_RANGE | grep -Eq '^(Firebase|Example)'
+ SKIP_FIREBASE="$?"
+ git diff --name-only $TRAVIS_COMMIT_RANGE | grep -q Firestore
+ SKIP_FIRESTORE="$?"
+ fi
script:
- "! git grep -I ' $'" # Fail on trailing whitespace in non-binary files
- - ./test.sh
+ - ./scripts/style.sh test-only # Validate clang-format compliance
+ - |
+ if [ $SKIP_FIREBASE != 1 ]; then
+ ./test.sh
+ fi
+ - |
+ if [ $SKIP_FIRESTORE != 1 ]; then
+ ./Firestore/test.sh
+ fi
# TODO fix os_log deprecation warning in FIRLogger to remove --allow-warnings
- - bundle exec pod lib lint FirebaseCore.podspec --allow-warnings
+ - |
+ if [ $SKIP_FIREBASE != 1 ]; then
+ bundle exec pod lib lint FirebaseCore.podspec --allow-warnings
+ fi
# TODO - Uncomment subsequent lines once FirebaseCore source repo is in public Specs repo
# - bundle exec pod lib lint FirebaseAuth.podspec
diff --git a/CMakeLists.txt b/CMakeLists.txt
index edcd611..f58a980 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,7 +37,7 @@ set(FIREBASE_INSTALL_DIR ${PROJECT_BINARY_DIR})
enable_testing()
include(external/FirebaseCore)
-
include(external/googletest)
include(external/leveldb)
+include(external/grpc)
include(external/firestore)
diff --git a/Example/Core/Tests/FIROptionsTest.m b/Example/Core/Tests/FIROptionsTest.m
index d27a0e1..8170405 100644
--- a/Example/Core/Tests/FIROptionsTest.m
+++ b/Example/Core/Tests/FIROptionsTest.m
@@ -25,7 +25,8 @@ extern NSString *const kFIRLibraryVersionID;
@interface FIROptions (Test)
-@property(nonatomic, readonly) NSDictionary *analyticsOptionsDictionary;
+- (nullable NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:
+ (nullable NSDictionary *)infoDictionary;
@end
@@ -263,22 +264,20 @@ extern NSString *const kFIRLibraryVersionID;
}
- (void)testAnalyticsOptions {
- id mainBundleMock = OCMPartialMock([NSBundle mainBundle]);
-
// No keys anywhere.
NSDictionary *optionsDictionary = nil;
FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
NSDictionary *mainDictionary = nil;
- OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary);
NSDictionary *expectedAnalyticsOptions = @{};
- XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions);
+ NSDictionary *analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:nil];
+ XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
optionsDictionary = @{};
options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
mainDictionary = @{};
- OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary);
expectedAnalyticsOptions = @{};
- XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions);
+ analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
+ XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
// Main has no keys.
optionsDictionary = @{
@@ -288,9 +287,9 @@ extern NSString *const kFIRLibraryVersionID;
};
options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
mainDictionary = @{};
- OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary);
expectedAnalyticsOptions = optionsDictionary;
- XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions);
+ analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
+ XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
// Main overrides all the keys.
optionsDictionary = @{
@@ -304,9 +303,9 @@ extern NSString *const kFIRLibraryVersionID;
kFIRIsAnalyticsCollectionEnabled : @NO,
kFIRIsMeasurementEnabled : @NO
};
- OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary);
expectedAnalyticsOptions = mainDictionary;
- XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions);
+ analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
+ XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
// Keys exist only in main.
optionsDictionary = @{};
@@ -316,9 +315,9 @@ extern NSString *const kFIRLibraryVersionID;
kFIRIsAnalyticsCollectionEnabled : @YES,
kFIRIsMeasurementEnabled : @YES
};
- OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary);
expectedAnalyticsOptions = mainDictionary;
- XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions);
+ analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
+ XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
// Main overrides single keys.
optionsDictionary = @{
@@ -328,13 +327,13 @@ extern NSString *const kFIRLibraryVersionID;
};
options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
mainDictionary = @{ kFIRIsAnalyticsCollectionDeactivated : @NO };
- OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary);
expectedAnalyticsOptions = @{
kFIRIsAnalyticsCollectionDeactivated : @NO, // override
kFIRIsAnalyticsCollectionEnabled : @YES,
kFIRIsMeasurementEnabled : @YES
};
- XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions);
+ analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
+ XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
optionsDictionary = @{
kFIRIsAnalyticsCollectionDeactivated : @YES,
@@ -343,13 +342,13 @@ extern NSString *const kFIRLibraryVersionID;
};
options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
mainDictionary = @{ kFIRIsAnalyticsCollectionEnabled : @NO };
- OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary);
expectedAnalyticsOptions = @{
kFIRIsAnalyticsCollectionDeactivated : @YES,
kFIRIsAnalyticsCollectionEnabled : @NO, // override
kFIRIsMeasurementEnabled : @YES
};
- XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions);
+ analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
+ XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
optionsDictionary = @{
kFIRIsAnalyticsCollectionDeactivated : @YES,
@@ -358,18 +357,18 @@ extern NSString *const kFIRLibraryVersionID;
};
options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
mainDictionary = @{ kFIRIsMeasurementEnabled : @NO };
- OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary);
expectedAnalyticsOptions = @{
kFIRIsAnalyticsCollectionDeactivated : @YES,
kFIRIsAnalyticsCollectionEnabled : @YES,
kFIRIsMeasurementEnabled : @NO // override
};
- XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions);
+ analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
+ XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
}
- (void)testAnalyticsOptions_combinatorial {
// Complete combinatorial test.
- id mainBundleMock = OCMPartialMock([NSBundle mainBundle]);
+
// Possible values for the flags in the plist, where NSNull means the flag is not present.
NSArray *values = @[ [NSNull null], @NO, @YES ];
@@ -398,6 +397,7 @@ extern NSString *const kFIRLibraryVersionID;
if (![optionsMeasurementEnabled isEqual:[NSNull null]]) {
optionsDictionary[kFIRIsMeasurementEnabled] = optionsMeasurementEnabled;
}
+
FIROptions *options =
[[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary];
if (![uniqueOptionsCombinations containsObject:optionsDictionary]) {
@@ -415,7 +415,8 @@ extern NSString *const kFIRLibraryVersionID;
if (![mainMeasurementEnabled isEqual:[NSNull null]]) {
mainDictionary[kFIRIsMeasurementEnabled] = mainMeasurementEnabled;
}
- OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary);
+
+ // Add mainDictionary to uniqueMainCombinations if it isn't included yet.
if (![uniqueMainCombinations containsObject:mainDictionary]) {
[uniqueMainCombinations addObject:mainDictionary];
}
@@ -427,7 +428,10 @@ extern NSString *const kFIRLibraryVersionID;
NSMutableDictionary *expectedAnalyticsOptions =
[[NSMutableDictionary alloc] initWithDictionary:optionsDictionary];
[expectedAnalyticsOptions addEntriesFromDictionary:mainDictionary];
- XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions);
+
+ NSDictionary *analyticsOptions =
+ [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary];
+ XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions);
combinationCount++;
}
diff --git a/Firebase/Core/FIROptions.m b/Firebase/Core/FIROptions.m
index aecb95d..fc92a55 100644
--- a/Firebase/Core/FIROptions.m
+++ b/Firebase/Core/FIROptions.m
@@ -62,12 +62,19 @@ NSString *const kFIRExceptionBadModification =
@property(nonatomic, readwrite) NSMutableDictionary *optionsDictionary;
/**
- * Combination of analytics options from both the main plist and the GoogleService-Info.plist.
+ * Calls `analyticsOptionsDictionaryWithInfoDictionary:` using [NSBundle mainBundle].infoDictionary.
+ * It combines analytics options from both the infoDictionary and the GoogleService-Info.plist.
* Values which are present in the main plist override values from the GoogleService-Info.plist.
*/
@property(nonatomic, readonly) NSDictionary *analyticsOptionsDictionary;
/**
+ * Combination of analytics options from both the infoDictionary and the GoogleService-Info.plist.
+ * Values which are present in the infoDictionary override values from the GoogleService-Info.plist.
+ */
+- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary;
+
+/**
* Throw exception if editing is locked when attempting to modify an option.
*/
- (void)checkEditingLocked;
@@ -346,16 +353,15 @@ static NSDictionary *sDefaultOptionsDictionary = nil;
#pragma mark - Internal instance methods
-- (NSDictionary *)analyticsOptionsDictionary {
+- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary {
dispatch_once(&_createAnalyticsOptionsDictionaryOnce, ^{
NSMutableDictionary *tempAnalyticsOptions = [[NSMutableDictionary alloc] init];
- NSDictionary *mainInfoDictionary = [NSBundle mainBundle].infoDictionary;
NSArray *measurementKeys = @[
kFIRIsMeasurementEnabled, kFIRIsAnalyticsCollectionEnabled,
kFIRIsAnalyticsCollectionDeactivated
];
for (NSString *key in measurementKeys) {
- id value = mainInfoDictionary[key] ?: self.optionsDictionary[key] ?: nil;
+ id value = infoDictionary[key] ?: self.optionsDictionary[key] ?: nil;
if (!value) {
continue;
}
@@ -366,6 +372,10 @@ static NSDictionary *sDefaultOptionsDictionary = nil;
return _analyticsOptionsDictionary;
}
+- (NSDictionary *)analyticsOptionsDictionary {
+ return [self analyticsOptionsDictionaryWithInfoDictionary:[NSBundle mainBundle].infoDictionary];
+}
+
/**
* Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
* GoogleService-Info.plist. This uses the old plist flag IS_MEASUREMENT_ENABLED, which should still
diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec
index 476f196..b6b2966 100644
--- a/FirebaseFirestore.podspec
+++ b/FirebaseFirestore.podspec
@@ -46,6 +46,7 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling,
'Firestore/third_party/Immutable/Tests/**',
# Exclude alternate implementations for other platforms
+ 'Firestore/core/src/firebase/firestore/util/assert_stdio.cc',
'Firestore/core/src/firebase/firestore/util/log_stdio.cc'
]
s.public_header_files = 'Firestore/Source/Public/*.h'
diff --git a/Firestore/CMakeLists.txt b/Firestore/CMakeLists.txt
index 9b90815..499e06c 100644
--- a/Firestore/CMakeLists.txt
+++ b/Firestore/CMakeLists.txt
@@ -13,9 +13,9 @@
# limitations under the License.
cmake_minimum_required(VERSION 2.8.11)
-project(firestore)
+project(firestore C CXX)
-set(FIREBASE_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/..")
+set(FIREBASE_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
# CMAKE_INSTALL_PREFIX should be passed in to this build so that it can find
# outputs of the superbuild. This is handled automatically if run via the
@@ -23,39 +23,41 @@ set(FIREBASE_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/..")
#
# If you want to use this project directly in e.g. CLion, make sure you
# configure this.
-set(FIREBASE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}")
+set(FIREBASE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
list(INSERT CMAKE_MODULE_PATH 0 ${FIREBASE_SOURCE_DIR}/cmake)
include(utils)
-find_package(GTest REQUIRED)
+# Include GoogleTest directly in the build.
+set(gtest_dir ${FIREBASE_INSTALL_DIR}/external/googletest)
+add_subdirectory(
+ ${gtest_dir}/src/googletest
+ ${gtest_dir}/src/googletest-build
+ EXCLUDE_FROM_ALL
+)
+
+# Set up aliases with the same names as available via FindGTest.
+add_library(
+ GTest::GTest ALIAS gtest
+)
+
+add_library(
+ GTest::Main ALIAS gtest_main
+)
+
find_package(LevelDB REQUIRED)
+find_package(GRPC REQUIRED)
if(APPLE)
find_package(FirebaseCore REQUIRED)
endif()
-# We use C++11
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
+enable_testing()
+add_subdirectory(third_party/abseil-cpp)
+
+include(CompilerSetup)
# Fully qualified imports, project wide
-include_directories("${FIREBASE_SOURCE_DIR}")
-
-if(APPLE)
- # CMake has no special support for Objective-C as a distinct language but enabling modules and
- # other clang extensions would apply even to regular C++ sources which is nonportable. Keep these
- # flags separate to avoid misuse.
- set(
- OBJC_FLAGS
- -fobjc-arc
- -fmodules
- -fno-autolink
- -F${FIREBASE_INSTALL_DIR}/Frameworks
- )
-endif(APPLE)
+include_directories(${FIREBASE_SOURCE_DIR})
-enable_testing()
-add_subdirectory(third_party/abseil-cpp EXCLUDE_FROM_ALL)
add_subdirectory(core)
diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj
index 06f790c..538758d 100644
--- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj
+++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj
@@ -29,6 +29,7 @@
54740A581FC914F000713A1A /* autoid_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A521FC913E500713A1A /* autoid_test.cc */; };
54764FAB1FAA0C320085E60A /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAA1FAA0C320085E60A /* string_util_test.cc */; };
54764FAF1FAA21B90085E60A /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; };
+ 548DB927200D590300E00ABC /* assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB926200D590300E00ABC /* assert_test.cc */; };
5491BC721FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; };
5491BC731FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; };
54C2294F1FECABAE007D065B /* log_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54C2294E1FECABAE007D065B /* log_test.cc */; };
@@ -63,6 +64,7 @@
6ED54761B845349D43DB6B78 /* Pods_Firestore_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75A6FE51C1A02DF38F62FAAD /* Pods_Firestore_Example.framework */; };
71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; };
873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; };
+ AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB356EF6200EA5EB0089B766 /* field_value_test.cc */; };
AB382F7C1FE02A1F007CA955 /* FIRDocumentReferenceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB382F7B1FE02A1F007CA955 /* FIRDocumentReferenceTests.m */; };
AB382F7E1FE03059007CA955 /* FIRFieldPathTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB382F7D1FE03059007CA955 /* FIRFieldPathTests.m */; };
AB9945261FE2D71100DFC1E6 /* FIRCollectionReferenceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB9945251FE2D71100DFC1E6 /* FIRCollectionReferenceTests.m */; };
@@ -201,6 +203,7 @@
54740A531FC913E500713A1A /* secure_random_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = secure_random_test.cc; path = ../../core/test/firebase/firestore/util/secure_random_test.cc; sourceTree = "<group>"; };
54764FAA1FAA0C320085E60A /* string_util_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_util_test.cc; path = ../../Port/string_util_test.cc; sourceTree = "<group>"; };
54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = FSTGoogleTestTests.mm; path = GoogleTest/FSTGoogleTestTests.mm; sourceTree = "<group>"; };
+ 548DB926200D590300E00ABC /* assert_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = assert_test.cc; path = ../../core/test/firebase/firestore/util/assert_test.cc; sourceTree = "<group>"; };
5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTIntegrationTestCase.mm; sourceTree = "<group>"; };
54C2294E1FECABAE007D065B /* log_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = log_test.cc; path = ../../core/test/firebase/firestore/util/log_test.cc; sourceTree = "<group>"; };
54DA129C1F315EE100DD57A1 /* collection_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = collection_spec_test.json; sourceTree = "<group>"; };
@@ -243,6 +246,7 @@
8E002F4AD5D9B6197C940847 /* Firestore.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Firestore.podspec; path = ../Firestore.podspec; sourceTree = "<group>"; };
9D52E67EE96AA7E5D6F69748 /* Pods-Firestore_IntegrationTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests/Pods-Firestore_IntegrationTests.debug.xcconfig"; sourceTree = "<group>"; };
9EF477AD4B2B643FD320867A /* Pods-Firestore_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example/Pods-Firestore_Example.debug.xcconfig"; sourceTree = "<group>"; };
+ AB356EF6200EA5EB0089B766 /* field_value_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = field_value_test.cc; sourceTree = "<group>"; };
AB382F7B1FE02A1F007CA955 /* FIRDocumentReferenceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRDocumentReferenceTests.m; sourceTree = "<group>"; };
AB382F7D1FE03059007CA955 /* FIRFieldPathTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRFieldPathTests.m; sourceTree = "<group>"; };
AB9945251FE2D71100DFC1E6 /* FIRCollectionReferenceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRCollectionReferenceTests.m; sourceTree = "<group>"; };
@@ -394,6 +398,7 @@
54740A561FC913EB00713A1A /* util */ = {
isa = PBXGroup;
children = (
+ 548DB926200D590300E00ABC /* assert_test.cc */,
54740A521FC913E500713A1A /* autoid_test.cc */,
54C2294E1FECABAE007D065B /* log_test.cc */,
54740A531FC913E500713A1A /* secure_random_test.cc */,
@@ -405,6 +410,7 @@
54764FAC1FAA0C390085E60A /* GoogleTests */ = {
isa = PBXGroup;
children = (
+ AB356EF5200E9D1A0089B766 /* model */,
54764FAD1FAA0C650085E60A /* Port */,
54740A561FC913EB00713A1A /* util */,
54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */,
@@ -537,6 +543,15 @@
name = Pods;
sourceTree = "<group>";
};
+ AB356EF5200E9D1A0089B766 /* model */ = {
+ isa = PBXGroup;
+ children = (
+ AB356EF6200EA5EB0089B766 /* field_value_test.cc */,
+ );
+ name = model;
+ path = ../../core/test/firebase/firestore/model;
+ sourceTree = "<group>";
+ };
DE0761E51F2FE611003233AF /* SwiftBuildTest */ = {
isa = PBXGroup;
children = (
@@ -1217,6 +1232,7 @@
DE51B1D41F0D48CD0013853F /* FSTViewTests.m in Sources */,
54740A581FC914F000713A1A /* autoid_test.cc in Sources */,
DE51B1F41F0D491B0013853F /* FSTRemoteEventTests.m in Sources */,
+ 548DB927200D590300E00ABC /* assert_test.cc in Sources */,
54E928241F33953300C1953E /* FSTEventAccumulator.m in Sources */,
DE51B1D11F0D48CD0013853F /* FSTTargetIDGeneratorTests.m in Sources */,
5436F32420008FAD006E51E3 /* string_printf_test.cc in Sources */,
@@ -1232,6 +1248,7 @@
DE51B1F11F0D49140013853F /* FSTMutationTests.m in Sources */,
DE51B1FB1F0D492C0013853F /* FSTMemorySpecTests.m in Sources */,
DE51B1DB1F0D490D0013853F /* FSTLevelDBQueryCacheTests.m in Sources */,
+ AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */,
54764FAB1FAA0C320085E60A /* string_util_test.cc in Sources */,
54E9282C1F339CAD00C1953E /* XCTestCase+Await.m in Sources */,
AB99452E1FE30AC800DFC1E6 /* FIRFieldValueTests.m in Sources */,
diff --git a/Firestore/Example/Tests/Local/FSTLocalSerializerTests.m b/Firestore/Example/Tests/Local/FSTLocalSerializerTests.m
index 27c3dc3..95b9b11 100644
--- a/Firestore/Example/Tests/Local/FSTLocalSerializerTests.m
+++ b/Firestore/Example/Tests/Local/FSTLocalSerializerTests.m
@@ -157,6 +157,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTQueryData *queryData = [[FSTQueryData alloc] initWithQuery:query
targetID:targetID
+ listenSequenceNumber:10
purpose:FSTQueryPurposeListen
snapshotVersion:version
resumeToken:resumeToken];
@@ -166,6 +167,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTPBTarget *expected = [FSTPBTarget message];
expected.targetId = targetID;
+ expected.lastListenSequenceNumber = 10;
expected.snapshotVersion.nanos = 1039000;
expected.resumeToken = [resumeToken copy];
expected.query.parent = queryTarget.parent;
diff --git a/Firestore/Example/Tests/Local/FSTQueryCacheTests.m b/Firestore/Example/Tests/Local/FSTQueryCacheTests.m
index 0b80bd9..0c6a2a4 100644
--- a/Firestore/Example/Tests/Local/FSTQueryCacheTests.m
+++ b/Firestore/Example/Tests/Local/FSTQueryCacheTests.m
@@ -31,12 +31,18 @@ NS_ASSUME_NONNULL_BEGIN
@implementation FSTQueryCacheTests {
FSTQuery *_queryRooms;
+ FSTListenSequenceNumber _previousSequenceNumber;
+ FSTTargetID _previousTargetID;
+ FSTTestSnapshotVersion _previousSnapshotVersion;
}
- (void)setUp {
[super setUp];
_queryRooms = FSTTestQuery(@"rooms");
+ _previousSequenceNumber = 1000;
+ _previousTargetID = 500;
+ _previousSnapshotVersion = 100;
}
/**
@@ -56,7 +62,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testSetAndReadAQuery {
if ([self isTestBaseClass]) return;
- FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms targetID:1 version:1];
+ FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms];
[self addQueryData:queryData];
FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms];
@@ -74,14 +80,14 @@ NS_ASSUME_NONNULL_BEGIN
FSTQuery *q2 = [FSTTestQuery(@"a") queryByAddingFilter:FSTTestFilter(@"foo", @"==", @"1")];
XCTAssertEqualObjects(q1.canonicalID, q2.canonicalID);
- FSTQueryData *data1 = [self queryDataWithQuery:q1 targetID:1 version:1];
+ FSTQueryData *data1 = [self queryDataWithQuery:q1];
[self addQueryData:data1];
// Using the other query should not return the query cache entry despite equal canonicalIDs.
XCTAssertNil([self.queryCache queryDataForQuery:q2]);
XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1);
- FSTQueryData *data2 = [self queryDataWithQuery:q2 targetID:2 version:1];
+ FSTQueryData *data2 = [self queryDataWithQuery:q2];
[self addQueryData:data2];
XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1);
@@ -99,10 +105,12 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testSetQueryToNewValue {
if ([self isTestBaseClass]) return;
- FSTQueryData *queryData1 = [self queryDataWithQuery:_queryRooms targetID:1 version:1];
+ FSTQueryData *queryData1 =
+ [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:1];
[self addQueryData:queryData1];
- FSTQueryData *queryData2 = [self queryDataWithQuery:_queryRooms targetID:1 version:2];
+ FSTQueryData *queryData2 =
+ [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:2];
[self addQueryData:queryData2];
FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms];
@@ -115,7 +123,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testRemoveQuery {
if ([self isTestBaseClass]) return;
- FSTQueryData *queryData1 = [self queryDataWithQuery:_queryRooms targetID:1 version:1];
+ FSTQueryData *queryData1 = [self queryDataWithQuery:_queryRooms];
[self addQueryData:queryData1];
[self removeQueryData:queryData1];
@@ -127,7 +135,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testRemoveNonExistentQuery {
if ([self isTestBaseClass]) return;
- FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms targetID:1 version:1];
+ FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms];
// no-op, but make sure it doesn't throw.
XCTAssertNoThrow([self removeQueryData:queryData]);
@@ -136,7 +144,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testRemoveQueryRemovesMatchingKeysToo {
if ([self isTestBaseClass]) return;
- FSTQueryData *rooms = [self queryDataWithQuery:_queryRooms targetID:1 version:1];
+ FSTQueryData *rooms = [self queryDataWithQuery:_queryRooms];
[self addQueryData:rooms];
FSTDocumentKey *key1 = FSTTestDocKey(@"rooms/foo");
@@ -204,14 +212,14 @@ NS_ASSUME_NONNULL_BEGIN
[garbageCollector addGarbageSource:self.queryCache];
FSTAssertEqualSets([garbageCollector collectGarbage], @[]);
- FSTQueryData *rooms = [self queryDataWithQuery:FSTTestQuery(@"rooms") targetID:1 version:1];
+ FSTQueryData *rooms = [self queryDataWithQuery:FSTTestQuery(@"rooms")];
FSTDocumentKey *room1 = FSTTestDocKey(@"rooms/bar");
FSTDocumentKey *room2 = FSTTestDocKey(@"rooms/foo");
[self addQueryData:rooms];
[self addMatchingKey:room1 forTargetID:rooms.targetID];
[self addMatchingKey:room2 forTargetID:rooms.targetID];
- FSTQueryData *halls = [self queryDataWithQuery:FSTTestQuery(@"halls") targetID:2 version:1];
+ FSTQueryData *halls = [self queryDataWithQuery:FSTTestQuery(@"halls")];
FSTDocumentKey *hall1 = FSTTestDocKey(@"halls/bar");
FSTDocumentKey *hall2 = FSTTestDocKey(@"halls/foo");
[self addQueryData:halls];
@@ -249,6 +257,46 @@ NS_ASSUME_NONNULL_BEGIN
FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:2], (@[ key1, key3 ]));
}
+- (void)testHighestListenSequenceNumber {
+ if ([self isTestBaseClass]) return;
+
+ FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"rooms")
+ targetID:1
+ listenSequenceNumber:10
+ purpose:FSTQueryPurposeListen];
+ [self addQueryData:query1];
+ FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"halls")
+ targetID:2
+ listenSequenceNumber:20
+ purpose:FSTQueryPurposeListen];
+ [self addQueryData:query2];
+ XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20);
+
+ // TargetIDs never come down.
+ [self removeQueryData:query2];
+ XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20);
+
+ // A query with an empty result set still counts.
+ FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"garages")
+ targetID:42
+ listenSequenceNumber:100
+ purpose:FSTQueryPurposeListen];
+ [self addQueryData:query3];
+ XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100);
+
+ [self removeQueryData:query1];
+ XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100);
+
+ [self removeQueryData:query3];
+ XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100);
+
+ // Verify that the highestTargetID even survives restarts.
+ [self.queryCache shutdown];
+ self.queryCache = [self.persistence queryCache];
+ [self.queryCache start];
+ XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100);
+}
+
- (void)testHighestTargetID {
if ([self isTestBaseClass]) return;
@@ -256,6 +304,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"rooms")
targetID:1
+ listenSequenceNumber:10
purpose:FSTQueryPurposeListen];
FSTDocumentKey *key1 = FSTTestDocKey(@"rooms/bar");
FSTDocumentKey *key2 = FSTTestDocKey(@"rooms/foo");
@@ -265,6 +314,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"halls")
targetID:2
+ listenSequenceNumber:20
purpose:FSTQueryPurposeListen];
FSTDocumentKey *key3 = FSTTestDocKey(@"halls/foo");
[self addQueryData:query2];
@@ -278,6 +328,7 @@ NS_ASSUME_NONNULL_BEGIN
// A query with an empty result set still counts.
FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"garages")
targetID:42
+ listenSequenceNumber:100
purpose:FSTQueryPurposeListen];
[self addQueryData:query3];
XCTAssertEqual([self.queryCache highestTargetID], 42);
@@ -319,12 +370,21 @@ NS_ASSUME_NONNULL_BEGIN
* Creates a new FSTQueryData object from the given parameters, synthesizing a resume token from
* the snapshot version.
*/
+- (FSTQueryData *)queryDataWithQuery:(FSTQuery *)query {
+ return [self queryDataWithQuery:query
+ targetID:++_previousTargetID
+ listenSequenceNumber:++_previousSequenceNumber
+ version:++_previousSnapshotVersion];
+}
+
- (FSTQueryData *)queryDataWithQuery:(FSTQuery *)query
targetID:(FSTTargetID)targetID
+ listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber
version:(FSTTestSnapshotVersion)version {
NSData *resumeToken = FSTTestResumeTokenFromSnapshotVersion(version);
return [[FSTQueryData alloc] initWithQuery:query
targetID:targetID
+ listenSequenceNumber:sequenceNumber
purpose:FSTQueryPurposeListen
snapshotVersion:FSTTestVersion(version)
resumeToken:resumeToken];
diff --git a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.m b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.m
index 61847b0..3357078 100644
--- a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.m
+++ b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.m
@@ -396,20 +396,25 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testEncodesListenRequestLabels {
FSTQuery *query = FSTTestQuery(@"collection/key");
- FSTQueryData *queryData =
- [[FSTQueryData alloc] initWithQuery:query targetID:2 purpose:FSTQueryPurposeListen];
+ FSTQueryData *queryData = [[FSTQueryData alloc] initWithQuery:query
+ targetID:2
+ listenSequenceNumber:3
+ purpose:FSTQueryPurposeListen];
NSDictionary<NSString *, NSString *> *result =
[self.serializer encodedListenRequestLabelsForQueryData:queryData];
XCTAssertNil(result);
- queryData =
- [[FSTQueryData alloc] initWithQuery:query targetID:2 purpose:FSTQueryPurposeLimboResolution];
+ queryData = [[FSTQueryData alloc] initWithQuery:query
+ targetID:2
+ listenSequenceNumber:3
+ purpose:FSTQueryPurposeLimboResolution];
result = [self.serializer encodedListenRequestLabelsForQueryData:queryData];
XCTAssertEqualObjects(result, @{@"goog-listen-tags" : @"limbo-document"});
queryData = [[FSTQueryData alloc] initWithQuery:query
targetID:2
+ listenSequenceNumber:3
purpose:FSTQueryPurposeExistenceFilterMismatch];
result = [self.serializer encodedListenRequestLabelsForQueryData:queryData];
XCTAssertEqualObjects(result, @{@"goog-listen-tags" : @"existence-filter-mismatch"});
@@ -627,6 +632,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTQuery *q = FSTTestQuery(@"docs");
FSTQueryData *model = [[FSTQueryData alloc] initWithQuery:q
targetID:1
+ listenSequenceNumber:0
purpose:FSTQueryPurposeListen
snapshotVersion:[FSTSnapshotVersion noVersion]
resumeToken:FSTTestData(1, 2, 3, -1)];
@@ -647,6 +653,7 @@ NS_ASSUME_NONNULL_BEGIN
- (FSTQueryData *)queryDataForQuery:(FSTQuery *)query {
return [[FSTQueryData alloc] initWithQuery:query
targetID:1
+ listenSequenceNumber:0
purpose:FSTQueryPurposeListen
snapshotVersion:[FSTSnapshotVersion noVersion]
resumeToken:[NSData data]];
diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.m b/Firestore/Example/Tests/SpecTests/FSTSpecTests.m
index 3abcb48..7fed64a 100644
--- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.m
+++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.m
@@ -503,6 +503,7 @@ static NSString *const kNoIOSTag = @"no-ios";
expectedActiveTargets[@(targetID)] =
[[FSTQueryData alloc] initWithQuery:query
targetID:targetID
+ listenSequenceNumber:0
purpose:FSTQueryPurposeListen
snapshotVersion:[FSTSnapshotVersion noVersion]
resumeToken:resumeToken];
diff --git a/Firestore/Source/Core/FSTListenSequence.h b/Firestore/Source/Core/FSTListenSequence.h
new file mode 100644
index 0000000..56d0e78
--- /dev/null
+++ b/Firestore/Source/Core/FSTListenSequence.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018 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 <Foundation/Foundation.h>
+
+#import "FSTTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * FSTListenSequence is a monotonic sequence. It is initialized with a minimum value to
+ * exceed. All subsequent calls to next will return increasing values.
+ */
+@interface FSTListenSequence : NSObject
+
+- (instancetype)initStartingAfter:(FSTListenSequenceNumber)after NS_DESIGNATED_INITIALIZER;
+
+- (id)init NS_UNAVAILABLE;
+
+- (FSTListenSequenceNumber)next;
+
+@end
+
+NS_ASSUME_NONNULL_END \ No newline at end of file
diff --git a/Firestore/Source/Core/FSTListenSequence.m b/Firestore/Source/Core/FSTListenSequence.m
new file mode 100644
index 0000000..27ade7c
--- /dev/null
+++ b/Firestore/Source/Core/FSTListenSequence.m
@@ -0,0 +1,34 @@
+#import "FSTListenSequence.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - FSTListenSequence
+
+@interface FSTListenSequence () {
+ FSTListenSequenceNumber _previousSequenceNumber;
+}
+
+@end
+
+@implementation FSTListenSequence
+
+#pragma mark - Constructors
+
+- (instancetype)initStartingAfter:(FSTListenSequenceNumber)after {
+ self = [super init];
+ if (self) {
+ _previousSequenceNumber = after;
+ }
+ return self;
+}
+
+#pragma mark - Public methods
+
+- (FSTListenSequenceNumber)next {
+ _previousSequenceNumber++;
+ return _previousSequenceNumber;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END \ No newline at end of file
diff --git a/Firestore/Source/Core/FSTSyncEngine.m b/Firestore/Source/Core/FSTSyncEngine.m
index 27ab73e..f90c5dd 100644
--- a/Firestore/Source/Core/FSTSyncEngine.m
+++ b/Firestore/Source/Core/FSTSyncEngine.m
@@ -43,6 +43,10 @@
NS_ASSUME_NONNULL_BEGIN
+// Limbo documents don't use persistence, and are eagerly GC'd. So, listens for them don't need
+// real sequence numbers.
+static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1;
+
#pragma mark - FSTQueryView
/**
@@ -490,6 +494,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTQuery *query = [FSTQuery queryWithPath:key.path];
FSTQueryData *queryData = [[FSTQueryData alloc] initWithQuery:query
targetID:limboTargetID
+ listenSequenceNumber:kIrrelevantSequenceNumber
purpose:FSTQueryPurposeLimboResolution];
self.limboKeysByTarget[@(limboTargetID)] = key;
[self.remoteStore listenToTargetWithQueryData:queryData];
diff --git a/Firestore/Source/Core/FSTTypes.h b/Firestore/Source/Core/FSTTypes.h
index b47bd0b..877ec94 100644
--- a/Firestore/Source/Core/FSTTypes.h
+++ b/Firestore/Source/Core/FSTTypes.h
@@ -26,6 +26,8 @@ typedef int32_t FSTBatchID;
typedef int32_t FSTTargetID;
+typedef int64_t FSTListenSequenceNumber;
+
typedef NSNumber FSTBoxedTargetID;
/**
diff --git a/Firestore/Source/Local/FSTLevelDBQueryCache.mm b/Firestore/Source/Local/FSTLevelDBQueryCache.mm
index 13d15ee..8388b96 100644
--- a/Firestore/Source/Local/FSTLevelDBQueryCache.mm
+++ b/Firestore/Source/Local/FSTLevelDBQueryCache.mm
@@ -100,6 +100,10 @@ static ReadOptions GetStandardReadOptions() {
return self.metadata.highestTargetId;
}
+- (FSTListenSequenceNumber)highestListenSequenceNumber {
+ return self.metadata.highestListenSequenceNumber;
+}
+
- (FSTSnapshotVersion *)lastRemoteSnapshotVersion {
return _lastRemoteSnapshotVersion;
}
@@ -116,7 +120,6 @@ static ReadOptions GetStandardReadOptions() {
}
- (void)addQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group {
- // TODO(mcg): actually populate listen sequence number
FSTTargetID targetID = queryData.targetID;
std::string key = [FSTLevelDBTargetKey keyWithTargetID:targetID];
[group setMessage:[self.serializer encodedQueryData:queryData] forKey:key];
@@ -127,9 +130,19 @@ static ReadOptions GetStandardReadOptions() {
std::string emptyBuffer;
[group setData:emptyBuffer forKey:indexKey];
+ BOOL saveMetadata = NO;
FSTPBTargetGlobal *metadata = self.metadata;
if (targetID > metadata.highestTargetId) {
metadata.highestTargetId = targetID;
+ saveMetadata = YES;
+ }
+
+ if (queryData.sequenceNumber > metadata.highestListenSequenceNumber) {
+ metadata.highestListenSequenceNumber = queryData.sequenceNumber;
+ saveMetadata = YES;
+ }
+
+ if (saveMetadata) {
[group setMessage:metadata forKey:[FSTLevelDBTargetGlobalKey key]];
}
}
diff --git a/Firestore/Source/Local/FSTLocalSerializer.m b/Firestore/Source/Local/FSTLocalSerializer.m
index c71e9dd..82aec4d 100644
--- a/Firestore/Source/Local/FSTLocalSerializer.m
+++ b/Firestore/Source/Local/FSTLocalSerializer.m
@@ -156,6 +156,7 @@
FSTPBTarget *proto = [FSTPBTarget message];
proto.targetId = queryData.targetID;
+ proto.lastListenSequenceNumber = queryData.sequenceNumber;
proto.snapshotVersion = [remoteSerializer encodedVersion:queryData.snapshotVersion];
proto.resumeToken = queryData.resumeToken;
@@ -173,6 +174,7 @@
FSTSerializerBeta *remoteSerializer = self.remoteSerializer;
FSTTargetID targetID = target.targetId;
+ FSTListenSequenceNumber sequenceNumber = target.lastListenSequenceNumber;
FSTSnapshotVersion *version = [remoteSerializer decodedVersion:target.snapshotVersion];
NSData *resumeToken = target.resumeToken;
@@ -192,6 +194,7 @@
return [[FSTQueryData alloc] initWithQuery:query
targetID:targetID
+ listenSequenceNumber:sequenceNumber
purpose:FSTQueryPurposeListen
snapshotVersion:version
resumeToken:resumeToken];
diff --git a/Firestore/Source/Local/FSTLocalStore.m b/Firestore/Source/Local/FSTLocalStore.m
index cde7104..3a5b0b4 100644
--- a/Firestore/Source/Local/FSTLocalStore.m
+++ b/Firestore/Source/Local/FSTLocalStore.m
@@ -17,6 +17,7 @@
#import "Firestore/Source/Local/FSTLocalStore.h"
#import "Firestore/Source/Auth/FSTUser.h"
+#import "Firestore/Source/Core/FSTListenSequence.h"
#import "Firestore/Source/Core/FSTQuery.h"
#import "Firestore/Source/Core/FSTSnapshotVersion.h"
#import "Firestore/Source/Core/FSTTargetIDGenerator.h"
@@ -76,6 +77,8 @@ NS_ASSUME_NONNULL_BEGIN
/** Used to generate targetIDs for queries tracked locally. */
@property(nonatomic, strong) FSTTargetIDGenerator *targetIDGenerator;
+@property(nonatomic, strong) FSTListenSequence *listenSequence;
+
/**
* A heldBatchResult is a mutation batch result (from a write acknowledgement) that arrived before
* the watch stream got notified of a snapshot that includes the write.  So we "hold" it until
@@ -148,6 +151,8 @@ NS_ASSUME_NONNULL_BEGIN
FSTTargetID targetID = [self.queryCache highestTargetID];
self.targetIDGenerator = [FSTTargetIDGenerator generatorForLocalStoreStartingAfterID:targetID];
+ FSTListenSequenceNumber sequenceNumber = [self.queryCache highestListenSequenceNumber];
+ self.listenSequence = [[FSTListenSequence alloc] initStartingAfter:sequenceNumber];
}
- (void)shutdown {
@@ -380,6 +385,7 @@ NS_ASSUME_NONNULL_BEGIN
- (FSTQueryData *)allocateQuery:(FSTQuery *)query {
FSTQueryData *cached = [self.queryCache queryDataForQuery:query];
FSTTargetID targetID;
+ FSTListenSequenceNumber sequenceNumber = [self.listenSequence next];
if (cached) {
// This query has been listened to previously, so reuse the previous targetID.
// TODO(mcg): freshen last accessed date?
@@ -388,8 +394,10 @@ NS_ASSUME_NONNULL_BEGIN
FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Allocate query"];
targetID = [self.targetIDGenerator nextID];
- cached =
- [[FSTQueryData alloc] initWithQuery:query targetID:targetID purpose:FSTQueryPurposeListen];
+ cached = [[FSTQueryData alloc] initWithQuery:query
+ targetID:targetID
+ listenSequenceNumber:sequenceNumber
+ purpose:FSTQueryPurposeListen];
[self.queryCache addQueryData:cached group:group];
[self.persistence commitGroup:group];
diff --git a/Firestore/Source/Local/FSTMemoryQueryCache.m b/Firestore/Source/Local/FSTMemoryQueryCache.m
index 8d37bcb..bcab174 100644
--- a/Firestore/Source/Local/FSTMemoryQueryCache.m
+++ b/Firestore/Source/Local/FSTMemoryQueryCache.m
@@ -34,6 +34,8 @@ NS_ASSUME_NONNULL_BEGIN
/** The highest numbered target ID encountered. */
@property(nonatomic, assign) FSTTargetID highestTargetID;
+@property(nonatomic, assign) FSTListenSequenceNumber highestListenSequenceNumber;
+
@end
@implementation FSTMemoryQueryCache {
@@ -65,6 +67,10 @@ NS_ASSUME_NONNULL_BEGIN
return _highestTargetID;
}
+- (FSTListenSequenceNumber)highestListenSequenceNumber {
+ return _highestListenSequenceNumber;
+}
+
- (FSTSnapshotVersion *)lastRemoteSnapshotVersion {
return _lastRemoteSnapshotVersion;
}
@@ -79,6 +85,9 @@ NS_ASSUME_NONNULL_BEGIN
if (queryData.targetID > self.highestTargetID) {
self.highestTargetID = queryData.targetID;
}
+ if (queryData.sequenceNumber > self.highestListenSequenceNumber) {
+ self.highestListenSequenceNumber = queryData.sequenceNumber;
+ }
}
- (void)removeQueryData:(FSTQueryData *)queryData group:(__unused FSTWriteGroup *)group {
diff --git a/Firestore/Source/Local/FSTQueryCache.h b/Firestore/Source/Local/FSTQueryCache.h
index e0cf4c8..88c9df9 100644
--- a/Firestore/Source/Local/FSTQueryCache.h
+++ b/Firestore/Source/Local/FSTQueryCache.h
@@ -53,6 +53,11 @@ NS_ASSUME_NONNULL_BEGIN
- (FSTTargetID)highestTargetID;
/**
+ * Returns the highest listen sequence number of any query seen by the cache.
+ */
+- (FSTListenSequenceNumber)highestListenSequenceNumber;
+
+/**
* A global snapshot version representing the last consistent snapshot we received from the
* backend. This is monotonically increasing and any snapshots received from the backend prior to
* this version (e.g. for targets resumed with a resume_token) should be suppressed (buffered)
diff --git a/Firestore/Source/Local/FSTQueryData.h b/Firestore/Source/Local/FSTQueryData.h
index 048bfad..5db2de6 100644
--- a/Firestore/Source/Local/FSTQueryData.h
+++ b/Firestore/Source/Local/FSTQueryData.h
@@ -40,6 +40,7 @@ typedef NS_ENUM(NSInteger, FSTQueryPurpose) {
- (instancetype)initWithQuery:(FSTQuery *)query
targetID:(FSTTargetID)targetID
+ listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber
purpose:(FSTQueryPurpose)purpose
snapshotVersion:(FSTSnapshotVersion *)snapshotVersion
resumeToken:(NSData *)resumeToken NS_DESIGNATED_INITIALIZER;
@@ -47,6 +48,7 @@ typedef NS_ENUM(NSInteger, FSTQueryPurpose) {
/** Convenience initializer for use when creating an FSTQueryData for the first time. */
- (instancetype)initWithQuery:(FSTQuery *)query
targetID:(FSTTargetID)targetID
+ listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber
purpose:(FSTQueryPurpose)purpose;
- (instancetype)init NS_UNAVAILABLE;
@@ -64,6 +66,8 @@ typedef NS_ENUM(NSInteger, FSTQueryPurpose) {
*/
@property(nonatomic, assign, readonly) FSTTargetID targetID;
+@property(nonatomic, assign, readonly) FSTListenSequenceNumber sequenceNumber;
+
/** The purpose of the query. */
@property(nonatomic, assign, readonly) FSTQueryPurpose purpose;
diff --git a/Firestore/Source/Local/FSTQueryData.m b/Firestore/Source/Local/FSTQueryData.m
index 080f136..6bb716a 100644
--- a/Firestore/Source/Local/FSTQueryData.m
+++ b/Firestore/Source/Local/FSTQueryData.m
@@ -25,6 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithQuery:(FSTQuery *)query
targetID:(FSTTargetID)targetID
+ listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber
purpose:(FSTQueryPurpose)purpose
snapshotVersion:(FSTSnapshotVersion *)snapshotVersion
resumeToken:(NSData *)resumeToken {
@@ -32,6 +33,7 @@ NS_ASSUME_NONNULL_BEGIN
if (self) {
_query = query;
_targetID = targetID;
+ _sequenceNumber = sequenceNumber;
_purpose = purpose;
_snapshotVersion = snapshotVersion;
_resumeToken = [resumeToken copy];
@@ -41,9 +43,11 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithQuery:(FSTQuery *)query
targetID:(FSTTargetID)targetID
+ listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber
purpose:(FSTQueryPurpose)purpose {
return [self initWithQuery:query
targetID:targetID
+ listenSequenceNumber:sequenceNumber
purpose:purpose
snapshotVersion:[FSTSnapshotVersion noVersion]
resumeToken:[NSData data]];
@@ -83,6 +87,7 @@ NS_ASSUME_NONNULL_BEGIN
resumeToken:(NSData *)resumeToken {
return [[FSTQueryData alloc] initWithQuery:self.query
targetID:self.targetID
+ listenSequenceNumber:self.sequenceNumber
purpose:self.purpose
snapshotVersion:snapshotVersion
resumeToken:resumeToken];
diff --git a/Firestore/Source/Remote/FSTRemoteStore.m b/Firestore/Source/Remote/FSTRemoteStore.m
index a0c5059..1201049 100644
--- a/Firestore/Source/Remote/FSTRemoteStore.m
+++ b/Firestore/Source/Remote/FSTRemoteStore.m
@@ -468,8 +468,10 @@ static const int kOnlineAttemptsBeforeFailure = 2;
[remoteEvent handleExistenceFilterMismatchForTargetID:target];
// Clear the resume token for the query, since we're in a known mismatch state.
- queryData =
- [[FSTQueryData alloc] initWithQuery:query targetID:targetID purpose:queryData.purpose];
+ queryData = [[FSTQueryData alloc] initWithQuery:query
+ targetID:targetID
+ listenSequenceNumber:queryData.sequenceNumber
+ purpose:queryData.purpose];
self.listenTargets[target] = queryData;
// Cause a hard reset by unwatching and rewatching immediately, but deliberately don't
@@ -483,6 +485,7 @@ static const int kOnlineAttemptsBeforeFailure = 2;
FSTQueryData *requestQueryData =
[[FSTQueryData alloc] initWithQuery:query
targetID:targetID
+ listenSequenceNumber:queryData.sequenceNumber
purpose:FSTQueryPurposeExistenceFilterMismatch];
[self sendWatchRequestWithQueryData:requestQueryData];
}
diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt
index c49b6db..da08dfc 100644
--- a/Firestore/core/CMakeLists.txt
+++ b/Firestore/core/CMakeLists.txt
@@ -12,5 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+add_subdirectory(src/firebase/firestore/model)
+add_subdirectory(src/firebase/firestore/remote)
add_subdirectory(src/firebase/firestore/util)
+
+add_subdirectory(test/firebase/firestore/model)
+add_subdirectory(test/firebase/firestore/remote)
add_subdirectory(test/firebase/firestore/util)
diff --git a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
new file mode 100644
index 0000000..ae80de3
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Copyright 2018 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.
+
+cc_library(
+ firebase_firestore_model
+ SOURCES
+ field_value.cc
+ DEPENDS
+ firebase_firestore_util
+)
diff --git a/Firestore/core/src/firebase/firestore/model/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc
new file mode 100644
index 0000000..45886bf
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/field_value.cc
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/field_value.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+using Type = FieldValue::Type;
+
+namespace {
+/**
+ * This deviates from the other platforms that define TypeOrder. Since
+ * we already define Type for union types, we use it together with this
+ * function to achive the equivalent order of types i.e.
+ * i) if two types are comparable, then they are of equal order;
+ * ii) otherwise, their order is the same as the order of their Type.
+ */
+bool Comparable(Type lhs, Type rhs) {
+ switch (lhs) {
+ case Type::Long:
+ case Type::Double:
+ return rhs == Type::Long || rhs == Type::Double;
+ case Type::Timestamp:
+ case Type::ServerTimestamp:
+ return rhs == Type::Timestamp || rhs == Type::ServerTimestamp;
+ default:
+ return lhs == rhs;
+ }
+}
+
+} // namespace
+
+FieldValue::FieldValue(const FieldValue& value) {
+ *this = value;
+}
+
+FieldValue::FieldValue(FieldValue&& value) {
+ *this = std::move(value);
+}
+
+FieldValue::~FieldValue() {
+ SwitchTo(Type::Null);
+}
+
+FieldValue& FieldValue::operator=(const FieldValue& value) {
+ SwitchTo(value.tag_);
+ switch (tag_) {
+ case Type::Null:
+ break;
+ case Type::Boolean:
+ boolean_value_ = value.boolean_value_;
+ break;
+ case Type::Array: {
+ // copy-and-swap
+ std::vector<const FieldValue> tmp = value.array_value_;
+ std::swap(array_value_, tmp);
+ break;
+ }
+ default:
+ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(
+ false, lhs.type(), "Unsupported type %d", value.type());
+ }
+ return *this;
+}
+
+FieldValue& FieldValue::operator=(FieldValue&& value) {
+ switch (value.tag_) {
+ case Type::Array:
+ SwitchTo(Type::Array);
+ std::swap(array_value_, value.array_value_);
+ return *this;
+ default:
+ // We just copy over POD union types.
+ return *this = value;
+ }
+}
+
+const FieldValue& FieldValue::NullValue() {
+ static const FieldValue kNullInstance;
+ return kNullInstance;
+}
+
+const FieldValue& FieldValue::TrueValue() {
+ static const FieldValue kTrueInstance(true);
+ return kTrueInstance;
+}
+
+const FieldValue& FieldValue::FalseValue() {
+ static const FieldValue kFalseInstance(false);
+ return kFalseInstance;
+}
+
+const FieldValue& FieldValue::BooleanValue(bool value) {
+ return value ? TrueValue() : FalseValue();
+}
+
+FieldValue FieldValue::ArrayValue(const std::vector<const FieldValue>& value) {
+ std::vector<const FieldValue> copy(value);
+ return ArrayValue(std::move(copy));
+}
+
+FieldValue FieldValue::ArrayValue(std::vector<const FieldValue>&& value) {
+ FieldValue result;
+ result.SwitchTo(Type::Array);
+ std::swap(result.array_value_, value);
+ return result;
+}
+
+bool operator<(const FieldValue& lhs, const FieldValue& rhs) {
+ if (!Comparable(lhs.type(), rhs.type())) {
+ return lhs.type() < rhs.type();
+ }
+
+ switch (lhs.type()) {
+ case Type::Null:
+ return false;
+ case Type::Boolean:
+ // lhs < rhs iff lhs == false and rhs == true.
+ return !lhs.boolean_value_ && rhs.boolean_value_;
+ case Type::Array:
+ return lhs.array_value_ < rhs.array_value_;
+ default:
+ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(
+ false, lhs.type(), "Unsupported type %d", lhs.type());
+ // return false if assertion does not abort the program. We will say
+ // each unsupported type takes only one value thus everything is equal.
+ return false;
+ }
+}
+
+void FieldValue::SwitchTo(const Type type) {
+ if (tag_ == type) {
+ return;
+ }
+ // Not same type. Destruct old type first and then initialize new type.
+ // Must call destructor explicitly for any non-POD type.
+ switch (tag_) {
+ case Type::Array:
+ array_value_.~vector();
+ break;
+ default: {} // The other types where there is nothing to worry about.
+ }
+ tag_ = type;
+ // Must call constructor explicitly for any non-POD type to initialize.
+ switch (tag_) {
+ case Type::Array:
+ new (&array_value_) std::vector<const FieldValue>();
+ break;
+ default: {} // The other types where there is nothing to worry about.
+ }
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h
new file mode 100644
index 0000000..781e34f
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/field_value.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2018 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_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_
+
+#include <memory>
+#include <vector>
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+/**
+ * tagged-union class representing an immutable data value as stored in
+ * Firestore. FieldValue represents all the different kinds of values
+ * that can be stored in fields in a document.
+ */
+class FieldValue {
+ public:
+ /**
+ * All the different kinds of values that can be stored in fields in
+ * a document. The types of the same comparison order should be defined
+ * together as a group. The order of each group is defined by the Firestore
+ * backend and is available at:
+ * https://firebase.google.com/docs/firestore/manage-data/data-types
+ */
+ enum class Type {
+ Null, // Null
+ Boolean, // Boolean
+ Long, // Number type starts here
+ Double,
+ Timestamp, // Timestamp type starts here
+ ServerTimestamp,
+ String, // String
+ Blob, // Blob
+ Reference, // Reference
+ GeoPoint, // GeoPoint
+ Array, // Array
+ Object, // Object
+ // New enum should not always been added at the tail. Add it to the correct
+ // position instead, see the doc comment above.
+ };
+
+ FieldValue() : tag_(Type::Null) {
+ }
+
+ // Do not inline these ctor/dtor below, which contain call to non-trivial
+ // operator=.
+ FieldValue(const FieldValue& value);
+ FieldValue(FieldValue&& value);
+
+ ~FieldValue();
+
+ FieldValue& operator=(const FieldValue& value);
+ FieldValue& operator=(FieldValue&& value);
+
+ /** Returns the true type for this value. */
+ Type type() const {
+ return tag_;
+ }
+
+ /** factory methods. */
+ static const FieldValue& NullValue();
+ static const FieldValue& TrueValue();
+ static const FieldValue& FalseValue();
+ static const FieldValue& BooleanValue(bool value);
+ static FieldValue ArrayValue(const std::vector<const FieldValue>& value);
+ static FieldValue ArrayValue(std::vector<const FieldValue>&& value);
+
+ friend bool operator<(const FieldValue& lhs, const FieldValue& rhs);
+
+ private:
+ explicit FieldValue(bool value) : tag_(Type::Boolean), boolean_value_(value) {
+ }
+
+ /**
+ * Switch to the specified type, if different from the current type.
+ */
+ void SwitchTo(const Type type);
+
+ Type tag_;
+ union {
+ // There is no null type as tag_ alone is enough for Null FieldValue.
+ bool boolean_value_;
+ std::vector<const FieldValue> array_value_;
+ };
+};
+
+/** Compares against another FieldValue. */
+bool operator<(const FieldValue& lhs, const FieldValue& rhs);
+
+inline bool operator>(const FieldValue& lhs, const FieldValue& rhs) {
+ return rhs < lhs;
+}
+
+inline bool operator>=(const FieldValue& lhs, const FieldValue& rhs) {
+ return !(lhs < rhs);
+}
+
+inline bool operator<=(const FieldValue& lhs, const FieldValue& rhs) {
+ return !(lhs > rhs);
+}
+
+inline bool operator!=(const FieldValue& lhs, const FieldValue& rhs) {
+ return lhs < rhs || lhs > rhs;
+}
+
+inline bool operator==(const FieldValue& lhs, const FieldValue& rhs) {
+ return !(lhs != rhs);
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_
diff --git a/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt b/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt
new file mode 100644
index 0000000..43320ce
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright 2018 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.
+
+cc_library(
+ firebase_firestore_remote
+ SOURCES
+ datastore.h
+ datastore.cc
+ DEPENDS
+ grpc::grpc
+)
diff --git a/Firestore/core/src/firebase/firestore/remote/datastore.cc b/Firestore/core/src/firebase/firestore/remote/datastore.cc
new file mode 100644
index 0000000..f8a8988
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/remote/datastore.cc
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/remote/datastore.h"
+
+namespace firebase {
+namespace firestore {
+namespace remote {
+
+Datastore::Datastore() {
+}
+
+Datastore::~Datastore() {
+}
+
+} // namespace remote
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/remote/datastore.h b/Firestore/core/src/firebase/firestore/remote/datastore.h
new file mode 100644
index 0000000..807b75f
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/remote/datastore.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018 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_FIREBASE_FIRESTORE_REMOTE_DATASTORE_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_DATASTORE_H_
+
+namespace firebase {
+namespace firestore {
+namespace remote {
+
+class Datastore {
+ public:
+ Datastore();
+ ~Datastore();
+
+ Datastore(const Datastore& other) = delete;
+ Datastore(Datastore&& other) = delete;
+
+ Datastore& operator=(const Datastore& other) = delete;
+ Datastore& operator=(Datastore&& other) = delete;
+};
+
+} // namespace remote
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_DATASTORE_H_
diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt
index 3028a95..7283942 100644
--- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt
+++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt
@@ -16,69 +16,61 @@
# libraries in here are an implementation detail of making this a
# mutli-platform build.
-add_library(
+cc_library(
firebase_firestore_util_base
- secure_random_arc4random.cc
- string_printf.cc
-)
-target_link_libraries(
- firebase_firestore_util_base
- PUBLIC
- absl_base
+ SOURCES
+ secure_random.h
+ secure_random_arc4random.cc
+ string_printf.cc
+ string_printf.h
+ DEPENDS
+ absl_base
)
-# stdio-dependent bits can be built and tested everywhere
-add_library(
- firebase_firestore_util_stdio
- assert_stdio.cc
- log_stdio.cc
-)
-target_link_libraries(
+## assert and log
+
+cc_library(
firebase_firestore_util_stdio
- PUBLIC
- firebase_firestore_util_base
+ SOURCES
+ assert_stdio.cc
+ log_stdio.cc
+ DEPENDS
+ firebase_firestore_util_base
+ absl_base
+ EXCLUDE_FROM_ALL
)
-# apple-dependent bits can only built and tested on apple plaforms
-if(APPLE)
- add_library(
- firebase_firestore_util_apple
+cc_library(
+ firebase_firestore_util_apple
+ SOURCES
assert_apple.mm
log_apple.mm
- )
- target_compile_options(
- firebase_firestore_util_apple
- PRIVATE
- ${OBJC_FLAGS}
- )
- target_link_libraries(
- firebase_firestore_util_apple
- PUBLIC
+ string_apple.h
+ DEPENDS
FirebaseCore
- )
-endif(APPLE)
-
-add_library(
- firebase_firestore_util
- autoid.cc
+ EXCLUDE_FROM_ALL
)
# Export a dependency on the correct logging library for this platform. All
# buildable libraries are built and tested but only the best fit is exported.
if(APPLE)
- target_link_libraries(
- firebase_firestore_util
- PUBLIC
- firebase_firestore_util_apple
- firebase_firestore_util_base
- )
+ list(APPEND UTIL_DEPENDS firebase_firestore_util_apple)
+else()
+ list(APPEND UTIL_DEPENDS firebase_firestore_util_stdio)
+endif()
-else(NOT APPLE)
- target_link_libraries(
- firebase_firestore_util
- PUBLIC
- firebase_firestore_util_stdio
- firebase_firestore_util_base
- )
-endif(APPLE)
+## main library
+
+cc_library(
+ firebase_firestore_util
+ SOURCES
+ autoid.cc
+ autoid.h
+ firebase_assert.h
+ log.h
+ DEPENDS
+ ${UTIL_DEPENDS}
+ firebase_firestore_util_base
+ absl_base
+)
diff --git a/Firestore/core/src/firebase/firestore/util/assert_stdio.cc b/Firestore/core/src/firebase/firestore/util/assert_stdio.cc
index 5476e65..f0d5251 100644
--- a/Firestore/core/src/firebase/firestore/util/assert_stdio.cc
+++ b/Firestore/core/src/firebase/firestore/util/assert_stdio.cc
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
-
#include <stdarg.h>
#include <exception>
#include <string>
-#include <absl/base/config.h>
-
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
#include "Firestore/core/src/firebase/firestore/util/string_printf.h"
+#include "absl/base/config.h"
namespace firebase {
namespace firestore {
diff --git a/Firestore/core/src/firebase/firestore/util/firebase_assert.h b/Firestore/core/src/firebase/firestore/util/firebase_assert.h
index da01864..cff550a 100644
--- a/Firestore/core/src/firebase/firestore/util/firebase_assert.h
+++ b/Firestore/core/src/firebase/firestore/util/firebase_assert.h
@@ -52,7 +52,7 @@
#else
#define FIREBASE_DEV_ASSERT_WITH_EXPRESSION(condition, expression) \
FIREBASE_ASSERT_WITH_EXPRESSION(condition, expression)
-#endif // !defined(NDEBUG)
+#endif // defined(NDEBUG)
// Custom assert() implementation that is not compiled out in release builds.
#define FIREBASE_ASSERT(expression) \
@@ -85,7 +85,7 @@
#define FIREBASE_DEV_ASSERT_MESSAGE_WITH_EXPRESSION(condition, expression, \
...) \
FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(condition, expression, __VA_ARGS__)
-#endif // !defined(NDEBUG)
+#endif // defined(NDEBUG)
namespace firebase {
namespace firestore {
diff --git a/Firestore/core/src/firebase/firestore/util/string_printf.cc b/Firestore/core/src/firebase/firestore/util/string_printf.cc
index 60cc564..9c4e31c 100644
--- a/Firestore/core/src/firebase/firestore/util/string_printf.cc
+++ b/Firestore/core/src/firebase/firestore/util/string_printf.cc
@@ -38,7 +38,7 @@ void StringAppendV(std::string* dst, const char* format, va_list ap) {
if (result < kSpaceLength) {
if (result >= 0) {
// Normal case -- everything fit.
- dst->append(space, result);
+ dst->append(space, static_cast<size_t>(result));
return;
}
@@ -49,28 +49,29 @@ void StringAppendV(std::string* dst, const char* format, va_list ap) {
result = vsnprintf(nullptr, 0, format, backup_ap);
va_end(backup_ap);
#endif
+ }
- if (result < 0) {
- // Just an error.
- return;
- }
+ if (result < 0) {
+ // Just an error.
+ return;
}
+ size_t result_size = static_cast<size_t>(result);
// Increase the buffer size to the size requested by vsnprintf,
// plus one for the closing \0.
size_t initial_size = dst->size();
- size_t target_size = initial_size + result;
+ size_t target_size = initial_size + result_size;
dst->resize(target_size + 1);
char* buf = &(*dst)[initial_size];
- int buf_remain = result + 1;
+ size_t buf_remain = result_size + 1;
// Restore the va_list before we use it again
va_copy(backup_ap, ap);
result = vsnprintf(buf, buf_remain, format, backup_ap);
va_end(backup_ap);
- if (result >= 0 && result < buf_remain) {
+ if (result >= 0 && static_cast<size_t>(result) < buf_remain) {
// It fit and vsnprintf copied in directly. Resize down one to
// remove the trailing \0.
dst->resize(target_size);
diff --git a/Firestore/core/src/firebase/firestore/util/string_printf.h b/Firestore/core/src/firebase/firestore/util/string_printf.h
index d15296e..10dfae9 100644
--- a/Firestore/core/src/firebase/firestore/util/string_printf.h
+++ b/Firestore/core/src/firebase/firestore/util/string_printf.h
@@ -21,7 +21,7 @@
#include <string>
-#include <absl/base/attributes.h>
+#include "absl/base/attributes.h"
namespace firebase {
namespace firestore {
@@ -44,4 +44,4 @@ void StringAppendV(std::string* dst, const char* format, va_list ap);
} // namespace firestore
} // namespace firebase
-#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_FORMAT_H_
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_PRINTF_H_
diff --git a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
new file mode 100644
index 0000000..31fe040
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Copyright 2018 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.
+
+cc_test(
+ firebase_firestore_model_test
+ SOURCES
+ field_value_test.cc
+ DEPENDS
+ firebase_firestore_model
+)
diff --git a/Firestore/core/test/firebase/firestore/model/field_value_test.cc b/Firestore/core/test/firebase/firestore/model/field_value_test.cc
new file mode 100644
index 0000000..1194e63
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/model/field_value_test.cc
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/field_value.h"
+
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+using Type = FieldValue::Type;
+
+TEST(FieldValue, NullType) {
+ const FieldValue value = FieldValue::NullValue();
+ EXPECT_EQ(Type::Null, value.type());
+ EXPECT_FALSE(value < value);
+}
+
+TEST(FieldValue, BooleanType) {
+ const FieldValue true_value = FieldValue::BooleanValue(true);
+ const FieldValue false_value = FieldValue::BooleanValue(false);
+ EXPECT_EQ(Type::Boolean, true_value.type());
+ EXPECT_FALSE(true_value < true_value);
+ EXPECT_FALSE(true_value < false_value);
+ EXPECT_FALSE(false_value < false_value);
+ EXPECT_TRUE(false_value < true_value);
+}
+
+TEST(FieldValue, ArrayType) {
+ const FieldValue empty =
+ FieldValue::ArrayValue(std::vector<const FieldValue>{});
+ std::vector<const FieldValue> array{FieldValue::NullValue(),
+ FieldValue::BooleanValue(true),
+ FieldValue::BooleanValue(false)};
+ // copy the array
+ const FieldValue small = FieldValue::ArrayValue(array);
+ std::vector<const FieldValue> another_array{FieldValue::BooleanValue(true),
+ FieldValue::BooleanValue(false)};
+ // move the array
+ const FieldValue large = FieldValue::ArrayValue(std::move(another_array));
+ EXPECT_EQ(Type::Array, empty.type());
+ EXPECT_EQ(Type::Array, small.type());
+ EXPECT_EQ(Type::Array, large.type());
+ EXPECT_TRUE(empty < small);
+ EXPECT_FALSE(small < empty);
+ EXPECT_FALSE(small < small);
+ EXPECT_TRUE(small < large);
+ EXPECT_FALSE(large < small);
+}
+
+TEST(FieldValue, Copy) {
+ FieldValue clone = FieldValue::TrueValue();
+ const FieldValue null_value = FieldValue::NullValue();
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+ EXPECT_EQ(FieldValue::NullValue(), null_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue true_value = FieldValue::TrueValue();
+ clone = true_value;
+ EXPECT_EQ(FieldValue::TrueValue(), clone);
+ EXPECT_EQ(FieldValue::TrueValue(), true_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::TrueValue(), clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue array_value =
+ FieldValue::ArrayValue(std::vector<const FieldValue>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()});
+ clone = array_value;
+ EXPECT_EQ(FieldValue::ArrayValue(std::vector<const FieldValue>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()}),
+ clone);
+ EXPECT_EQ(FieldValue::ArrayValue(std::vector<const FieldValue>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()}),
+ array_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::ArrayValue(std::vector<const FieldValue>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()}),
+ clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+}
+
+TEST(FieldValue, Move) {
+ FieldValue clone = FieldValue::TrueValue();
+
+ FieldValue null_value = FieldValue::NullValue();
+ clone = std::move(null_value);
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ FieldValue true_value = FieldValue::TrueValue();
+ clone = std::move(true_value);
+ EXPECT_EQ(FieldValue::TrueValue(), clone);
+ clone = FieldValue::NullValue();
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ FieldValue array_value = FieldValue::ArrayValue(std::vector<const FieldValue>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()});
+ clone = std::move(array_value);
+ EXPECT_EQ(FieldValue::ArrayValue(std::vector<const FieldValue>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()}),
+ clone);
+ clone = FieldValue::NullValue();
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+}
+
+TEST(FieldValue, CompareMixedType) {
+ const FieldValue null_value = FieldValue::NullValue();
+ const FieldValue true_value = FieldValue::TrueValue();
+ const FieldValue array_value =
+ FieldValue::ArrayValue(std::vector<const FieldValue>());
+ EXPECT_TRUE(null_value < true_value);
+ EXPECT_TRUE(true_value < array_value);
+}
+
+TEST(FieldValue, CompareWithOperator) {
+ const FieldValue small = FieldValue::NullValue();
+ const FieldValue large = FieldValue::TrueValue();
+
+ EXPECT_TRUE(small < large);
+ EXPECT_FALSE(small < small);
+ EXPECT_FALSE(large < small);
+
+ EXPECT_TRUE(large > small);
+ EXPECT_FALSE(small > small);
+ EXPECT_FALSE(small > large);
+
+ EXPECT_TRUE(large >= small);
+ EXPECT_TRUE(small >= small);
+ EXPECT_FALSE(small >= large);
+
+ EXPECT_TRUE(small <= large);
+ EXPECT_TRUE(small <= small);
+ EXPECT_FALSE(large <= small);
+
+ EXPECT_TRUE(small != large);
+ EXPECT_FALSE(small != small);
+
+ EXPECT_TRUE(small == small);
+ EXPECT_FALSE(small == large);
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/remote/CMakeLists.txt b/Firestore/core/test/firebase/firestore/remote/CMakeLists.txt
new file mode 100644
index 0000000..7d99e6f
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/remote/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Copyright 2018 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.
+
+cc_test(
+ firebase_firestore_remote_test
+ SOURCES
+ datastore_test.cc
+ DEPENDS
+ firebase_firestore_remote
+)
diff --git a/Firestore/core/test/firebase/firestore/remote/datastore_test.cc b/Firestore/core/test/firebase/firestore/remote/datastore_test.cc
new file mode 100644
index 0000000..46a19cc
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/remote/datastore_test.cc
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/remote/datastore.h"
+
+#include <grpc/grpc.h>
+#include <gtest/gtest.h>
+
+TEST(Datastore, CanLinkToGrpc) {
+ // This test doesn't actually do anything interesting as far as actually
+ // using gRPC is concerned but that it can run at all is proof that all the
+ // libraries required for gRPC to work are actually linked correctly into the
+ // test.
+ grpc_init();
+}
diff --git a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt
index e51bb51..7f0539c 100644
--- a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt
+++ b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt
@@ -14,33 +14,30 @@
cc_test(
firebase_firestore_util_test
- autoid_test.cc
- secure_random_test.cc
- string_printf_test.cc
-)
-target_link_libraries(
- firebase_firestore_util_test
- firebase_firestore_util
+ SOURCES
+ autoid_test.cc
+ secure_random_test.cc
+ string_printf_test.cc
+ DEPENDS
+ firebase_firestore_util
)
if(APPLE)
cc_test(
firebase_firestore_util_apple_test
- assert_test.cc
- log_test.cc
- )
- target_link_libraries(
- firebase_firestore_util_apple_test
- firebase_firestore_util_apple
+ SOURCES
+ assert_test.cc
+ log_test.cc
+ DEPENDS
+ firebase_firestore_util_apple
)
endif(APPLE)
cc_test(
firebase_firestore_util_stdio_test
- assert_test.cc
- log_test.cc
-)
-target_link_libraries(
- firebase_firestore_util_stdio_test
- firebase_firestore_util_stdio
+ SOURCES
+ assert_test.cc
+ log_test.cc
+ DEPENDS
+ firebase_firestore_util_stdio
)
diff --git a/Firestore/core/test/firebase/firestore/util/assert_test.cc b/Firestore/core/test/firebase/firestore/util/assert_test.cc
index 7c49462..fb15e61 100644
--- a/Firestore/core/test/firebase/firestore/util/assert_test.cc
+++ b/Firestore/core/test/firebase/firestore/util/assert_test.cc
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
-
#include <exception>
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
#include "gtest/gtest.h"
namespace firebase {
diff --git a/Firestore/core/test/firebase/firestore/util/autoid_test.cc b/Firestore/core/test/firebase/firestore/util/autoid_test.cc
index e55a8e9..079b990 100644
--- a/Firestore/core/test/firebase/firestore/util/autoid_test.cc
+++ b/Firestore/core/test/firebase/firestore/util/autoid_test.cc
@@ -25,8 +25,8 @@ using firebase::firestore::util::CreateAutoId;
TEST(AutoId, IsSane) {
for (int i = 0; i < 50; i++) {
std::string auto_id = CreateAutoId();
- EXPECT_EQ(20, auto_id.length());
- for (int pos = 0; pos < 20; pos++) {
+ EXPECT_EQ(20u, auto_id.length());
+ for (size_t pos = 0; pos < 20; pos++) {
char c = auto_id[pos];
EXPECT_TRUE(isalpha(c) || isdigit(c))
<< "Should be printable ascii character: '" << c << "' in \""
diff --git a/Firestore/core/test/firebase/firestore/util/string_printf_test.cc b/Firestore/core/test/firebase/firestore/util/string_printf_test.cc
index 76f7cde..085be84 100644
--- a/Firestore/core/test/firebase/firestore/util/string_printf_test.cc
+++ b/Firestore/core/test/firebase/firestore/util/string_printf_test.cc
@@ -64,7 +64,7 @@ TEST(StringPrintf, DontOverwriteErrno) {
TEST(StringPrintf, LargeBuf) {
// Check that the large buffer is handled correctly.
- int n = 2048;
+ size_t n = 2048;
char* buf = new char[n + 1];
memset(buf, ' ', n);
buf[n] = 0;
diff --git a/cmake/CompilerSetup.cmake b/cmake/CompilerSetup.cmake
new file mode 100644
index 0000000..8bcfa76
--- /dev/null
+++ b/cmake/CompilerSetup.cmake
@@ -0,0 +1,92 @@
+# Copyright 2018 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.
+
+# C++ Compiler setup
+
+# We use C++11
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(CLANG ON)
+endif()
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ set(GNU ON)
+endif()
+
+if(CLANG OR GNU)
+ set(
+ common_flags
+ -Wall -Wextra -Wconversion -Werror
+
+ # Be super pedantic about format strings
+ -Wformat
+
+ # Avoid use of uninitialized values
+ -Wuninitialized
+ -fno-common
+
+ # Delete unused things
+ -Wunused-function -Wunused-value -Wunused-variable
+
+ # Cut down on symbol clutter
+ # TODO(wilhuff) try -fvisibility=hidden
+ -fvisibility-inlines-hidden
+ )
+
+ set(
+ c_flags
+ -Wstrict-prototypes
+ )
+
+ if(CLANG)
+ list(
+ APPEND common_flags
+ -Wconditional-uninitialized -Werror=return-type -Winfinite-recursion -Wmove
+ -Wrange-loop-analysis -Wunreachable-code
+ )
+ endif()
+
+ foreach(flag ${common_flags} ${c_flags})
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}")
+ endforeach()
+
+ foreach(flag ${common_flags})
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}")
+ endforeach()
+endif()
+
+if(APPLE)
+ # CMake has no special support for Objective-C as a distinct language but
+ # enabling modules and other clang extensions would apply even to regular C++
+ # sources which is nonportable. Keep these flags separate to avoid misuse.
+ set(
+ OBJC_FLAGS
+ -Werror=deprecated-objc-isa-usage
+ -Werror=non-modular-include-in-framework-module
+ -Werror=objc-root-class
+
+ -Wblock-capture-autoreleasing
+ -Wimplicit-atomic-properties
+ -Wnon-modular-include-in-framework-module
+
+ -fobjc-arc
+ -fmodules
+ -fno-autolink
+
+ -F${FIREBASE_INSTALL_DIR}/Frameworks
+ )
+endif(APPLE)
diff --git a/cmake/ExternalProjectFlags.cmake b/cmake/ExternalProjectFlags.cmake
new file mode 100644
index 0000000..ed4db2c
--- /dev/null
+++ b/cmake/ExternalProjectFlags.cmake
@@ -0,0 +1,71 @@
+# Copyright 2018 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.
+
+include(CMakeParseArguments)
+
+# Assemble the git-related arguments to an external project making use of the
+# latest features where available but avoiding them when run under CMake
+# versions that don't support them.
+#
+# The complete set of git-related arguments are stored as a list in the
+# variable named by RESULT_VAR in the calling scope.
+#
+# Currently this handles:
+# * GIT_SUBMODULES -- added on CMake 3.0 or later. Earlier CMakes will
+# check out all submodules.
+# * GIT_SHALLOW -- added by default on CMake 3.6 or later. Disable by passing
+# GIT_SHALLOW OFF
+# * GIT_PROGRESS -- added by default on CMake 3.8 or later. Disable by
+# passing GIT_PROGRESS OFF
+function(ExternalProject_GitSource RESULT_VAR)
+ # Parse arguments
+ set(options "")
+ set(single_value GIT_REPOSITORY GIT_TAG GIT_PROGRESS GIT_SHALLOW)
+ set(multi_value GIT_SUBMODULES)
+ cmake_parse_arguments(EP "${options}" "${single_value}" "${multi_value}" ${ARGN})
+
+ set(
+ result
+ GIT_REPOSITORY ${EP_GIT_REPOSITORY}
+ GIT_TAG ${EP_GIT_TAG}
+ ${EP_UNPARSED_ARGUMENTS}
+ )
+
+ # CMake 3.0 added support for constraining the set of submodules to clone
+ if(NOT (CMAKE_VERSION VERSION_LESS "3.0") AND EP_GIT_SUBMODULES)
+ list(APPEND result GIT_SUBMODULES ${EP_GIT_SUBMODULES})
+ endif()
+
+ # CMake 3.6 added support for shallow git clones. Use a shallow clone if
+ # available
+ if(NOT (CMAKE_VERSION VERSION_LESS "3.6"))
+ if(NOT EP_GIT_SHALLOW)
+ set(EP_GIT_SHALLOW ON)
+ endif()
+
+ list(APPEND result GIT_SHALLOW ${EP_GIT_SHALLOW})
+ endif()
+
+ # CMake 3.8 added support for showing progress for large downloads
+ if(NOT (CMAKE_VERSION VERSION_LESS "3.8"))
+ if(NOT EP_GIT_PROGRESS)
+ set(EP_GIT_PROGRESS ON)
+ endif()
+
+ list(APPEND result GIT_PROGRESS ${EP_GIT_PROGRESS})
+ endif()
+
+ set(${RESULT_VAR} ${result} PARENT_SCOPE)
+
+endfunction()
diff --git a/cmake/FindGRPC.cmake b/cmake/FindGRPC.cmake
new file mode 100644
index 0000000..f594b9e
--- /dev/null
+++ b/cmake/FindGRPC.cmake
@@ -0,0 +1,142 @@
+# Copyright 2018 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.
+
+include(FindPackageHandleStandardArgs)
+include(FindZLIB)
+
+set(BINARY_DIR ${FIREBASE_INSTALL_DIR}/external/grpc)
+
+## ZLIB
+
+# the grpc ExternalProject already figures out if zlib should be built or
+# referenced from its installed location. If it elected to allow grpc to build
+# zlib then it will be available at this location.
+find_library(
+ ZLIB_LIBRARY
+ NAMES z
+ HINTS ${BINARY_DIR}/src/grpc-build/third_party/zlib
+)
+
+# If found above, the standard package will honor the ZLIB_LIBRARY variable.
+find_package(ZLIB REQUIRED)
+
+
+## BoringSSL/OpenSSL
+
+find_path(
+ OPENSSL_INCLUDE_DIR openssl/ssl.h
+ HINTS ${BINARY_DIR}/src/grpc/third_party/boringssl/include
+)
+
+find_library(
+ OPENSSL_SSL_LIBRARY
+ NAMES ssl
+ HINTS ${BINARY_DIR}/src/grpc-build/third_party/boringssl/ssl
+)
+
+find_library(
+ OPENSSL_CRYPTO_LIBRARY
+ NAMES crypto
+ HINTS ${BINARY_DIR}/src/grpc-build/third_party/boringssl/crypto
+)
+
+find_package(OpenSSL REQUIRED)
+
+
+## C-Ares
+
+find_library(
+ CARES_LIBRARY
+ NAMES cares
+ HINTS ${BINARY_DIR}/src/grpc-build/third_party/cares/cares/lib
+)
+if(NOT (CARES_LIBRARY STREQUAL "CARES_LIBRARY-NOTFOUND"))
+ if (NOT TARGET c-ares::ares)
+ add_library(c-ares::ares UNKNOWN IMPORTED)
+ set_target_properties(
+ c-ares::ares PROPERTIES
+ IMPORTED_LOCATION ${CARES_LIBRARY}
+ )
+ endif()
+endif()
+
+
+## GRPC
+
+find_path(
+ GRPC_INCLUDE_DIR grpc/grpc.h
+ HINTS
+ $ENV{GRPC_ROOT}/include
+ ${GRPC_ROOT}/include
+ ${BINARY_DIR}/src/grpc/include
+)
+
+find_library(
+ GPR_LIBRARY
+ NAMES gpr
+ HINTS
+ $ENV{GRPC_ROOT}/lib
+ ${GRPC_ROOT}/lib
+ ${BINARY_DIR}/src/grpc-build
+)
+
+find_library(
+ GRPC_LIBRARY
+ NAMES grpc
+ HINTS
+ $ENV{GRPC_ROOT}/lib
+ ${GRPC_ROOT}/lib
+ ${BINARY_DIR}/src/grpc-build
+)
+
+find_package_handle_standard_args(
+ gRPC
+ DEFAULT_MSG
+ GRPC_INCLUDE_DIR
+ GRPC_LIBRARY
+ GPR_LIBRARY
+)
+
+if(GRPC_FOUND)
+ set(GRPC_INCLUDE_DIRS ${GRPC_INCLUDE_DIR})
+ set(GRPC_LIBRARIES ${GRPC_LIBRARY})
+
+ if (NOT TARGET grpc::gpr)
+ add_library(grpc::gpr UNKNOWN IMPORTED)
+ set_target_properties(
+ grpc::gpr PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
+ IMPORTED_LOCATION ${GPR_LIBRARY}
+ )
+ endif()
+
+ if (NOT TARGET grpc::grpc)
+ set(
+ GRPC_LINK_LIBRARIES
+ c-ares::ares
+ grpc::gpr
+ OpenSSL::SSL
+ OpenSSL::Crypto
+ ZLIB::ZLIB
+ )
+
+ add_library(grpc::grpc UNKNOWN IMPORTED)
+ set_target_properties(
+ grpc::grpc PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
+ INTERFACE_LINK_LIBRARIES "${GRPC_LINK_LIBRARIES}"
+ IMPORTED_LOCATION ${GRPC_LIBRARY}
+ )
+ endif()
+endif(GRPC_FOUND)
diff --git a/cmake/FindLevelDB.cmake b/cmake/FindLevelDB.cmake
index 386a298..b664fa8 100644
--- a/cmake/FindLevelDB.cmake
+++ b/cmake/FindLevelDB.cmake
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-set(binary_dir ${FIREBASE_INSTALL_DIR}/third_party/leveldb/src/leveldb)
+set(binary_dir ${FIREBASE_INSTALL_DIR}/external/leveldb/src/leveldb)
find_path(
LEVELDB_INCLUDE_DIR leveldb/db.h
diff --git a/cmake/external/firestore.cmake b/cmake/external/firestore.cmake
index 1abb629..1a89435 100644
--- a/cmake/external/firestore.cmake
+++ b/cmake/external/firestore.cmake
@@ -14,27 +14,26 @@
include(ExternalProject)
-set(source_dir ${PROJECT_SOURCE_DIR}/Firestore)
-set(binary_dir ${PROJECT_BINARY_DIR}/Firestore)
-
ExternalProject_Add(
Firestore
- DEPENDS FirebaseCore googletest leveldb
+ DEPENDS
+ FirebaseCore
+ googletest
+ leveldb
+ grpc
# Lay the binary directory out as if this were a subproject. This makes it
# possible to build and test in it directly.
- PREFIX ${binary_dir}
- SOURCE_DIR ${source_dir}
- BINARY_DIR ${binary_dir}
+ PREFIX ${PROJECT_BINARY_DIR}/external/Firestore
+ SOURCE_DIR ${PROJECT_SOURCE_DIR}/Firestore
+ BINARY_DIR ${PROJECT_BINARY_DIR}/Firestore
+
+ CMAKE_ARGS
+ -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+ -DCMAKE_INSTALL_PREFIX:PATH=${FIREBASE_INSTALL_DIR}
+
BUILD_ALWAYS ON
- # Even though this isn't installed, set up the INSTALL_DIR so that
- # find_package can find dependencies built from source.
- INSTALL_DIR ${FIREBASE_INSTALL_DIR}
INSTALL_COMMAND ""
TEST_BEFORE_INSTALL ON
-
- CMAKE_ARGS
- -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
- -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)
diff --git a/cmake/external/googletest.cmake b/cmake/external/googletest.cmake
index 128f849..56a5f13 100644
--- a/cmake/external/googletest.cmake
+++ b/cmake/external/googletest.cmake
@@ -13,21 +13,26 @@
# limitations under the License.
include(ExternalProject)
+include(ExternalProjectFlags)
-ExternalProject_Add(
- googletest
-
+ExternalProject_GitSource(
+ GOOGLETEST_GIT
GIT_REPOSITORY "https://github.com/google/googletest.git"
GIT_TAG "release-1.8.0"
+)
- PREFIX ${PROJECT_BINARY_DIR}/third_party/googletest
+ExternalProject_Add(
+ googletest
+ DEPENDS
+ FirebaseCore # for sequencing
- INSTALL_DIR ${FIREBASE_INSTALL_DIR}
+ ${GOOGLETEST_GIT}
- TEST_COMMAND ""
+ PREFIX ${PROJECT_BINARY_DIR}/external/googletest
- CMAKE_ARGS
- -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
- -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
- -DBUILD_SHARED_LIBS:BOOL=OFF
+ # Just download the sources without building.
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
)
diff --git a/cmake/external/grpc.cmake b/cmake/external/grpc.cmake
new file mode 100644
index 0000000..fb54960
--- /dev/null
+++ b/cmake/external/grpc.cmake
@@ -0,0 +1,83 @@
+# Copyright 2018 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.
+
+include(ExternalProject)
+include(ExternalProjectFlags)
+include(FindZLIB)
+
+if(GRPC_ROOT)
+ # If the user has supplied a GRPC_ROOT then just use it. Add an empty custom
+ # target so that the superbuild dependencies still work.
+ add_custom_target(grpc)
+
+else()
+ set(
+ GIT_SUBMODULES
+ third_party/boringssl
+ third_party/cares/cares
+ )
+
+ set(
+ CMAKE_ARGS
+ -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+ -DgRPC_BUILD_TESTS:BOOL=OFF
+ -DBUILD_SHARED_LIBS:BOOL=OFF
+ )
+
+ # zlib can be built by grpc but we can avoid it on platforms that provide it
+ # by default.
+ find_package(ZLIB)
+ if(ZLIB_FOUND)
+ list(
+ APPEND CMAKE_ARGS
+ -DgRPC_ZLIB_PROVIDER:STRING=package
+ -DZLIB_INCLUDE_DIR=${ZLIB_INCLUDE_DIR}
+ -DZLIB_LIBRARY=${ZLIB_LIBRARY}
+ )
+
+ else()
+ list(
+ APPEND GIT_SUBMODULES
+ third_party/zlib
+ )
+
+ endif(ZLIB_FOUND)
+
+ ExternalProject_GitSource(
+ GRPC_GIT
+ GIT_REPOSITORY "https://github.com/grpc/grpc.git"
+ GIT_TAG "v1.8.3"
+ GIT_SUBMODULES ${GIT_SUBMODULES}
+ )
+
+ ExternalProject_Add(
+ grpc
+ DEPENDS
+ leveldb # for sequencing
+
+ ${GRPC_GIT}
+
+ PREFIX ${PROJECT_BINARY_DIR}/external/grpc
+
+ CMAKE_ARGS ${CMAKE_ARGS}
+
+ BUILD_COMMAND
+ ${CMAKE_COMMAND} --build . --target grpc
+
+ TEST_COMMAND ""
+ INSTALL_COMMAND ""
+ )
+
+endif(GRPC_ROOT)
+
diff --git a/cmake/external/leveldb.cmake b/cmake/external/leveldb.cmake
index b3b3fe1..5b2068a 100644
--- a/cmake/external/leveldb.cmake
+++ b/cmake/external/leveldb.cmake
@@ -13,6 +13,7 @@
# limitations under the License.
include(ExternalProject)
+include(ExternalProjectFlags)
if(WIN32 OR LEVELDB_ROOT)
# If the user has supplied a LEVELDB_ROOT then just use it. Add an empty
@@ -41,23 +42,38 @@ else()
$<$<CONFIG:Release>:${CMAKE_CXX_FLAGS_RELEASE}>"
)
+ ExternalProject_GitSource(
+ LEVELDB_GIT
+ GIT_REPOSITORY "https://github.com/google/leveldb.git"
+ GIT_TAG "v1.20"
+ )
+
ExternalProject_Add(
leveldb
+ DEPENDS
+ googletest # for sequencing
- GIT_REPOSITORY "https://github.com/google/leveldb.git"
- GIT_TAG "v1.20"
+ ${LEVELDB_GIT}
- PREFIX ${PROJECT_BINARY_DIR}/third_party/leveldb
+ PREFIX ${PROJECT_BINARY_DIR}/external/leveldb
+ # LevelDB's configuration is done in the Makefile
CONFIGURE_COMMAND ""
- BUILD_ALWAYS ON
+
+ # The Makefile-based build of leveldb does not support building
+ # out-of-source.
BUILD_IN_SOURCE ON
+
+ # Only build the leveldb library skipping the tools and in-memory
+ # implementation we don't use.
BUILD_COMMAND
- env CXXFLAGS=${LEVELDB_CXX_FLAGS} OPT=${LEVELDB_OPT} make -j all
+ env CXXFLAGS=${LEVELDB_CXX_FLAGS} OPT=${LEVELDB_OPT}
+ make -j out-static/libleveldb.a
INSTALL_DIR ${FIREBASE_INSTALL_DIR}
INSTALL_COMMAND ""
TEST_COMMAND ""
)
+
endif(WIN32 OR LEVELDB_ROOT)
diff --git a/cmake/utils.cmake b/cmake/utils.cmake
index 54044d6..1c3cbd6 100644
--- a/cmake/utils.cmake
+++ b/cmake/utils.cmake
@@ -12,16 +12,80 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Defines a new test executable and does all the things we want done with
-# tests:
+include(CMakeParseArguments)
+
+# cc_library(
+# target
+# SOURCES sources...
+# DEPENDS libraries...
+# )
+#
+# Defines a new library target with the given target name, sources, and dependencies.
+function(cc_library name)
+ set(flag EXCLUDE_FROM_ALL)
+ set(multi DEPENDS SOURCES)
+ cmake_parse_arguments(ccl "${flag}" "" "${multi}" ${ARGN})
+
+ add_library(
+ ${name}
+ ${ccl_SOURCES}
+ )
+ add_objc_flags(${name} ccl)
+ target_link_libraries(
+ ${name}
+ PUBLIC
+ ${ccl_DEPENDS}
+ )
+
+ if(ccl_EXCLUDE_FROM_ALL)
+ set_property(
+ TARGET ${name}
+ PROPERTY EXCLUDE_FROM_ALL ON
+ )
+ endif()
+
+endfunction()
+
+# cc_test(
+# target
+# SOURCES sources...
+# DEPENDS libraries...
+# )
#
-# * add_executable (with the given arguments)
-# * add_Test - defines a test with the same name
-# * declares that the test links against gtest
-# * adds the executable as a dependency of the `check` target.
+# Defines a new test executable target with the given target name, sources, and
+# dependencies. Implicitly adds DEPENDS on GTest::GTest and GTest::Main.
function(cc_test name)
- add_executable(${name} ${ARGN})
+ set(multi DEPENDS SOURCES)
+ cmake_parse_arguments(cct "" "" "${multi}" ${ARGN})
+
+ list(APPEND cct_DEPENDS GTest::GTest GTest::Main)
+
+ add_executable(${name} ${cct_SOURCES})
+ add_objc_flags(${name} cct)
add_test(${name} ${name})
- target_link_libraries(${name} GTest::GTest GTest::Main)
+ target_link_libraries(${name} ${cct_DEPENDS})
+endfunction()
+
+# add_objc_flags(target sources...)
+#
+# Adds OBJC_FLAGS to the compile options of the given target if any of the
+# sources have filenames that indicate they are are Objective-C.
+function(add_objc_flags target)
+ set(_has_objc OFF)
+
+ foreach(source ${ARGN})
+ get_filename_component(ext ${source} EXT)
+ if((ext STREQUAL ".m") OR (ext STREQUAL ".mm"))
+ set(_has_objc ON)
+ endif()
+ endforeach()
+
+ if(_has_objc)
+ target_compile_options(
+ ${target}
+ PRIVATE
+ ${OBJC_FLAGS}
+ )
+ endif()
endfunction()
diff --git a/cmake/xcodebuild.cmake b/cmake/xcodebuild.cmake
index 8312f6d..01a2961 100644
--- a/cmake/xcodebuild.cmake
+++ b/cmake/xcodebuild.cmake
@@ -33,7 +33,7 @@ function(xcodebuild framework)
set(options "")
set(single_value SCHEME WORKSPACE)
set(multi_value DEPENDS)
- cmake_parse_arguments(xcb "${options}" "${single_value}" "${multi_value}")
+ cmake_parse_arguments(xcb "${options}" "${single_value}" "${multi_value}" ${ARGN})
if(NOT xcb_WORKSPACE)
set(xcb_WORKSPACE ${PROJECT_SOURCE_DIR}/Example/Firebase.xcworkspace)
@@ -46,8 +46,6 @@ function(xcodebuild framework)
set(destination "platform=macOS,arch=x86_64")
set(scheme "${framework}-${platform}")
- set(binary_dir ${PROJECT_BINARY_DIR}/${scheme})
-
# CMake has a variety of release types, but Xcode has just one by default.
if(CMAKE_BUILD_TYPE STREQUAL Debug)
set(configuration Debug)
@@ -65,11 +63,11 @@ function(xcodebuild framework)
${framework}
DEPENDS ${xcb_DEPENDS}
- PREFIX ${binary_dir}
+ PREFIX ${PROJECT_BINARY_DIR}/external/${framework}
# The source directory doesn't actually matter
SOURCE_DIR ${PROJECT_SOURCE_DIR}
- BINARY_DIR ${binary_dir}
+ BINARY_DIR ${PROJECT_BINARY_DIR}/Frameworks
CONFIGURE_COMMAND ""
@@ -79,7 +77,7 @@ function(xcodebuild framework)
-scheme ${scheme}
-configuration ${configuration}
-destination ${destination}
- CONFIGURATION_BUILD_DIR=${FIREBASE_INSTALL_DIR}/Frameworks
+ CONFIGURATION_BUILD_DIR=<BINARY_DIR>
build
${pipe_xcpretty}
BUILD_ALWAYS ${BUILD_PODS}
@@ -87,4 +85,5 @@ function(xcodebuild framework)
INSTALL_COMMAND ""
TEST_COMMAND ""
)
+
endfunction()
diff --git a/scripts/style.sh b/scripts/style.sh
index 72f7520..96255f9 100755
--- a/scripts/style.sh
+++ b/scripts/style.sh
@@ -30,16 +30,17 @@ fi
if [[ $# -gt 0 && "$1" = "test-only" ]]; then
test_only=true
options="-output-replacements-xml"
+ shift
else
test_only=false
options="-i"
fi
(
- if [[ "$test_only" = false && $# -gt 0 ]]; then
+ if [[ $# -gt 0 ]]; then
if git rev-parse "$1" -- >& /dev/null; then
# Argument was a branch name show files changed since that branch
- git diff --name-only --relative "$1"
+ git diff --name-only --relative --diff-filter=ACMR "$1"
else
# Otherwise assume the passed things are files or directories
find "$@" -type f
@@ -57,6 +58,9 @@ fi
\%/third_party/% d
\%/Firestore/Port/% d
+# Sources pulled in by travis bundler
+\%/vendor/bundle/% d
+
# Sources within the tree that are not subject to formatting
\%^./(Example|Firebase)/(Auth|AuthSamples|Database|Messaging)/% d
@@ -65,9 +69,11 @@ fi
# Format C-ish sources only
\%\.(h|m|mm|cc)$% p
-' | xargs clang-format -style=file $options | grep "<replacement " > /dev/null
+' | xargs clang-format -style=file $options \
+ | grep "<replacement " > /dev/null
if [[ "$test_only" = true && $? -ne 1 ]]; then
- echo "Proposed commit is not style compliant. Run scripts/style.sh and git add the result."
+ echo "Proposed commit is not style compliant."
+ echo "Run scripts/style.sh and git add the result."
exit 1
fi
diff --git a/test.sh b/test.sh
index 367205a..ee3be7c 100755
--- a/test.sh
+++ b/test.sh
@@ -70,6 +70,3 @@ if [ $RESULT != 0 ]; then exit $RESULT; fi
test_tvOS; RESULT=$?
if [ $RESULT != 0 ]; then exit $RESULT; fi
-
-# Also test Firestore
-Firestore/test.sh