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