FSTLevelDBTransactionTests.mm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /*
  2. * Copyright 2018 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 <XCTest/XCTest.h>
  17. #include <memory>
  18. #include <string>
  19. // This is out of order to satisfy the linter, which doesn't realize this is
  20. // the header corresponding to this test.
  21. // TODO(wilhuff): move this to the top once the test filename matches
  22. #include "Firestore/core/src/firebase/firestore/local/leveldb_transaction.h"
  23. #import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h"
  24. #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h"
  25. #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
  26. #include "Firestore/core/src/firebase/firestore/local/leveldb_key.h"
  27. #include "absl/strings/string_view.h"
  28. #include "leveldb/db.h"
  29. NS_ASSUME_NONNULL_BEGIN
  30. using firebase::firestore::local::LevelDbMutationKey;
  31. using firebase::firestore::local::LevelDbTransaction;
  32. using firebase::firestore::util::Path;
  33. using leveldb::DB;
  34. using leveldb::Options;
  35. using leveldb::ReadOptions;
  36. using leveldb::Status;
  37. using leveldb::WriteOptions;
  38. @interface FSTLevelDBTransactionTests : XCTestCase
  39. @end
  40. @implementation FSTLevelDBTransactionTests {
  41. std::unique_ptr<DB> _db;
  42. }
  43. - (void)setUp {
  44. Options options;
  45. options.error_if_exists = true;
  46. options.create_if_missing = true;
  47. Path dir = [FSTPersistenceTestHelpers levelDBDir];
  48. DB *db;
  49. Status status = DB::Open(options, dir.ToUtf8String(), &db);
  50. XCTAssert(status.ok(), @"Failed to create db: %s", status.ToString().c_str());
  51. _db.reset(db);
  52. }
  53. - (void)tearDown {
  54. _db.reset();
  55. }
  56. - (void)testCreateTransaction {
  57. LevelDbTransaction transaction(_db.get(), "testCreateTransaction");
  58. std::string key = "key1";
  59. transaction.Put(key, "value");
  60. auto iter = transaction.NewIterator();
  61. iter->Seek(key);
  62. XCTAssertEqual(key, iter->key());
  63. iter->Next();
  64. XCTAssertFalse(iter->Valid());
  65. }
  66. - (void)testCanReadCommittedAndMutations {
  67. const std::string committed_key1 = "c_key1";
  68. const std::string committed_value1 = "c_value1";
  69. const WriteOptions &writeOptions = LevelDbTransaction::DefaultWriteOptions();
  70. // add two things committed, mutate one, add another mutation
  71. // verify you can get the original committed, the mutation, and the addition
  72. Status status = _db->Put(writeOptions, committed_key1, committed_value1);
  73. XCTAssertTrue(status.ok());
  74. const std::string committed_key2 = "c_key2";
  75. const std::string committed_value2 = "c_value2";
  76. status = _db->Put(writeOptions, committed_key2, committed_value2);
  77. XCTAssertTrue(status.ok());
  78. LevelDbTransaction transaction(_db.get(), "testCanReadCommittedAndMutations");
  79. const std::string mutation_key1 = "m_key1";
  80. const std::string mutation_value1 = "m_value1";
  81. transaction.Put(mutation_key1, mutation_value1);
  82. const std::string mutation_key2 = committed_key2;
  83. const std::string mutation_value2 = "m_value2";
  84. transaction.Put(mutation_key2, mutation_value2);
  85. std::string value;
  86. status = transaction.Get(committed_key1, &value);
  87. XCTAssertTrue(status.ok());
  88. XCTAssertEqual(value, committed_value1);
  89. status = transaction.Get(mutation_key1, &value);
  90. XCTAssertTrue(status.ok());
  91. XCTAssertEqual(value, mutation_value1);
  92. status = transaction.Get(committed_key2, &value);
  93. XCTAssertTrue(status.ok());
  94. XCTAssertEqual(value, mutation_value2);
  95. }
  96. - (void)testDeleteCommitted {
  97. // add something committed, delete it, verify you can't read it
  98. for (int i = 0; i < 3; ++i) {
  99. Status status = _db->Put(LevelDbTransaction::DefaultWriteOptions(), "key_" + std::to_string(i),
  100. "value_" + std::to_string(i));
  101. XCTAssertTrue(status.ok());
  102. }
  103. LevelDbTransaction transaction(_db.get(), "testDeleteCommitted");
  104. transaction.Put("key_1", "new_value");
  105. std::string value;
  106. Status status = transaction.Get("key_1", &value);
  107. XCTAssertTrue(status.ok());
  108. XCTAssertEqual(value, "new_value");
  109. transaction.Delete("key_1");
  110. status = transaction.Get("key_1", &value);
  111. XCTAssertTrue(status.IsNotFound());
  112. LevelDbTransaction::Iterator iter(&transaction);
  113. iter.Seek("");
  114. XCTAssertEqual(iter.key(), "key_0");
  115. iter.Next();
  116. XCTAssertEqual(iter.key(), "key_2");
  117. iter.Next();
  118. XCTAssertFalse(iter.Valid());
  119. }
  120. - (void)testMutateDeleted {
  121. // delete something, then mutate it, then read it.
  122. // Also include an actual deletion
  123. for (int i = 0; i < 4; ++i) {
  124. Status status = _db->Put(LevelDbTransaction::DefaultWriteOptions(), "key_" + std::to_string(i),
  125. "value_" + std::to_string(i));
  126. XCTAssertTrue(status.ok());
  127. }
  128. std::string value;
  129. LevelDbTransaction transaction(_db.get(), "testMutateDeleted");
  130. transaction.Delete("key_1");
  131. Status status = transaction.Get("key_1", &value);
  132. XCTAssertTrue(status.IsNotFound());
  133. transaction.Put("key_1", "new_value");
  134. status = transaction.Get("key_1", &value);
  135. XCTAssertTrue(status.ok());
  136. XCTAssertEqual(value, "new_value");
  137. transaction.Delete("key_3");
  138. LevelDbTransaction::Iterator iter(&transaction);
  139. iter.Seek("");
  140. XCTAssertEqual(iter.key(), "key_0");
  141. iter.Next();
  142. XCTAssertEqual(iter.key(), "key_1");
  143. XCTAssertEqual(iter.value(), "new_value");
  144. iter.Next();
  145. XCTAssertEqual(iter.key(), "key_2");
  146. iter.Next();
  147. XCTAssertFalse(iter.Valid());
  148. // Commit, then check underlying db.
  149. transaction.Commit();
  150. const ReadOptions &readOptions = LevelDbTransaction::DefaultReadOptions();
  151. status = _db->Get(readOptions, "key_0", &value);
  152. XCTAssertTrue(status.ok());
  153. XCTAssertEqual("value_0", value);
  154. status = _db->Get(readOptions, "key_1", &value);
  155. XCTAssertTrue(status.ok());
  156. XCTAssertEqual("new_value", value);
  157. status = _db->Get(readOptions, "key_2", &value);
  158. XCTAssertTrue(status.ok());
  159. XCTAssertEqual("value_2", value);
  160. status = _db->Get(readOptions, "key_3", &value);
  161. XCTAssertTrue(status.IsNotFound());
  162. }
  163. - (void)testProtobufSupport {
  164. LevelDbTransaction transaction(_db.get(), "testProtobufSupport");
  165. FSTPBTarget *target = [FSTPBTarget message];
  166. target.targetId = 1;
  167. target.lastListenSequenceNumber = 2;
  168. std::string key("theKey");
  169. transaction.Put(key, target);
  170. std::string value;
  171. Status status = transaction.Get("theKey", &value);
  172. NSData *result = [[NSData alloc] initWithBytesNoCopy:(void *)value.data()
  173. length:value.size()
  174. freeWhenDone:NO];
  175. NSError *error;
  176. FSTPBTarget *parsed = [FSTPBTarget parseFromData:result error:&error];
  177. XCTAssertNil(error);
  178. XCTAssertTrue([target isEqual:parsed]);
  179. }
  180. - (void)testCanIterateAndDelete {
  181. LevelDbTransaction transaction(_db.get(), "testCanIterateAndDelete");
  182. for (int i = 0; i < 4; ++i) {
  183. transaction.Put("key_" + std::to_string(i), "value_" + std::to_string(i));
  184. }
  185. auto it = transaction.NewIterator();
  186. it->Seek("key_0");
  187. for (int i = 0; i < 4; ++i) {
  188. XCTAssertTrue(it->Valid());
  189. absl::string_view key = it->key();
  190. std::string expected = "key_" + std::to_string(i);
  191. XCTAssertEqual(expected, key);
  192. transaction.Delete(key);
  193. it->Next();
  194. }
  195. }
  196. - (void)testCanIterateFromDeletionToCommitted {
  197. // Write keys key_0 and key_1
  198. for (int i = 0; i < 2; ++i) {
  199. Status status = _db->Put(LevelDbTransaction::DefaultWriteOptions(), "key_" + std::to_string(i),
  200. "value_" + std::to_string(i));
  201. XCTAssertTrue(status.ok());
  202. }
  203. // Create a transaction, iterate, deleting key_0. Verify we still iterate key_1.
  204. LevelDbTransaction transaction(_db.get(), "testCanIterateFromDeletionToCommitted");
  205. auto it = transaction.NewIterator();
  206. it->Seek("key_0");
  207. XCTAssertTrue(it->Valid());
  208. XCTAssertEqual("key_0", it->key());
  209. transaction.Delete("key_0");
  210. it->Next();
  211. XCTAssertTrue(it->Valid());
  212. XCTAssertEqual("key_1", it->key());
  213. it->Next();
  214. XCTAssertFalse(it->Valid());
  215. }
  216. - (void)testDeletingAheadOfAnIterator {
  217. // Write keys
  218. for (int i = 0; i < 4; ++i) {
  219. Status status = _db->Put(LevelDbTransaction::DefaultWriteOptions(), "key_" + std::to_string(i),
  220. "value_" + std::to_string(i));
  221. XCTAssertTrue(status.ok());
  222. }
  223. // Create a transaction, iterate to key_1, delete key_2. Verify we still iterate key_3.
  224. LevelDbTransaction transaction(_db.get(), "testDeletingAheadOfAnIterator");
  225. auto it = transaction.NewIterator();
  226. it->Seek("key_0");
  227. XCTAssertTrue(it->Valid());
  228. XCTAssertEqual("key_0", it->key());
  229. it->Next();
  230. XCTAssertTrue(it->Valid());
  231. XCTAssertEqual("key_1", it->key());
  232. transaction.Delete("key_2");
  233. it->Next();
  234. XCTAssertTrue(it->Valid());
  235. XCTAssertEqual("key_3", it->key());
  236. XCTAssertTrue(it->Valid());
  237. it->Next();
  238. XCTAssertFalse(it->Valid());
  239. }
  240. - (void)testToString {
  241. std::string key = LevelDbMutationKey::Key("user1", 42);
  242. FSTPBWriteBatch *message = [FSTPBWriteBatch message];
  243. message.batchId = 42;
  244. LevelDbTransaction transaction(_db.get(), "testToString");
  245. std::string description = transaction.ToString();
  246. XCTAssertEqual(description, "<LevelDbTransaction testToString: 0 changes (0 bytes):>");
  247. transaction.Put(key, message);
  248. description = transaction.ToString();
  249. XCTAssertEqual(description, "<LevelDbTransaction testToString: 1 changes (2 bytes):\n"
  250. " - Put [mutation: user_id=user1 batch_id=42] (2 bytes)>");
  251. std::string key2 = LevelDbMutationKey::Key("user1", 43);
  252. transaction.Delete(key2);
  253. description = transaction.ToString();
  254. XCTAssertEqual(description, "<LevelDbTransaction testToString: 2 changes (2 bytes):\n"
  255. " - Delete [mutation: user_id=user1 batch_id=43]\n"
  256. " - Put [mutation: user_id=user1 batch_id=42] (2 bytes)>");
  257. }
  258. @end
  259. NS_ASSUME_NONNULL_END