| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- /*
- * 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>
- #include <string>
- #import "Firestore/Example/Tests/Util/FSTHelpers.h"
- #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
- #include "Firestore/core/test/firebase/firestore/testutil/testutil.h"
- namespace util = firebase::firestore::util;
- namespace testutil = firebase::firestore::testutil;
- 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:testutil::Resource(util::MakeStringView(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:testutil::Resource("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
|