mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-09 22:36:01 +05:45
This simplifies the import process back into google3 and allows us to add additional directories to the project without needing to update the project files for each directory we add. This shows up most clearly in the change to Firestore/Example/Firestore.xcodeproj/project.pbxproj: this no longer needs to list essentially every directory in the project as a header search path. * Clang-format configuration for C++ files * Add support C++-only sources to the podspec * Podspec support for fully qualified imports * xcodeproj changes for fully-qualified imports * Use fully-qualified imports in Firestore sources
362 lines
15 KiB
Plaintext
362 lines
15 KiB
Plaintext
/*
|
|
* Copyright 2017 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 "Firestore/Source/Local/FSTLevelDBKey.h"
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
#import "Firestore/Source/Model/FSTPath.h"
|
|
|
|
#import "Firestore/Example/Tests/Util/FSTHelpers.h"
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
@interface FSTLevelDBKeyTests : XCTestCase
|
|
@end
|
|
|
|
// I can't believe I have to write this...
|
|
bool StartsWith(const std::string &value, const std::string &prefix) {
|
|
return prefix.size() <= value.size() && std::equal(prefix.begin(), prefix.end(), value.begin());
|
|
}
|
|
|
|
static std::string RemoteDocKey(NSString *pathString) {
|
|
return [FSTLevelDBRemoteDocumentKey keyWithDocumentKey:FSTTestDocKey(pathString)];
|
|
}
|
|
|
|
static std::string RemoteDocKeyPrefix(NSString *pathString) {
|
|
return [FSTLevelDBRemoteDocumentKey keyPrefixWithResourcePath:FSTTestPath(pathString)];
|
|
}
|
|
|
|
static std::string DocMutationKey(NSString *userID, NSString *key, FSTBatchID batchID) {
|
|
return [FSTLevelDBDocumentMutationKey keyWithUserID:userID
|
|
documentKey:FSTTestDocKey(key)
|
|
batchID:batchID];
|
|
}
|
|
|
|
static std::string TargetDocKey(FSTTargetID targetID, NSString *key) {
|
|
return [FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:FSTTestDocKey(key)];
|
|
}
|
|
|
|
static std::string DocTargetKey(NSString *key, FSTTargetID targetID) {
|
|
return [FSTLevelDBDocumentTargetKey keyWithDocumentKey:FSTTestDocKey(key) targetID:targetID];
|
|
}
|
|
|
|
/**
|
|
* Asserts that the description for given key is equal to the expected description.
|
|
*
|
|
* @param key A StringView of a textual key
|
|
* @param key An NSString that [FSTLevelDBKey descriptionForKey:] is expected to produce.
|
|
*/
|
|
#define FSTAssertExpectedKeyDescription(key, expectedDescription) \
|
|
XCTAssertEqualObjects([FSTLevelDBKey descriptionForKey:(key)], (expectedDescription))
|
|
|
|
#define FSTAssertKeyLessThan(left, right) \
|
|
do { \
|
|
std::string leftKey = (left); \
|
|
std::string rightKey = (right); \
|
|
XCTAssertLessThan(leftKey.compare(right), 0, @"Expected %@ to be less than %@", \
|
|
[FSTLevelDBKey descriptionForKey:leftKey], \
|
|
[FSTLevelDBKey descriptionForKey:rightKey]); \
|
|
} while (0)
|
|
|
|
@implementation FSTLevelDBKeyTests
|
|
|
|
- (void)testMutationKeyPrefixing {
|
|
auto tableKey = [FSTLevelDBMutationKey keyPrefix];
|
|
auto emptyUserKey = [FSTLevelDBMutationKey keyPrefixWithUserID:""];
|
|
auto fooUserKey = [FSTLevelDBMutationKey keyPrefixWithUserID:"foo"];
|
|
|
|
auto foo2Key = [FSTLevelDBMutationKey keyWithUserID:"foo" batchID:2];
|
|
|
|
XCTAssertTrue(StartsWith(emptyUserKey, tableKey));
|
|
|
|
// This is critical: prefixes of the a value don't convert into prefixes of the key.
|
|
XCTAssertTrue(StartsWith(fooUserKey, tableKey));
|
|
XCTAssertFalse(StartsWith(fooUserKey, emptyUserKey));
|
|
|
|
// However whole segments in common are prefixes.
|
|
XCTAssertTrue(StartsWith(foo2Key, tableKey));
|
|
XCTAssertTrue(StartsWith(foo2Key, fooUserKey));
|
|
}
|
|
|
|
- (void)testMutationKeyEncodeDecodeCycle {
|
|
FSTLevelDBMutationKey *key = [[FSTLevelDBMutationKey alloc] init];
|
|
std::string user("foo");
|
|
|
|
NSArray<NSNumber *> *batchIds = @[ @0, @1, @100, @(INT_MAX - 1), @(INT_MAX) ];
|
|
for (NSNumber *batchIDNumber in batchIds) {
|
|
FSTBatchID batchID = [batchIDNumber intValue];
|
|
auto encoded = [FSTLevelDBMutationKey keyWithUserID:user batchID:batchID];
|
|
|
|
BOOL ok = [key decodeKey:encoded];
|
|
XCTAssertTrue(ok);
|
|
XCTAssertEqual(key.userID, user);
|
|
XCTAssertEqual(key.batchID, batchID);
|
|
}
|
|
}
|
|
|
|
- (void)testMutationKeyDescription {
|
|
FSTAssertExpectedKeyDescription([FSTLevelDBMutationKey keyPrefix], @"[mutation: incomplete key]");
|
|
|
|
FSTAssertExpectedKeyDescription([FSTLevelDBMutationKey keyPrefixWithUserID:@"user1"],
|
|
@"[mutation: userID=user1 incomplete key]");
|
|
|
|
auto key = [FSTLevelDBMutationKey keyWithUserID:@"user1" batchID:42];
|
|
FSTAssertExpectedKeyDescription(key, @"[mutation: userID=user1 batchID=42]");
|
|
|
|
FSTAssertExpectedKeyDescription(key + " extra",
|
|
@"[mutation: userID=user1 batchID=42 invalid "
|
|
@"key=<hW11dGF0aW9uAAGNdXNlcjEAAYqqgCBleHRyYQ==>]");
|
|
|
|
// Truncate the key so that it's missing its terminator.
|
|
key.resize(key.size() - 1);
|
|
FSTAssertExpectedKeyDescription(key, @"[mutation: userID=user1 batchID=42 incomplete key]");
|
|
}
|
|
|
|
- (void)testDocumentMutationKeyPrefixing {
|
|
auto tableKey = [FSTLevelDBDocumentMutationKey keyPrefix];
|
|
auto emptyUserKey = [FSTLevelDBDocumentMutationKey keyPrefixWithUserID:""];
|
|
auto fooUserKey = [FSTLevelDBDocumentMutationKey keyPrefixWithUserID:"foo"];
|
|
|
|
FSTDocumentKey *documentKey = FSTTestDocKey(@"foo/bar");
|
|
auto foo2Key =
|
|
[FSTLevelDBDocumentMutationKey keyWithUserID:"foo" documentKey:documentKey batchID:2];
|
|
|
|
XCTAssertTrue(StartsWith(emptyUserKey, tableKey));
|
|
|
|
// While we want a key with whole segments in common be considered a prefix it's vital that
|
|
// partial segments in common not be prefixes.
|
|
XCTAssertTrue(StartsWith(fooUserKey, tableKey));
|
|
|
|
// Here even though "" is a prefix of "foo" that prefix is within a segment so keys derived from
|
|
// those segments cannot be prefixes of each other.
|
|
XCTAssertFalse(StartsWith(fooUserKey, emptyUserKey));
|
|
XCTAssertFalse(StartsWith(emptyUserKey, fooUserKey));
|
|
|
|
// However whole segments in common are prefixes.
|
|
XCTAssertTrue(StartsWith(foo2Key, tableKey));
|
|
XCTAssertTrue(StartsWith(foo2Key, fooUserKey));
|
|
}
|
|
|
|
- (void)testDocumentMutationKeyEncodeDecodeCycle {
|
|
FSTLevelDBDocumentMutationKey *key = [[FSTLevelDBDocumentMutationKey alloc] init];
|
|
std::string user("foo");
|
|
|
|
NSArray<FSTDocumentKey *> *documentKeys = @[ FSTTestDocKey(@"a/b"), FSTTestDocKey(@"a/b/c/d") ];
|
|
|
|
NSArray<NSNumber *> *batchIds = @[ @0, @1, @100, @(INT_MAX - 1), @(INT_MAX) ];
|
|
for (NSNumber *batchIDNumber in batchIds) {
|
|
for (FSTDocumentKey *documentKey in documentKeys) {
|
|
FSTBatchID batchID = [batchIDNumber intValue];
|
|
auto encoded = [FSTLevelDBDocumentMutationKey keyWithUserID:user
|
|
documentKey:documentKey
|
|
batchID:batchID];
|
|
|
|
BOOL ok = [key decodeKey:encoded];
|
|
XCTAssertTrue(ok);
|
|
XCTAssertEqual(key.userID, user);
|
|
XCTAssertEqualObjects(key.documentKey, documentKey);
|
|
XCTAssertEqual(key.batchID, batchID);
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)testDocumentMutationKeyOrdering {
|
|
// Different user:
|
|
FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0), DocMutationKey(@"10", @"foo/bar", 0));
|
|
FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0), DocMutationKey(@"2", @"foo/bar", 0));
|
|
|
|
// Different paths:
|
|
FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0), DocMutationKey(@"1", @"foo/baz", 0));
|
|
FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0), DocMutationKey(@"1", @"foo/bar2", 0));
|
|
FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0),
|
|
DocMutationKey(@"1", @"foo/bar/suffix/key", 0));
|
|
FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar/suffix/key", 0),
|
|
DocMutationKey(@"1", @"foo/bar2", 0));
|
|
|
|
// Different batchID:
|
|
FSTAssertKeyLessThan(DocMutationKey(@"1", @"foo/bar", 0), DocMutationKey(@"1", @"foo/bar", 1));
|
|
}
|
|
|
|
- (void)testDocumentMutationKeyDescription {
|
|
FSTAssertExpectedKeyDescription([FSTLevelDBDocumentMutationKey keyPrefix],
|
|
@"[document_mutation: incomplete key]");
|
|
|
|
FSTAssertExpectedKeyDescription([FSTLevelDBDocumentMutationKey keyPrefixWithUserID:@"user1"],
|
|
@"[document_mutation: userID=user1 incomplete key]");
|
|
|
|
auto key = [FSTLevelDBDocumentMutationKey keyPrefixWithUserID:@"user1"
|
|
resourcePath:FSTTestPath(@"foo/bar")];
|
|
FSTAssertExpectedKeyDescription(key,
|
|
@"[document_mutation: userID=user1 key=foo/bar incomplete key]");
|
|
|
|
key = [FSTLevelDBDocumentMutationKey keyWithUserID:@"user1"
|
|
documentKey:FSTTestDocKey(@"foo/bar")
|
|
batchID:42];
|
|
FSTAssertExpectedKeyDescription(key, @"[document_mutation: userID=user1 key=foo/bar batchID=42]");
|
|
}
|
|
|
|
- (void)testTargetGlobalKeyEncodeDecodeCycle {
|
|
FSTLevelDBTargetGlobalKey *key = [[FSTLevelDBTargetGlobalKey alloc] init];
|
|
|
|
auto encoded = [FSTLevelDBTargetGlobalKey key];
|
|
BOOL ok = [key decodeKey:encoded];
|
|
XCTAssertTrue(ok);
|
|
}
|
|
|
|
- (void)testTargetGlobalKeyDescription {
|
|
FSTAssertExpectedKeyDescription([FSTLevelDBTargetGlobalKey key], @"[target_global:]");
|
|
}
|
|
|
|
- (void)testTargetKeyEncodeDecodeCycle {
|
|
FSTLevelDBTargetKey *key = [[FSTLevelDBTargetKey alloc] init];
|
|
FSTTargetID targetID = 42;
|
|
|
|
auto encoded = [FSTLevelDBTargetKey keyWithTargetID:42];
|
|
BOOL ok = [key decodeKey:encoded];
|
|
XCTAssertTrue(ok);
|
|
XCTAssertEqual(key.targetID, targetID);
|
|
}
|
|
|
|
- (void)testTargetKeyDescription {
|
|
FSTAssertExpectedKeyDescription([FSTLevelDBTargetKey keyWithTargetID:42],
|
|
@"[target: targetID=42]");
|
|
}
|
|
|
|
- (void)testQueryTargetKeyEncodeDecodeCycle {
|
|
FSTLevelDBQueryTargetKey *key = [[FSTLevelDBQueryTargetKey alloc] init];
|
|
std::string canonicalID("foo");
|
|
FSTTargetID targetID = 42;
|
|
|
|
auto encoded = [FSTLevelDBQueryTargetKey keyWithCanonicalID:canonicalID targetID:42];
|
|
BOOL ok = [key decodeKey:encoded];
|
|
XCTAssertTrue(ok);
|
|
XCTAssertEqual(key.canonicalID, canonicalID);
|
|
XCTAssertEqual(key.targetID, targetID);
|
|
}
|
|
|
|
- (void)testQueryKeyDescription {
|
|
FSTAssertExpectedKeyDescription([FSTLevelDBQueryTargetKey keyWithCanonicalID:"foo" targetID:42],
|
|
@"[query_target: canonicalID=foo targetID=42]");
|
|
}
|
|
|
|
- (void)testTargetDocumentKeyEncodeDecodeCycle {
|
|
FSTLevelDBTargetDocumentKey *key = [[FSTLevelDBTargetDocumentKey alloc] init];
|
|
|
|
auto encoded =
|
|
[FSTLevelDBTargetDocumentKey keyWithTargetID:42 documentKey:FSTTestDocKey(@"foo/bar")];
|
|
BOOL ok = [key decodeKey:encoded];
|
|
XCTAssertTrue(ok);
|
|
XCTAssertEqual(key.targetID, 42);
|
|
XCTAssertEqualObjects(key.documentKey, FSTTestDocKey(@"foo/bar"));
|
|
}
|
|
|
|
- (void)testTargetDocumentKeyOrdering {
|
|
// Different targetID:
|
|
FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(2, @"foo/bar"));
|
|
FSTAssertKeyLessThan(TargetDocKey(2, @"foo/bar"), TargetDocKey(10, @"foo/bar"));
|
|
FSTAssertKeyLessThan(TargetDocKey(10, @"foo/bar"), TargetDocKey(100, @"foo/bar"));
|
|
FSTAssertKeyLessThan(TargetDocKey(42, @"foo/bar"), TargetDocKey(100, @"foo/bar"));
|
|
|
|
// Different paths:
|
|
FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(1, @"foo/baz"));
|
|
FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(1, @"foo/bar2"));
|
|
FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(1, @"foo/bar/suffix/key"));
|
|
FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar/suffix/key"), TargetDocKey(1, @"foo/bar2"));
|
|
}
|
|
|
|
- (void)testTargetDocumentKeyDescription {
|
|
auto key = [FSTLevelDBTargetDocumentKey keyWithTargetID:42 documentKey:FSTTestDocKey(@"foo/bar")];
|
|
XCTAssertEqualObjects([FSTLevelDBKey descriptionForKey:key],
|
|
@"[target_document: targetID=42 key=foo/bar]");
|
|
}
|
|
|
|
- (void)testDocumentTargetKeyEncodeDecodeCycle {
|
|
FSTLevelDBDocumentTargetKey *key = [[FSTLevelDBDocumentTargetKey alloc] init];
|
|
|
|
auto encoded =
|
|
[FSTLevelDBDocumentTargetKey keyWithDocumentKey:FSTTestDocKey(@"foo/bar") targetID:42];
|
|
BOOL ok = [key decodeKey:encoded];
|
|
XCTAssertTrue(ok);
|
|
XCTAssertEqualObjects(key.documentKey, FSTTestDocKey(@"foo/bar"));
|
|
XCTAssertEqual(key.targetID, 42);
|
|
}
|
|
|
|
- (void)testDocumentTargetKeyDescription {
|
|
auto key = [FSTLevelDBDocumentTargetKey keyWithDocumentKey:FSTTestDocKey(@"foo/bar") targetID:42];
|
|
XCTAssertEqualObjects([FSTLevelDBKey descriptionForKey:key],
|
|
@"[document_target: key=foo/bar targetID=42]");
|
|
}
|
|
|
|
- (void)testDocumentTargetKeyOrdering {
|
|
// Different paths:
|
|
FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/baz", 1));
|
|
FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/bar2", 1));
|
|
FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/bar/suffix/key", 1));
|
|
FSTAssertKeyLessThan(DocTargetKey(@"foo/bar/suffix/key", 1), DocTargetKey(@"foo/bar2", 1));
|
|
|
|
// Different targetID:
|
|
FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/bar", 2));
|
|
FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 2), DocTargetKey(@"foo/bar", 10));
|
|
FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 10), DocTargetKey(@"foo/bar", 100));
|
|
FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 42), DocTargetKey(@"foo/bar", 100));
|
|
}
|
|
|
|
- (void)testRemoteDocumentKeyPrefixing {
|
|
auto tableKey = [FSTLevelDBRemoteDocumentKey keyPrefix];
|
|
|
|
XCTAssertTrue(StartsWith(RemoteDocKey(@"foo/bar"), tableKey));
|
|
|
|
// This is critical: foo/bar2 should not contain foo/bar.
|
|
XCTAssertFalse(StartsWith(RemoteDocKey(@"foo/bar2"), RemoteDocKey(@"foo/bar")));
|
|
|
|
// Prefixes must be encoded specially
|
|
XCTAssertFalse(StartsWith(RemoteDocKey(@"foo/bar/baz/quu"), RemoteDocKey(@"foo/bar")));
|
|
XCTAssertTrue(StartsWith(RemoteDocKey(@"foo/bar/baz/quu"), RemoteDocKeyPrefix(@"foo/bar")));
|
|
XCTAssertTrue(StartsWith(RemoteDocKeyPrefix(@"foo/bar/baz/quu"), RemoteDocKeyPrefix(@"foo/bar")));
|
|
XCTAssertTrue(StartsWith(RemoteDocKeyPrefix(@"foo/bar/baz"), RemoteDocKeyPrefix(@"foo/bar")));
|
|
XCTAssertTrue(StartsWith(RemoteDocKeyPrefix(@"foo/bar"), RemoteDocKeyPrefix(@"foo")));
|
|
}
|
|
|
|
- (void)testRemoteDocumentKeyOrdering {
|
|
FSTAssertKeyLessThan(RemoteDocKey(@"foo/bar"), RemoteDocKey(@"foo/bar2"));
|
|
FSTAssertKeyLessThan(RemoteDocKey(@"foo/bar"), RemoteDocKey(@"foo/bar/suffix/key"));
|
|
}
|
|
|
|
- (void)testRemoteDocumentKeyEncodeDecodeCycle {
|
|
FSTLevelDBRemoteDocumentKey *key = [[FSTLevelDBRemoteDocumentKey alloc] init];
|
|
|
|
NSArray<NSString *> *paths = @[ @"foo/bar", @"foo/bar2", @"foo/bar/baz/quux" ];
|
|
for (NSString *path in paths) {
|
|
auto encoded = RemoteDocKey(path);
|
|
BOOL ok = [key decodeKey:encoded];
|
|
XCTAssertTrue(ok);
|
|
XCTAssertEqualObjects(key.documentKey, FSTTestDocKey(path));
|
|
}
|
|
}
|
|
|
|
- (void)testRemoteDocumentKeyDescription {
|
|
FSTAssertExpectedKeyDescription(
|
|
[FSTLevelDBRemoteDocumentKey keyWithDocumentKey:FSTTestDocKey(@"foo/bar/baz/quux")],
|
|
@"[remote_document: key=foo/bar/baz/quux]");
|
|
}
|
|
|
|
@end
|
|
|
|
#undef FSTAssertExpectedKeyDescription
|
|
|
|
NS_ASSUME_NONNULL_END
|