FSTLevelDBTransactionTests.mm 10 KB

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