FSTLevelDBTransactionTests.mm 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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 =
  173. [[NSData alloc] initWithBytesNoCopy:(void *)value.data() length:value.size() freeWhenDone:NO];
  174. NSError *error;
  175. FSTPBTarget *parsed = [FSTPBTarget parseFromData:result error:&error];
  176. XCTAssertNil(error);
  177. XCTAssertTrue([target isEqual:parsed]);
  178. }
  179. - (void)testCanIterateAndDelete {
  180. LevelDbTransaction transaction(_db.get(), "testCanIterateAndDelete");
  181. for (int i = 0; i < 4; ++i) {
  182. transaction.Put("key_" + std::to_string(i), "value_" + std::to_string(i));
  183. }
  184. auto it = transaction.NewIterator();
  185. it->Seek("key_0");
  186. for (int i = 0; i < 4; ++i) {
  187. XCTAssertTrue(it->Valid());
  188. absl::string_view key = it->key();
  189. std::string expected = "key_" + std::to_string(i);
  190. XCTAssertEqual(expected, key);
  191. transaction.Delete(key);
  192. it->Next();
  193. }
  194. }
  195. - (void)testCanIterateFromDeletionToCommitted {
  196. // Write keys key_0 and key_1
  197. for (int i = 0; i < 2; ++i) {
  198. Status status = _db->Put(LevelDbTransaction::DefaultWriteOptions(), "key_" + std::to_string(i),
  199. "value_" + std::to_string(i));
  200. XCTAssertTrue(status.ok());
  201. }
  202. // Create a transaction, iterate, deleting key_0. Verify we still iterate key_1.
  203. LevelDbTransaction transaction(_db.get(), "testCanIterateFromDeletionToCommitted");
  204. auto it = transaction.NewIterator();
  205. it->Seek("key_0");
  206. XCTAssertTrue(it->Valid());
  207. XCTAssertEqual("key_0", it->key());
  208. transaction.Delete("key_0");
  209. it->Next();
  210. XCTAssertTrue(it->Valid());
  211. XCTAssertEqual("key_1", it->key());
  212. it->Next();
  213. XCTAssertFalse(it->Valid());
  214. }
  215. - (void)testDeletingAheadOfAnIterator {
  216. // Write keys
  217. for (int i = 0; i < 4; ++i) {
  218. Status status = _db->Put(LevelDbTransaction::DefaultWriteOptions(), "key_" + std::to_string(i),
  219. "value_" + std::to_string(i));
  220. XCTAssertTrue(status.ok());
  221. }
  222. // Create a transaction, iterate to key_1, delete key_2. Verify we still iterate key_3.
  223. LevelDbTransaction transaction(_db.get(), "testDeletingAheadOfAnIterator");
  224. auto it = transaction.NewIterator();
  225. it->Seek("key_0");
  226. XCTAssertTrue(it->Valid());
  227. XCTAssertEqual("key_0", it->key());
  228. it->Next();
  229. XCTAssertTrue(it->Valid());
  230. XCTAssertEqual("key_1", it->key());
  231. transaction.Delete("key_2");
  232. it->Next();
  233. XCTAssertTrue(it->Valid());
  234. XCTAssertEqual("key_3", it->key());
  235. XCTAssertTrue(it->Valid());
  236. it->Next();
  237. XCTAssertFalse(it->Valid());
  238. }
  239. - (void)testToString {
  240. std::string key = LevelDbMutationKey::Key("user1", 42);
  241. FSTPBWriteBatch *message = [FSTPBWriteBatch message];
  242. message.batchId = 42;
  243. LevelDbTransaction transaction(_db.get(), "testToString");
  244. std::string description = transaction.ToString();
  245. XCTAssertEqual(description, "<LevelDbTransaction testToString: 0 changes (0 bytes):>");
  246. transaction.Put(key, message);
  247. description = transaction.ToString();
  248. XCTAssertEqual(description,
  249. "<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,
  255. "<LevelDbTransaction testToString: 2 changes (2 bytes):\n"
  256. " - Delete [mutation: user_id=user1 batch_id=43]\n"
  257. " - Put [mutation: user_id=user1 batch_id=42] (2 bytes)>");
  258. }
  259. @end
  260. NS_ASSUME_NONNULL_END