FSTLevelDBKeyTests.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "Firestore/Source/Local/FSTLevelDBKey.h"
  17. #import <XCTest/XCTest.h>
  18. #include <string>
  19. #import "Firestore/Example/Tests/Util/FSTHelpers.h"
  20. #include "Firestore/core/src/firebase/firestore/local/leveldb_key.h"
  21. #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
  22. #include "Firestore/core/test/firebase/firestore/testutil/testutil.h"
  23. namespace util = firebase::firestore::util;
  24. namespace testutil = firebase::firestore::testutil;
  25. using firebase::firestore::local::DescribeKey;
  26. NS_ASSUME_NONNULL_BEGIN
  27. @interface FSTLevelDBKeyTests : XCTestCase
  28. @end
  29. // I can't believe I have to write this...
  30. bool StartsWith(const std::string &value, const std::string &prefix) {
  31. return prefix.size() <= value.size() && std::equal(prefix.begin(), prefix.end(), value.begin());
  32. }
  33. static std::string RemoteDocKey(NSString *pathString) {
  34. return [FSTLevelDBRemoteDocumentKey keyWithDocumentKey:FSTTestDocKey(pathString)];
  35. }
  36. static std::string RemoteDocKeyPrefix(NSString *pathString) {
  37. return [FSTLevelDBRemoteDocumentKey
  38. keyPrefixWithResourcePath:testutil::Resource(util::MakeStringView(pathString))];
  39. }
  40. static std::string DocMutationKey(const std::string &userID, NSString *key, FSTBatchID batchID) {
  41. return [FSTLevelDBDocumentMutationKey keyWithUserID:userID
  42. documentKey:FSTTestDocKey(key)
  43. batchID:batchID];
  44. }
  45. static std::string TargetDocKey(FSTTargetID targetID, NSString *key) {
  46. return [FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:FSTTestDocKey(key)];
  47. }
  48. static std::string DocTargetKey(NSString *key, FSTTargetID targetID) {
  49. return [FSTLevelDBDocumentTargetKey keyWithDocumentKey:FSTTestDocKey(key) targetID:targetID];
  50. }
  51. #define FSTAssertKeyLessThan(left, right) \
  52. do { \
  53. std::string leftKey = (left); \
  54. std::string rightKey = (right); \
  55. XCTAssertLessThan(leftKey.compare(right), 0, @"Expected %s to be less than %s", \
  56. DescribeKey(leftKey).c_str(), DescribeKey(rightKey).c_str()); \
  57. } while (0)
  58. @implementation FSTLevelDBKeyTests
  59. - (void)testMutationKeyPrefixing {
  60. auto tableKey = [FSTLevelDBMutationKey keyPrefix];
  61. auto emptyUserKey = [FSTLevelDBMutationKey keyPrefixWithUserID:""];
  62. auto fooUserKey = [FSTLevelDBMutationKey keyPrefixWithUserID:"foo"];
  63. auto foo2Key = [FSTLevelDBMutationKey keyWithUserID:"foo" batchID:2];
  64. XCTAssertTrue(StartsWith(emptyUserKey, tableKey));
  65. // This is critical: prefixes of the a value don't convert into prefixes of the key.
  66. XCTAssertTrue(StartsWith(fooUserKey, tableKey));
  67. XCTAssertFalse(StartsWith(fooUserKey, emptyUserKey));
  68. // However whole segments in common are prefixes.
  69. XCTAssertTrue(StartsWith(foo2Key, tableKey));
  70. XCTAssertTrue(StartsWith(foo2Key, fooUserKey));
  71. }
  72. - (void)testMutationKeyEncodeDecodeCycle {
  73. FSTLevelDBMutationKey *key = [[FSTLevelDBMutationKey alloc] init];
  74. std::string user("foo");
  75. NSArray<NSNumber *> *batchIds = @[ @0, @1, @100, @(INT_MAX - 1), @(INT_MAX) ];
  76. for (NSNumber *batchIDNumber in batchIds) {
  77. FSTBatchID batchID = [batchIDNumber intValue];
  78. auto encoded = [FSTLevelDBMutationKey keyWithUserID:user batchID:batchID];
  79. BOOL ok = [key decodeKey:encoded];
  80. XCTAssertTrue(ok);
  81. XCTAssertEqual(key.userID, user);
  82. XCTAssertEqual(key.batchID, batchID);
  83. }
  84. }
  85. - (void)testDocumentMutationKeyPrefixing {
  86. auto tableKey = [FSTLevelDBDocumentMutationKey keyPrefix];
  87. auto emptyUserKey = [FSTLevelDBDocumentMutationKey keyPrefixWithUserID:""];
  88. auto fooUserKey = [FSTLevelDBDocumentMutationKey keyPrefixWithUserID:"foo"];
  89. FSTDocumentKey *documentKey = FSTTestDocKey(@"foo/bar");
  90. auto foo2Key =
  91. [FSTLevelDBDocumentMutationKey keyWithUserID:"foo" documentKey:documentKey batchID:2];
  92. XCTAssertTrue(StartsWith(emptyUserKey, tableKey));
  93. // While we want a key with whole segments in common be considered a prefix it's vital that
  94. // partial segments in common not be prefixes.
  95. XCTAssertTrue(StartsWith(fooUserKey, tableKey));
  96. // Here even though "" is a prefix of "foo" that prefix is within a segment so keys derived from
  97. // those segments cannot be prefixes of each other.
  98. XCTAssertFalse(StartsWith(fooUserKey, emptyUserKey));
  99. XCTAssertFalse(StartsWith(emptyUserKey, fooUserKey));
  100. // However whole segments in common are prefixes.
  101. XCTAssertTrue(StartsWith(foo2Key, tableKey));
  102. XCTAssertTrue(StartsWith(foo2Key, fooUserKey));
  103. }
  104. - (void)testDocumentMutationKeyEncodeDecodeCycle {
  105. FSTLevelDBDocumentMutationKey *key = [[FSTLevelDBDocumentMutationKey alloc] init];
  106. std::string user("foo");
  107. NSArray<FSTDocumentKey *> *documentKeys = @[ FSTTestDocKey(@"a/b"), FSTTestDocKey(@"a/b/c/d") ];
  108. NSArray<NSNumber *> *batchIds = @[ @0, @1, @100, @(INT_MAX - 1), @(INT_MAX) ];
  109. for (NSNumber *batchIDNumber in batchIds) {
  110. for (FSTDocumentKey *documentKey in documentKeys) {
  111. FSTBatchID batchID = [batchIDNumber intValue];
  112. auto encoded = [FSTLevelDBDocumentMutationKey keyWithUserID:user
  113. documentKey:documentKey
  114. batchID:batchID];
  115. BOOL ok = [key decodeKey:encoded];
  116. XCTAssertTrue(ok);
  117. XCTAssertEqual(key.userID, user);
  118. XCTAssertEqualObjects(key.documentKey, documentKey);
  119. XCTAssertEqual(key.batchID, batchID);
  120. }
  121. }
  122. }
  123. - (void)testDocumentMutationKeyOrdering {
  124. // Different user:
  125. FSTAssertKeyLessThan(DocMutationKey("1", @"foo/bar", 0), DocMutationKey("10", @"foo/bar", 0));
  126. FSTAssertKeyLessThan(DocMutationKey("1", @"foo/bar", 0), DocMutationKey("2", @"foo/bar", 0));
  127. // Different paths:
  128. FSTAssertKeyLessThan(DocMutationKey("1", @"foo/bar", 0), DocMutationKey("1", @"foo/baz", 0));
  129. FSTAssertKeyLessThan(DocMutationKey("1", @"foo/bar", 0), DocMutationKey("1", @"foo/bar2", 0));
  130. FSTAssertKeyLessThan(DocMutationKey("1", @"foo/bar", 0),
  131. DocMutationKey("1", @"foo/bar/suffix/key", 0));
  132. FSTAssertKeyLessThan(DocMutationKey("1", @"foo/bar/suffix/key", 0),
  133. DocMutationKey("1", @"foo/bar2", 0));
  134. // Different batchID:
  135. FSTAssertKeyLessThan(DocMutationKey("1", @"foo/bar", 0), DocMutationKey("1", @"foo/bar", 1));
  136. }
  137. - (void)testTargetGlobalKeyEncodeDecodeCycle {
  138. FSTLevelDBTargetGlobalKey *key = [[FSTLevelDBTargetGlobalKey alloc] init];
  139. auto encoded = [FSTLevelDBTargetGlobalKey key];
  140. BOOL ok = [key decodeKey:encoded];
  141. XCTAssertTrue(ok);
  142. }
  143. - (void)testTargetKeyEncodeDecodeCycle {
  144. FSTLevelDBTargetKey *key = [[FSTLevelDBTargetKey alloc] init];
  145. FSTTargetID targetID = 42;
  146. auto encoded = [FSTLevelDBTargetKey keyWithTargetID:42];
  147. BOOL ok = [key decodeKey:encoded];
  148. XCTAssertTrue(ok);
  149. XCTAssertEqual(key.targetID, targetID);
  150. }
  151. - (void)testQueryTargetKeyEncodeDecodeCycle {
  152. FSTLevelDBQueryTargetKey *key = [[FSTLevelDBQueryTargetKey alloc] init];
  153. std::string canonicalID("foo");
  154. FSTTargetID targetID = 42;
  155. auto encoded = [FSTLevelDBQueryTargetKey keyWithCanonicalID:canonicalID targetID:42];
  156. BOOL ok = [key decodeKey:encoded];
  157. XCTAssertTrue(ok);
  158. XCTAssertEqual(key.canonicalID, canonicalID);
  159. XCTAssertEqual(key.targetID, targetID);
  160. }
  161. - (void)testTargetDocumentKeyEncodeDecodeCycle {
  162. FSTLevelDBTargetDocumentKey *key = [[FSTLevelDBTargetDocumentKey alloc] init];
  163. auto encoded =
  164. [FSTLevelDBTargetDocumentKey keyWithTargetID:42 documentKey:FSTTestDocKey(@"foo/bar")];
  165. BOOL ok = [key decodeKey:encoded];
  166. XCTAssertTrue(ok);
  167. XCTAssertEqual(key.targetID, 42);
  168. XCTAssertEqualObjects(key.documentKey, FSTTestDocKey(@"foo/bar"));
  169. }
  170. - (void)testTargetDocumentKeyOrdering {
  171. // Different targetID:
  172. FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(2, @"foo/bar"));
  173. FSTAssertKeyLessThan(TargetDocKey(2, @"foo/bar"), TargetDocKey(10, @"foo/bar"));
  174. FSTAssertKeyLessThan(TargetDocKey(10, @"foo/bar"), TargetDocKey(100, @"foo/bar"));
  175. FSTAssertKeyLessThan(TargetDocKey(42, @"foo/bar"), TargetDocKey(100, @"foo/bar"));
  176. // Different paths:
  177. FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(1, @"foo/baz"));
  178. FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(1, @"foo/bar2"));
  179. FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar"), TargetDocKey(1, @"foo/bar/suffix/key"));
  180. FSTAssertKeyLessThan(TargetDocKey(1, @"foo/bar/suffix/key"), TargetDocKey(1, @"foo/bar2"));
  181. }
  182. - (void)testTargetDocumentKeyDescription {
  183. auto key = [FSTLevelDBTargetDocumentKey keyWithTargetID:42 documentKey:FSTTestDocKey(@"foo/bar")];
  184. }
  185. - (void)testDocumentTargetKeyEncodeDecodeCycle {
  186. FSTLevelDBDocumentTargetKey *key = [[FSTLevelDBDocumentTargetKey alloc] init];
  187. auto encoded =
  188. [FSTLevelDBDocumentTargetKey keyWithDocumentKey:FSTTestDocKey(@"foo/bar") targetID:42];
  189. BOOL ok = [key decodeKey:encoded];
  190. XCTAssertTrue(ok);
  191. XCTAssertEqualObjects(key.documentKey, FSTTestDocKey(@"foo/bar"));
  192. XCTAssertEqual(key.targetID, 42);
  193. }
  194. - (void)testDocumentTargetKeyOrdering {
  195. // Different paths:
  196. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/baz", 1));
  197. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/bar2", 1));
  198. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/bar/suffix/key", 1));
  199. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar/suffix/key", 1), DocTargetKey(@"foo/bar2", 1));
  200. // Different targetID:
  201. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 1), DocTargetKey(@"foo/bar", 2));
  202. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 2), DocTargetKey(@"foo/bar", 10));
  203. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 10), DocTargetKey(@"foo/bar", 100));
  204. FSTAssertKeyLessThan(DocTargetKey(@"foo/bar", 42), DocTargetKey(@"foo/bar", 100));
  205. }
  206. - (void)testRemoteDocumentKeyPrefixing {
  207. auto tableKey = [FSTLevelDBRemoteDocumentKey keyPrefix];
  208. XCTAssertTrue(StartsWith(RemoteDocKey(@"foo/bar"), tableKey));
  209. // This is critical: foo/bar2 should not contain foo/bar.
  210. XCTAssertFalse(StartsWith(RemoteDocKey(@"foo/bar2"), RemoteDocKey(@"foo/bar")));
  211. // Prefixes must be encoded specially
  212. XCTAssertFalse(StartsWith(RemoteDocKey(@"foo/bar/baz/quu"), RemoteDocKey(@"foo/bar")));
  213. XCTAssertTrue(StartsWith(RemoteDocKey(@"foo/bar/baz/quu"), RemoteDocKeyPrefix(@"foo/bar")));
  214. XCTAssertTrue(StartsWith(RemoteDocKeyPrefix(@"foo/bar/baz/quu"), RemoteDocKeyPrefix(@"foo/bar")));
  215. XCTAssertTrue(StartsWith(RemoteDocKeyPrefix(@"foo/bar/baz"), RemoteDocKeyPrefix(@"foo/bar")));
  216. XCTAssertTrue(StartsWith(RemoteDocKeyPrefix(@"foo/bar"), RemoteDocKeyPrefix(@"foo")));
  217. }
  218. - (void)testRemoteDocumentKeyOrdering {
  219. FSTAssertKeyLessThan(RemoteDocKey(@"foo/bar"), RemoteDocKey(@"foo/bar2"));
  220. FSTAssertKeyLessThan(RemoteDocKey(@"foo/bar"), RemoteDocKey(@"foo/bar/suffix/key"));
  221. }
  222. - (void)testRemoteDocumentKeyEncodeDecodeCycle {
  223. FSTLevelDBRemoteDocumentKey *key = [[FSTLevelDBRemoteDocumentKey alloc] init];
  224. NSArray<NSString *> *paths = @[ @"foo/bar", @"foo/bar2", @"foo/bar/baz/quux" ];
  225. for (NSString *path in paths) {
  226. auto encoded = RemoteDocKey(path);
  227. BOOL ok = [key decodeKey:encoded];
  228. XCTAssertTrue(ok);
  229. XCTAssertEqualObjects(key.documentKey, FSTTestDocKey(path));
  230. }
  231. }
  232. @end
  233. #undef FSTAssertExpectedKeyDescription
  234. NS_ASSUME_NONNULL_END