From 3f36f5467de4c191fa2903743e5a210420e9d49a Mon Sep 17 00:00:00 2001 From: Greg Soltis Date: Fri, 30 Mar 2018 10:18:25 -0700 Subject: Drop FSTWriteGroup (#986) * Drop write group from remote document change buffer * Unwind some group dependendencies in local store * Write group dropped from local store * Drop write group from mutation queue tests * Drop write group usage from query cache tests * Drop write groups from remote document cache tests * Drop write groups from remote document change buffer tests * Drop write groups and the write group tracker * Style * Put the action in transaction * Merge master, fix test * Fix some compiler warnings but mostly trigger travis * Responses to feedback --- .../Example/Firestore.xcodeproj/project.pbxproj | 4 - .../Tests/Local/FSTLevelDBMigrationsTests.mm | 8 +- .../Tests/Local/FSTLevelDBMutationQueueTests.mm | 5 +- .../Tests/Local/FSTLevelDBTransactionTests.mm | 24 +- .../Example/Tests/Local/FSTMutationQueueTests.mm | 690 ++++++++++----------- .../Example/Tests/Local/FSTQueryCacheTests.mm | 486 ++++++++------- .../Tests/Local/FSTRemoteDocumentCacheTests.mm | 101 ++- .../Local/FSTRemoteDocumentChangeBufferTests.mm | 57 +- .../Example/Tests/Local/FSTWriteGroupTests.mm | 101 --- 9 files changed, 660 insertions(+), 816 deletions(-) delete mode 100644 Firestore/Example/Tests/Local/FSTWriteGroupTests.mm (limited to 'Firestore/Example') diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index eb88210..7dbb45d 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -95,7 +95,6 @@ 5492E0AD2021552D00B64F25 /* FSTMemoryMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0972021552C00B64F25 /* FSTMemoryMutationQueueTests.mm */; }; 5492E0AE2021552D00B64F25 /* FSTLevelDBQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */; }; 5492E0AF2021552D00B64F25 /* FSTReferenceSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09A2021552C00B64F25 /* FSTReferenceSetTests.mm */; }; - 5492E0B02021552D00B64F25 /* FSTWriteGroupTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09B2021552C00B64F25 /* FSTWriteGroupTests.mm */; }; 5492E0B12021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */; }; 5492E0B92021555100B64F25 /* FSTDocumentKeyTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */; }; 5492E0BA2021555100B64F25 /* FSTDocumentSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */; }; @@ -311,7 +310,6 @@ 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBQueryCacheTests.mm; sourceTree = ""; }; 5492E0992021552C00B64F25 /* FSTPersistenceTestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTPersistenceTestHelpers.h; sourceTree = ""; }; 5492E09A2021552C00B64F25 /* FSTReferenceSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTReferenceSetTests.mm; sourceTree = ""; }; - 5492E09B2021552C00B64F25 /* FSTWriteGroupTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTWriteGroupTests.mm; sourceTree = ""; }; 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTRemoteDocumentCacheTests.mm; sourceTree = ""; }; 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDocumentKeyTests.mm; sourceTree = ""; }; 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDocumentSetTests.mm; sourceTree = ""; }; @@ -755,7 +753,6 @@ 5492E0852021552A00B64F25 /* FSTRemoteDocumentCacheTests.h */, 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */, 5492E0902021552B00B64F25 /* FSTRemoteDocumentChangeBufferTests.mm */, - 5492E09B2021552C00B64F25 /* FSTWriteGroupTests.mm */, 5492E0932021552B00B64F25 /* StringViewTests.mm */, 132E36BB104830BD806351AC /* FSTLevelDBTransactionTests.mm */, ); @@ -1477,7 +1474,6 @@ B686F2B22025000D0028D6BE /* resource_path_test.cc in Sources */, 5492E0CA2021557E00B64F25 /* FSTWatchChangeTests.mm in Sources */, 5492E063202154B900B64F25 /* FSTViewSnapshotTest.mm in Sources */, - 5492E0B02021552D00B64F25 /* FSTWriteGroupTests.mm in Sources */, 5492E058202154AB00B64F25 /* FSTAPIHelpers.mm in Sources */, AB380CFB2019388600D97691 /* target_id_generator_test.cc in Sources */, 5492E0A82021552D00B64F25 /* FSTLevelDBLocalStoreTests.mm in Sources */, diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm index ad892d8..14910ce 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm @@ -60,7 +60,7 @@ using leveldb::Status; - (void)testAddsTargetGlobal { FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; XCTAssertNil(metadata, @"Not expecting metadata yet, we should have an empty db"); - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testAddsTargetGlobal"); [FSTLevelDBMigrations runMigrationsWithTransaction:&transaction]; transaction.Commit(); metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; @@ -68,7 +68,7 @@ using leveldb::Status; } - (void)testSetsVersionNumber { - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testSetsVersionNumber"); FSTLevelDBSchemaVersion initial = [FSTLevelDBMigrations schemaVersionWithTransaction:&transaction]; XCTAssertEqual(0, initial, "No version should be equivalent to 0"); @@ -83,7 +83,7 @@ using leveldb::Status; NSUInteger expected = 50; { // Setup some targets to be counted in the migration. - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCountsQueries setup"); for (int i = 0; i < expected; i++) { std::string key = [FSTLevelDBTargetKey keyWithTargetID:i]; transaction.Put(key, "dummy"); @@ -100,7 +100,7 @@ using leveldb::Status; } { - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCountsQueries"); [FSTLevelDBMigrations runMigrationsWithTransaction:&transaction]; transaction.Commit(); FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm index cb03bf2..cd8176e 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm @@ -22,7 +22,6 @@ #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" #import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Source/Local/FSTLevelDBKey.h" -#import "Firestore/Source/Local/FSTWriteGroup.h" #import "Firestore/Example/Tests/Local/FSTMutationQueueTests.h" #import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" @@ -73,9 +72,7 @@ std::string MutationLikeKey(StringView table, StringView userID, FSTBatchID batc self.mutationQueue = [_db mutationQueueForUser:User("user")]; self.persistence = _db; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Start MutationQueue"]; - [self.mutationQueue start]; - [self.persistence commitGroup:group]; + self.persistence.run("Setup", [&]() { [self.mutationQueue start]; }); } - (void)testLoadNextBatchID_zeroWhenTotallyEmpty { diff --git a/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm index c959b4f..a32ce9f 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm @@ -58,7 +58,7 @@ using firebase::firestore::local::LevelDbTransaction; } - (void)testCreateTransaction { - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCreateTransaction"); std::string key = "key1"; transaction.Put(key, "value"); @@ -83,7 +83,7 @@ using firebase::firestore::local::LevelDbTransaction; status = _db->Put(writeOptions, committed_key2, committed_value2); XCTAssertTrue(status.ok()); - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCanReadCommittedAndMutations"); const std::string mutation_key1 = "m_key1"; const std::string mutation_value1 = "m_value1"; transaction.Put(mutation_key1, mutation_value1); @@ -113,7 +113,7 @@ using firebase::firestore::local::LevelDbTransaction; "value_" + std::to_string(i)); XCTAssertTrue(status.ok()); } - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testDeleteCommitted"); transaction.Put("key_1", "new_value"); std::string value; Status status = transaction.Get("key_1", &value); @@ -142,7 +142,7 @@ using firebase::firestore::local::LevelDbTransaction; XCTAssertTrue(status.ok()); } std::string value; - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testMutateDeleted"); transaction.Delete("key_1"); Status status = transaction.Get("key_1", &value); XCTAssertTrue(status.IsNotFound()); @@ -186,7 +186,7 @@ using firebase::firestore::local::LevelDbTransaction; } - (void)testProtobufSupport { - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testProtobufSupport"); FSTPBTarget *target = [FSTPBTarget message]; target.targetId = 1; @@ -206,7 +206,7 @@ using firebase::firestore::local::LevelDbTransaction; } - (void)testCanIterateAndDelete { - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCanIterateAndDelete"); for (int i = 0; i < 4; ++i) { transaction.Put("key_" + std::to_string(i), "value_" + std::to_string(i)); @@ -233,7 +233,7 @@ using firebase::firestore::local::LevelDbTransaction; } // Create a transaction, iterate, deleting key_0. Verify we still iterate key_1. - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testCanIterateFromDeletionToCommitted"); auto it = transaction.NewIterator(); it->Seek("key_0"); XCTAssertTrue(it->Valid()); @@ -255,7 +255,7 @@ using firebase::firestore::local::LevelDbTransaction; } // Create a transaction, iterate to key_1, delete key_2. Verify we still iterate key_3. - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testDeletingAheadOfAnIterator"); auto it = transaction.NewIterator(); it->Seek("key_0"); XCTAssertTrue(it->Valid()); @@ -277,21 +277,21 @@ using firebase::firestore::local::LevelDbTransaction; FSTPBWriteBatch *message = [FSTPBWriteBatch message]; message.batchId = 42; - LevelDbTransaction transaction(_db.get()); + LevelDbTransaction transaction(_db.get(), "testToString"); std::string description = transaction.ToString(); - XCTAssertEqual(description, ""); + XCTAssertEqual(description, ""); transaction.Put(key, message); description = transaction.ToString(); XCTAssertEqual(description, - ""); std::string key2 = LevelDbMutationKey::Key("user1", 43); transaction.Delete(key2); description = transaction.ToString(); XCTAssertEqual(description, - ""); } diff --git a/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm b/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm index 465a2cb..b228657 100644 --- a/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm +++ b/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm @@ -21,7 +21,6 @@ #import "Firestore/Source/Local/FSTEagerGarbageCollector.h" #import "Firestore/Source/Local/FSTMutationQueue.h" #import "Firestore/Source/Local/FSTPersistence.h" -#import "Firestore/Source/Local/FSTWriteGroup.h" #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Model/FSTMutationBatch.h" @@ -56,32 +55,24 @@ NS_ASSUME_NONNULL_BEGIN - (void)testCountBatches { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Count batches"]; - XCTAssertEqual(0, [self batchCount]); - XCTAssertTrue([self.mutationQueue isEmpty]); - [self.persistence commitGroup:group]; - - FSTMutationBatch *batch1 = [self addMutationBatch]; - group = [self.persistence startGroupWithAction:@"Count batches"]; - XCTAssertEqual(1, [self batchCount]); - XCTAssertFalse([self.mutationQueue isEmpty]); - [self.persistence commitGroup:group]; - - FSTMutationBatch *batch2 = [self addMutationBatch]; - group = [self.persistence startGroupWithAction:@"Count batches"]; - XCTAssertEqual(2, [self batchCount]); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batch2 ]]; - group = [self.persistence startGroupWithAction:@"Count batches"]; - XCTAssertEqual(1, [self batchCount]); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batch1 ]]; - group = [self.persistence startGroupWithAction:@"Count batches"]; - XCTAssertEqual(0, [self batchCount]); - XCTAssertTrue([self.mutationQueue isEmpty]); - [self.persistence commitGroup:group]; + self.persistence.run("testCountBatches", [&]() { + XCTAssertEqual(0, [self batchCount]); + XCTAssertTrue([self.mutationQueue isEmpty]); + + FSTMutationBatch *batch1 = [self addMutationBatch]; + XCTAssertEqual(1, [self batchCount]); + XCTAssertFalse([self.mutationQueue isEmpty]); + + FSTMutationBatch *batch2 = [self addMutationBatch]; + XCTAssertEqual(2, [self batchCount]); + + [self.mutationQueue removeMutationBatches:@[ batch2 ]]; + XCTAssertEqual(1, [self batchCount]); + + [self.mutationQueue removeMutationBatches:@[ batch1 ]]; + XCTAssertEqual(0, [self batchCount]); + XCTAssertTrue([self.mutationQueue isEmpty]); + }); } - (void)testAcknowledgeBatchID { @@ -91,334 +82,327 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); // Adding mutation batches should not change the highest acked batchID. - FSTMutationBatch *batch1 = [self addMutationBatch]; - FSTMutationBatch *batch2 = [self addMutationBatch]; - FSTMutationBatch *batch3 = [self addMutationBatch]; - XCTAssertGreaterThan(batch1.batchID, kFSTBatchIDUnknown); - XCTAssertGreaterThan(batch2.batchID, batch1.batchID); - XCTAssertGreaterThan(batch3.batchID, batch2.batchID); - - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); - - [self acknowledgeBatch:batch1]; - [self acknowledgeBatch:batch2]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); - - [self removeMutationBatches:@[ batch1 ]]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); - - [self removeMutationBatches:@[ batch2 ]]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); - - // Batch 3 never acknowledged. - [self removeMutationBatches:@[ batch3 ]]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + self.persistence.run("testAcknowledgeBatchID", [&]() { + FSTMutationBatch *batch1 = [self addMutationBatch]; + FSTMutationBatch *batch2 = [self addMutationBatch]; + FSTMutationBatch *batch3 = [self addMutationBatch]; + XCTAssertGreaterThan(batch1.batchID, kFSTBatchIDUnknown); + XCTAssertGreaterThan(batch2.batchID, batch1.batchID); + XCTAssertGreaterThan(batch3.batchID, batch2.batchID); + + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); + + [self.mutationQueue acknowledgeBatch:batch1 streamToken:nil]; + [self.mutationQueue acknowledgeBatch:batch2 streamToken:nil]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + + [self.mutationQueue removeMutationBatches:@[ batch1 ]]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + + [self.mutationQueue removeMutationBatches:@[ batch2 ]]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + + // Batch 3 never acknowledged. + [self.mutationQueue removeMutationBatches:@[ batch3 ]]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + }); } - (void)testAcknowledgeThenRemove { if ([self isTestBaseClass]) return; - FSTMutationBatch *batch1 = [self addMutationBatch]; + self.persistence.run("testAcknowledgeThenRemove", [&]() { + FSTMutationBatch *batch1 = [self addMutationBatch]; - FSTWriteGroup *group = [self.persistence startGroupWithAction:NSStringFromSelector(_cmd)]; - [self.mutationQueue acknowledgeBatch:batch1 streamToken:nil]; - [self.mutationQueue removeMutationBatches:@[ batch1 ]]; + [self.mutationQueue acknowledgeBatch:batch1 streamToken:nil]; + [self.mutationQueue removeMutationBatches:@[ batch1 ]]; - XCTAssertEqual([self batchCount], 0); - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch1.batchID); - [self.persistence commitGroup:group]; + XCTAssertEqual([self batchCount], 0); + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch1.batchID); + }); } - (void)testHighestAcknowledgedBatchIDNeverExceedsNextBatchID { if ([self isTestBaseClass]) return; - FSTMutationBatch *batch1 = [self addMutationBatch]; - FSTMutationBatch *batch2 = [self addMutationBatch]; - [self acknowledgeBatch:batch1]; - [self acknowledgeBatch:batch2]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + FSTMutationBatch *batch1 = + self.persistence.run("testHighestAcknowledgedBatchIDNeverExceedsNextBatchID batch1", + [&]() -> FSTMutationBatch * { return [self addMutationBatch]; }); + FSTMutationBatch *batch2 = + self.persistence.run("testHighestAcknowledgedBatchIDNeverExceedsNextBatchID batch2", + [&]() -> FSTMutationBatch * { return [self addMutationBatch]; }); - [self removeMutationBatches:@[ batch1, batch2 ]]; - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + self.persistence.run("testHighestAcknowledgedBatchIDNeverExceedsNextBatchID", [&]() { + [self.mutationQueue acknowledgeBatch:batch1 streamToken:nil]; + [self.mutationQueue acknowledgeBatch:batch2 streamToken:nil]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + + [self.mutationQueue removeMutationBatches:@[ batch1, batch2 ]]; + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], batch2.batchID); + }); // Restart the queue so that nextBatchID will be reset. - self.mutationQueue = [self.persistence mutationQueueForUser:User("user")]; + FSTMutationBatch *batch = self.persistence.run( + "testHighestAcknowledgedBatchIDNeverExceedsNextBatchID restart", [&]() -> FSTMutationBatch * { + self.mutationQueue = [self.persistence mutationQueueForUser:User("user")]; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Start MutationQueue"]; - [self.mutationQueue start]; - [self.persistence commitGroup:group]; + [self.mutationQueue start]; - // Verify that on restart with an empty queue, nextBatchID falls to a lower value. - XCTAssertLessThan(self.mutationQueue.nextBatchID, batch2.batchID); + // Verify that on restart with an empty queue, nextBatchID falls to a lower value. + XCTAssertLessThan(self.mutationQueue.nextBatchID, batch2.batchID); - // As a result highestAcknowledgedBatchID must also reset lower. - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); + // As a result highestAcknowledgedBatchID must also reset lower. + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); - // The mutation queue will reset the next batchID after all mutations are removed so adding - // another mutation will cause a collision. - FSTMutationBatch *newBatch = [self addMutationBatch]; - XCTAssertEqual(newBatch.batchID, batch1.batchID); + // The mutation queue will reset the next batchID after all mutations are removed so adding + // another mutation will cause a collision. + FSTMutationBatch *newBatch = [self addMutationBatch]; + XCTAssertEqual(newBatch.batchID, batch1.batchID); + return newBatch; + }); + self.persistence.run("testHighestAcknowledgedBatchIDNeverExceedsNextBatchID restart2", [&]() { + // Restart the queue with one unacknowledged batch in it. + [self.mutationQueue start]; - // Restart the queue with one unacknowledged batch in it. - self.persistence.run([&]() { [self.mutationQueue start]; }); + XCTAssertEqual([self.mutationQueue nextBatchID], batch.batchID + 1); - XCTAssertEqual([self.mutationQueue nextBatchID], newBatch.batchID + 1); - - // highestAcknowledgedBatchID must still be kFSTBatchIDUnknown. - XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); + // highestAcknowledgedBatchID must still be kFSTBatchIDUnknown. + XCTAssertEqual([self.mutationQueue highestAcknowledgedBatchID], kFSTBatchIDUnknown); + }); } - (void)testLookupMutationBatch { if ([self isTestBaseClass]) return; // Searching on an empty queue should not find a non-existent batch - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"LookupMutationBatch"]; - FSTMutationBatch *notFound = [self.mutationQueue lookupMutationBatch:42]; - [self.persistence commitGroup:group]; - XCTAssertNil(notFound); - - NSMutableArray *batches = [self createBatches:10]; - NSArray *removed = [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; - - // After removing, a batch should not be found - group = [self.persistence startGroupWithAction:@"LookupMutationBatch"]; - for (NSUInteger i = 0; i < removed.count; i++) { - notFound = [self.mutationQueue lookupMutationBatch:removed[i].batchID]; + self.persistence.run("testLookupMutationBatch", [&]() { + FSTMutationBatch *notFound = [self.mutationQueue lookupMutationBatch:42]; XCTAssertNil(notFound); - } - // Remaining entries should still be found - for (FSTMutationBatch *batch in batches) { - FSTMutationBatch *found = [self.mutationQueue lookupMutationBatch:batch.batchID]; - XCTAssertEqual(found.batchID, batch.batchID); - } + NSMutableArray *batches = [self createBatches:10]; + NSArray *removed = [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; + + // After removing, a batch should not be found + for (NSUInteger i = 0; i < removed.count; i++) { + notFound = [self.mutationQueue lookupMutationBatch:removed[i].batchID]; + XCTAssertNil(notFound); + } - // Even on a nonempty queue searching should not find a non-existent batch - notFound = [self.mutationQueue lookupMutationBatch:42]; - XCTAssertNil(notFound); - [self.persistence commitGroup:group]; + // Remaining entries should still be found + for (FSTMutationBatch *batch in batches) { + FSTMutationBatch *found = [self.mutationQueue lookupMutationBatch:batch.batchID]; + XCTAssertEqual(found.batchID, batch.batchID); + } + + // Even on a nonempty queue searching should not find a non-existent batch + notFound = [self.mutationQueue lookupMutationBatch:42]; + XCTAssertNil(notFound); + }); } - (void)testNextMutationBatchAfterBatchID { if ([self isTestBaseClass]) return; - NSMutableArray *batches = [self createBatches:10]; - - // This is an array of successors assuming the removals below will happen: - NSArray *afters = @[ batches[3], batches[8], batches[8] ]; - NSArray *removed = [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; - - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"NextMutationBatchAfterBatchID"]; - for (NSUInteger i = 0; i < batches.count - 1; i++) { - FSTMutationBatch *current = batches[i]; - FSTMutationBatch *next = batches[i + 1]; - FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:current.batchID]; - XCTAssertEqual(found.batchID, next.batchID); - } - - for (NSUInteger i = 0; i < removed.count; i++) { - FSTMutationBatch *current = removed[i]; - FSTMutationBatch *next = afters[i]; - FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:current.batchID]; - XCTAssertEqual(found.batchID, next.batchID); - } - - FSTMutationBatch *first = batches[0]; - FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:first.batchID - 42]; - XCTAssertEqual(found.batchID, first.batchID); - - FSTMutationBatch *last = batches[batches.count - 1]; - FSTMutationBatch *notFound = [self.mutationQueue nextMutationBatchAfterBatchID:last.batchID]; - XCTAssertNil(notFound); - [self.persistence commitGroup:group]; + self.persistence.run("testNextMutationBatchAfterBatchID", [&]() { + NSMutableArray *batches = [self createBatches:10]; + + // This is an array of successors assuming the removals below will happen: + NSArray *afters = @[ batches[3], batches[8], batches[8] ]; + NSArray *removed = [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; + + for (NSUInteger i = 0; i < batches.count - 1; i++) { + FSTMutationBatch *current = batches[i]; + FSTMutationBatch *next = batches[i + 1]; + FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:current.batchID]; + XCTAssertEqual(found.batchID, next.batchID); + } + + for (NSUInteger i = 0; i < removed.count; i++) { + FSTMutationBatch *current = removed[i]; + FSTMutationBatch *next = afters[i]; + FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:current.batchID]; + XCTAssertEqual(found.batchID, next.batchID); + } + + FSTMutationBatch *first = batches[0]; + FSTMutationBatch *found = [self.mutationQueue nextMutationBatchAfterBatchID:first.batchID - 42]; + XCTAssertEqual(found.batchID, first.batchID); + + FSTMutationBatch *last = batches[batches.count - 1]; + FSTMutationBatch *notFound = [self.mutationQueue nextMutationBatchAfterBatchID:last.batchID]; + XCTAssertNil(notFound); + }); } - (void)testNextMutationBatchAfterBatchIDSkipsAcknowledgedBatches { if ([self isTestBaseClass]) return; - NSMutableArray *batches = [self createBatches:3]; - FSTWriteGroup *group = [self.persistence - startGroupWithAction:@"NextMutationBatchAfterBatchIDSkipsAcknowledgedBatches"]; - XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:kFSTBatchIDUnknown], - batches[0]); - [self.persistence commitGroup:group]; - - [self acknowledgeBatch:batches[0]]; - group = [self.persistence - startGroupWithAction:@"NextMutationBatchAfterBatchIDSkipsAcknowledgedBatches"]; - XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:kFSTBatchIDUnknown], - batches[1]); - XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:batches[0].batchID], - batches[1]); - XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:batches[1].batchID], - batches[2]); - [self.persistence commitGroup:group]; + NSMutableArray *batches = self.persistence.run( + "testNextMutationBatchAfterBatchIDSkipsAcknowledgedBatches newBatches", + [&]() -> NSMutableArray * { + NSMutableArray *newBatches = [self createBatches:3]; + XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:kFSTBatchIDUnknown], + newBatches[0]); + return newBatches; + }); + self.persistence.run("testNextMutationBatchAfterBatchIDSkipsAcknowledgedBatches", [&]() { + + [self.mutationQueue acknowledgeBatch:batches[0] streamToken:nil]; + XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:kFSTBatchIDUnknown], + batches[1]); + XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:batches[0].batchID], + batches[1]); + XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:batches[1].batchID], + batches[2]); + }); } - (void)testAllMutationBatchesThroughBatchID { if ([self isTestBaseClass]) return; - NSMutableArray *batches = [self createBatches:10]; - [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; + self.persistence.run("testAllMutationBatchesThroughBatchID", [&]() { + NSMutableArray *batches = [self createBatches:10]; + [self makeHoles:@[ @2, @6, @7 ] inBatches:batches]; - NSArray *found, *expected; + NSArray *found, *expected; - FSTWriteGroup *group = - [self.persistence startGroupWithAction:@"AllMutationBatchesThroughBatchID"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:batches[0].batchID - 1]; - XCTAssertEqualObjects(found, (@[])); + found = [self.mutationQueue allMutationBatchesThroughBatchID:batches[0].batchID - 1]; + XCTAssertEqualObjects(found, (@[])); - for (NSUInteger i = 0; i < batches.count; i++) { - found = [self.mutationQueue allMutationBatchesThroughBatchID:batches[i].batchID]; - expected = [batches subarrayWithRange:NSMakeRange(0, i + 1)]; - XCTAssertEqualObjects(found, expected, @"for index %lu", (unsigned long)i); - } - [self.persistence commitGroup:group]; + for (NSUInteger i = 0; i < batches.count; i++) { + found = [self.mutationQueue allMutationBatchesThroughBatchID:batches[i].batchID]; + expected = [batches subarrayWithRange:NSMakeRange(0, i + 1)]; + XCTAssertEqualObjects(found, expected, @"for index %lu", (unsigned long)i); + } + }); } - (void)testAllMutationBatchesAffectingDocumentKey { if ([self isTestBaseClass]) return; - NSArray *mutations = @[ - FSTTestSetMutation(@"fob/bar", - @{ @"a" : @1 }), - FSTTestSetMutation(@"foo/bar", - @{ @"a" : @1 }), - FSTTestPatchMutation("foo/bar", - @{ @"b" : @1 }, {}), - FSTTestSetMutation(@"foo/bar/suffix/key", - @{ @"a" : @1 }), - FSTTestSetMutation(@"foo/baz", - @{ @"a" : @1 }), - FSTTestSetMutation(@"food/bar", - @{ @"a" : @1 }) - ]; - - // Store all the mutations. - NSMutableArray *batches = [NSMutableArray array]; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"New mutation batch"]; - for (FSTMutation *mutation in mutations) { - FSTMutationBatch *batch = - [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] - mutations:@[ mutation ]]; - [batches addObject:batch]; - } - - NSArray *expected = @[ batches[1], batches[2] ]; - NSArray *matches = - [self.mutationQueue allMutationBatchesAffectingDocumentKey:testutil::Key("foo/bar")]; - - [self.persistence commitGroup:group]; - XCTAssertEqualObjects(matches, expected); + self.persistence.run("testAllMutationBatchesAffectingDocumentKey", [&]() { + NSArray *mutations = @[ + FSTTestSetMutation(@"fob/bar", + @{ @"a" : @1 }), + FSTTestSetMutation(@"foo/bar", + @{ @"a" : @1 }), + FSTTestPatchMutation("foo/bar", + @{ @"b" : @1 }, {}), + FSTTestSetMutation(@"foo/bar/suffix/key", + @{ @"a" : @1 }), + FSTTestSetMutation(@"foo/baz", + @{ @"a" : @1 }), + FSTTestSetMutation(@"food/bar", + @{ @"a" : @1 }) + ]; + + // Store all the mutations. + NSMutableArray *batches = [NSMutableArray array]; + for (FSTMutation *mutation in mutations) { + FSTMutationBatch *batch = + [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] + mutations:@[ mutation ]]; + [batches addObject:batch]; + } + + NSArray *expected = @[ batches[1], batches[2] ]; + NSArray *matches = + [self.mutationQueue allMutationBatchesAffectingDocumentKey:testutil::Key("foo/bar")]; + + XCTAssertEqualObjects(matches, expected); + }); } - (void)testAllMutationBatchesAffectingQuery { if ([self isTestBaseClass]) return; - NSArray *mutations = @[ - FSTTestSetMutation(@"fob/bar", - @{ @"a" : @1 }), - FSTTestSetMutation(@"foo/bar", - @{ @"a" : @1 }), - FSTTestPatchMutation("foo/bar", - @{ @"b" : @1 }, {}), - FSTTestSetMutation(@"foo/bar/suffix/key", - @{ @"a" : @1 }), - FSTTestSetMutation(@"foo/baz", - @{ @"a" : @1 }), - FSTTestSetMutation(@"food/bar", - @{ @"a" : @1 }) - ]; - - // Store all the mutations. - NSMutableArray *batches = [NSMutableArray array]; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"New mutation batch"]; - for (FSTMutation *mutation in mutations) { - FSTMutationBatch *batch = - [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] - mutations:@[ mutation ]]; - [batches addObject:batch]; - } - - NSArray *expected = @[ batches[1], batches[2], batches[4] ]; - FSTQuery *query = FSTTestQuery("foo"); - NSArray *matches = - [self.mutationQueue allMutationBatchesAffectingQuery:query]; - [self.persistence commitGroup:group]; - - XCTAssertEqualObjects(matches, expected); + self.persistence.run("testAllMutationBatchesAffectingQuery", [&]() { + NSArray *mutations = @[ + FSTTestSetMutation(@"fob/bar", + @{ @"a" : @1 }), + FSTTestSetMutation(@"foo/bar", + @{ @"a" : @1 }), + FSTTestPatchMutation("foo/bar", + @{ @"b" : @1 }, {}), + FSTTestSetMutation(@"foo/bar/suffix/key", + @{ @"a" : @1 }), + FSTTestSetMutation(@"foo/baz", + @{ @"a" : @1 }), + FSTTestSetMutation(@"food/bar", + @{ @"a" : @1 }) + ]; + + // Store all the mutations. + NSMutableArray *batches = [NSMutableArray array]; + for (FSTMutation *mutation in mutations) { + FSTMutationBatch *batch = + [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] + mutations:@[ mutation ]]; + [batches addObject:batch]; + } + + NSArray *expected = @[ batches[1], batches[2], batches[4] ]; + FSTQuery *query = FSTTestQuery("foo"); + NSArray *matches = + [self.mutationQueue allMutationBatchesAffectingQuery:query]; + + XCTAssertEqualObjects(matches, expected); + }); } - (void)testRemoveMutationBatches { if ([self isTestBaseClass]) return; - NSMutableArray *batches = [self createBatches:10]; - FSTMutationBatch *last = batches[batches.count - 1]; - - [self removeMutationBatches:@[ batches[0] ]]; - [batches removeObjectAtIndex:0]; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - XCTAssertEqual([self batchCount], 9); - [self.persistence commitGroup:group]; - - NSArray *found; - - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; - XCTAssertEqualObjects(found, batches); - XCTAssertEqual(found.count, 9); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[0], batches[1], batches[2] ]]; - [batches removeObjectsInRange:NSMakeRange(0, 3)]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - XCTAssertEqual([self batchCount], 6); - [self.persistence commitGroup:group]; - - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; - XCTAssertEqualObjects(found, batches); - XCTAssertEqual(found.count, 6); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[batches.count - 1] ]]; - [batches removeObjectAtIndex:batches.count - 1]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - XCTAssertEqual([self batchCount], 5); - [self.persistence commitGroup:group]; - - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; - XCTAssertEqualObjects(found, batches); - XCTAssertEqual(found.count, 5); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[3] ]]; - [batches removeObjectAtIndex:3]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - XCTAssertEqual([self batchCount], 4); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[1] ]]; - [batches removeObjectAtIndex:1]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - XCTAssertEqual([self batchCount], 3); - [self.persistence commitGroup:group]; - - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; - XCTAssertEqualObjects(found, batches); - XCTAssertEqual(found.count, 3); - XCTAssertFalse([self.mutationQueue isEmpty]); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:batches]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatches"]; - found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; - XCTAssertEqualObjects(found, @[]); - XCTAssertEqual(found.count, 0); - XCTAssertTrue([self.mutationQueue isEmpty]); - [self.persistence commitGroup:group]; + self.persistence.run("testRemoveMutationBatches", [&]() { + NSMutableArray *batches = [self createBatches:10]; + + [self.mutationQueue removeMutationBatches:@[ batches[0] ]]; + [batches removeObjectAtIndex:0]; + + FSTMutationBatch *last = batches[batches.count - 1]; + XCTAssertEqual([self batchCount], 9); + + NSArray *found; + + found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; + XCTAssertEqualObjects(found, batches); + XCTAssertEqual(found.count, 9); + + [self.mutationQueue removeMutationBatches:@[ batches[0], batches[1], batches[2] ]]; + [batches removeObjectsInRange:NSMakeRange(0, 3)]; + XCTAssertEqual([self batchCount], 6); + + found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; + XCTAssertEqualObjects(found, batches); + XCTAssertEqual(found.count, 6); + + [self.mutationQueue removeMutationBatches:@[ batches[batches.count - 1] ]]; + [batches removeObjectAtIndex:batches.count - 1]; + XCTAssertEqual([self batchCount], 5); + + found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; + XCTAssertEqualObjects(found, batches); + XCTAssertEqual(found.count, 5); + + [self.mutationQueue removeMutationBatches:@[ batches[3] ]]; + [batches removeObjectAtIndex:3]; + XCTAssertEqual([self batchCount], 4); + + [self.mutationQueue removeMutationBatches:@[ batches[1] ]]; + [batches removeObjectAtIndex:1]; + XCTAssertEqual([self batchCount], 3); + + found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; + XCTAssertEqualObjects(found, batches); + XCTAssertEqual(found.count, 3); + XCTAssertFalse([self.mutationQueue isEmpty]); + + [self.mutationQueue removeMutationBatches:batches]; + found = [self.mutationQueue allMutationBatchesThroughBatchID:last.batchID]; + XCTAssertEqualObjects(found, @[]); + XCTAssertEqual(found.count, 0); + XCTAssertTrue([self.mutationQueue isEmpty]); + }); } - (void)testRemoveMutationBatchesEmitsGarbageEvents { @@ -428,52 +412,41 @@ NS_ASSUME_NONNULL_BEGIN [garbageCollector addGarbageSource:self.mutationQueue]; NSMutableArray *batches = [NSMutableArray array]; - [batches addObjectsFromArray:@[ - [self addMutationBatchWithKey:@"foo/bar"], - [self addMutationBatchWithKey:@"foo/ba"], - [self addMutationBatchWithKey:@"foo/bar2"], - [self addMutationBatchWithKey:@"foo/bar"], - [self addMutationBatchWithKey:@"foo/bar/suffix/baz"], - [self addMutationBatchWithKey:@"bar/baz"], - ]]; - - [self removeMutationBatches:@[ batches[0] ]]; - FSTWriteGroup *group = - [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - std::set garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, std::set({})); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[1] ]]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, std::set({testutil::Key("foo/ba")})); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[5] ]]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, std::set({testutil::Key("bar/baz")})); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[2], batches[3] ]]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, - std::set({testutil::Key("foo/bar"), testutil::Key("foo/bar2")})); - [self.persistence commitGroup:group]; - - [batches addObject:[self addMutationBatchWithKey:@"foo/bar/suffix/baz"]]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, std::set({})); - [self.persistence commitGroup:group]; - - [self removeMutationBatches:@[ batches[4], batches[6] ]]; - group = [self.persistence startGroupWithAction:@"RemoveMutationBatchesEmitsGarbageEvents"]; - garbage = [garbageCollector collectGarbage]; - XCTAssertEqual(garbage, std::set({testutil::Key("foo/bar/suffix/baz")})); - [self.persistence commitGroup:group]; + self.persistence.run("testRemoveMutationBatchesEmitsGarbageEvents", [&]() { + [batches addObjectsFromArray:@[ + [self addMutationBatchWithKey:@"foo/bar"], + [self addMutationBatchWithKey:@"foo/ba"], + [self addMutationBatchWithKey:@"foo/bar2"], + [self addMutationBatchWithKey:@"foo/bar"], + [self addMutationBatchWithKey:@"foo/bar/suffix/baz"], + [self addMutationBatchWithKey:@"bar/baz"], + ]]; + + [self.mutationQueue removeMutationBatches:@[ batches[0] ]]; + std::set garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, std::set({})); + + [self.mutationQueue removeMutationBatches:@[ batches[1] ]]; + garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, std::set({testutil::Key("foo/ba")})); + + [self.mutationQueue removeMutationBatches:@[ batches[5] ]]; + garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, std::set({testutil::Key("bar/baz")})); + + [self.mutationQueue removeMutationBatches:@[ batches[2], batches[3] ]]; + garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, + std::set({testutil::Key("foo/bar"), testutil::Key("foo/bar2")})); + + [batches addObject:[self addMutationBatchWithKey:@"foo/bar/suffix/baz"]]; + garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, std::set({})); + + [self.mutationQueue removeMutationBatches:@[ batches[4], batches[6] ]]; + garbage = [garbageCollector collectGarbage]; + XCTAssertEqual(garbage, std::set({testutil::Key("foo/bar/suffix/baz")})); + }); } - (void)testStreamToken { @@ -482,20 +455,22 @@ NS_ASSUME_NONNULL_BEGIN NSData *streamToken1 = [@"token1" dataUsingEncoding:NSUTF8StringEncoding]; NSData *streamToken2 = [@"token2" dataUsingEncoding:NSUTF8StringEncoding]; - self.persistence.run([&]() { [self.mutationQueue setLastStreamToken:streamToken1]; }); - - FSTMutationBatch *batch1 = [self addMutationBatch]; - [self addMutationBatch]; + self.persistence.run("testStreamToken", [&]() { + [self.mutationQueue setLastStreamToken:streamToken1]; - XCTAssertEqualObjects([self.mutationQueue lastStreamToken], streamToken1); + FSTMutationBatch *batch1 = [self addMutationBatch]; + [self addMutationBatch]; - self.persistence.run( - [&]() { [self.mutationQueue acknowledgeBatch:batch1 streamToken:streamToken2]; }); + XCTAssertEqualObjects([self.mutationQueue lastStreamToken], streamToken1); - XCTAssertEqual(self.mutationQueue.highestAcknowledgedBatchID, batch1.batchID); - XCTAssertEqualObjects([self.mutationQueue lastStreamToken], streamToken2); + [self.mutationQueue acknowledgeBatch:batch1 streamToken:streamToken2]; + XCTAssertEqual(self.mutationQueue.highestAcknowledgedBatchID, batch1.batchID); + XCTAssertEqualObjects([self.mutationQueue lastStreamToken], streamToken2); + }); } +#pragma mark - Helpers + /** Creates a new FSTMutationBatch with the next batch ID and a set of dummy mutations. */ - (FSTMutationBatch *)addMutationBatch { return [self addMutationBatchWithKey:@"foo/bar"]; @@ -508,11 +483,9 @@ NS_ASSUME_NONNULL_BEGIN - (FSTMutationBatch *)addMutationBatchWithKey:(NSString *)key { FSTSetMutation *mutation = FSTTestSetMutation(key, @{ @"a" : @1 }); - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"New mutation batch"]; FSTMutationBatch *batch = [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] mutations:@[ mutation ]]; - [self.persistence commitGroup:group]; return batch; } @@ -531,23 +504,6 @@ NS_ASSUME_NONNULL_BEGIN return batches; } -/** - * Calls -acknowledgeBatch:streamToken:group: on the mutation queue in a new group and commits the - * the group. - */ -// TODO(gsoltis): delete this helper, just run it in a transaction directly -- (void)acknowledgeBatch:(FSTMutationBatch *)batch { - self.persistence.run([&]() { [self.mutationQueue acknowledgeBatch:batch streamToken:nil]; }); -} - -/** - * Calls -removeMutationBatches:group: on the mutation queue in a new group and commits the group. - */ -// TODO(gsoltis): delete this helper, just run it in a transaction directly -- (void)removeMutationBatches:(NSArray *)batches { - self.persistence.run([&]() { [self.mutationQueue removeMutationBatches:batches]; }); -} - /** Returns the number of mutation batches in the mutation queue. */ - (NSUInteger)batchCount { return [self.mutationQueue allMutationBatches].count; @@ -567,7 +523,7 @@ NS_ASSUME_NONNULL_BEGIN for (NSUInteger i = 0; i < holes.count; i++) { NSUInteger index = holes[i].unsignedIntegerValue - i; FSTMutationBatch *batch = batches[index]; - [self removeMutationBatches:@[ batch ]]; + [self.mutationQueue removeMutationBatches:@[ batch ]]; [batches removeObjectAtIndex:index]; [removed addObject:batch]; diff --git a/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm b/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm index b575004..956afa9 100644 --- a/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm +++ b/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm @@ -21,7 +21,6 @@ #import "Firestore/Source/Local/FSTEagerGarbageCollector.h" #import "Firestore/Source/Local/FSTPersistence.h" #import "Firestore/Source/Local/FSTQueryData.h" -#import "Firestore/Source/Local/FSTWriteGroup.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" #import "Firestore/third_party/Immutable/Tests/FSTImmutableSortedSet+Testing.h" @@ -61,339 +60,344 @@ NS_ASSUME_NONNULL_BEGIN - (void)testReadQueryNotInCache { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"ReadQueryNotInCache"]; - XCTAssertNil([self.queryCache queryDataForQuery:_queryRooms]); - [self.persistence commitGroup:group]; + self.persistence.run("testReadQueryNotInCache", + [&]() { XCTAssertNil([self.queryCache queryDataForQuery:_queryRooms]); }); } - (void)testSetAndReadAQuery { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"SetAndReadQuery"]; - FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms]; - [self.queryCache addQueryData:queryData]; + self.persistence.run("testSetAndReadAQuery", [&]() { + FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms]; + [self.queryCache addQueryData:queryData]; - FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; - XCTAssertEqualObjects(result.query, queryData.query); - XCTAssertEqual(result.targetID, queryData.targetID); - XCTAssertEqualObjects(result.resumeToken, queryData.resumeToken); - [self.persistence commitGroup:group]; + FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; + XCTAssertEqualObjects(result.query, queryData.query); + XCTAssertEqual(result.targetID, queryData.targetID); + XCTAssertEqualObjects(result.resumeToken, queryData.resumeToken); + }); } - (void)testCanonicalIDCollision { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"CanonicalIDCollision"]; - // Type information is currently lost in our canonicalID implementations so this currently an - // easy way to force colliding canonicalIDs - FSTQuery *q1 = [FSTTestQuery("a") queryByAddingFilter:FSTTestFilter("foo", @"==", @(1))]; - FSTQuery *q2 = [FSTTestQuery("a") queryByAddingFilter:FSTTestFilter("foo", @"==", @"1")]; - XCTAssertEqualObjects(q1.canonicalID, q2.canonicalID); - - FSTQueryData *data1 = [self queryDataWithQuery:q1]; - [self.queryCache addQueryData:data1]; - - // Using the other query should not return the query cache entry despite equal canonicalIDs. - XCTAssertNil([self.queryCache queryDataForQuery:q2]); - XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1); - - FSTQueryData *data2 = [self queryDataWithQuery:q2]; - [self.queryCache addQueryData:data2]; - XCTAssertEqual([self.queryCache count], 2); - - XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1); - XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2); - - [self.queryCache removeQueryData:data1]; - XCTAssertNil([self.queryCache queryDataForQuery:q1]); - XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2); - XCTAssertEqual([self.queryCache count], 1); - - [self.queryCache removeQueryData:data2]; - XCTAssertNil([self.queryCache queryDataForQuery:q1]); - XCTAssertNil([self.queryCache queryDataForQuery:q2]); - XCTAssertEqual([self.queryCache count], 0); - [self.persistence commitGroup:group]; + self.persistence.run("testCanonicalIDCollision", [&]() { + // Type information is currently lost in our canonicalID implementations so this currently an + // easy way to force colliding canonicalIDs + FSTQuery *q1 = [FSTTestQuery("a") queryByAddingFilter:FSTTestFilter("foo", @"==", @(1))]; + FSTQuery *q2 = [FSTTestQuery("a") queryByAddingFilter:FSTTestFilter("foo", @"==", @"1")]; + XCTAssertEqualObjects(q1.canonicalID, q2.canonicalID); + + FSTQueryData *data1 = [self queryDataWithQuery:q1]; + [self.queryCache addQueryData:data1]; + + // Using the other query should not return the query cache entry despite equal canonicalIDs. + XCTAssertNil([self.queryCache queryDataForQuery:q2]); + XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1); + + FSTQueryData *data2 = [self queryDataWithQuery:q2]; + [self.queryCache addQueryData:data2]; + XCTAssertEqual([self.queryCache count], 2); + + XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1); + XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2); + + [self.queryCache removeQueryData:data1]; + XCTAssertNil([self.queryCache queryDataForQuery:q1]); + XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2); + XCTAssertEqual([self.queryCache count], 1); + + [self.queryCache removeQueryData:data2]; + XCTAssertNil([self.queryCache queryDataForQuery:q1]); + XCTAssertNil([self.queryCache queryDataForQuery:q2]); + XCTAssertEqual([self.queryCache count], 0); + }); } - (void)testSetQueryToNewValue { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"SetQueryToNewValue"]; - FSTQueryData *queryData1 = - [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:1]; - [self.queryCache addQueryData:queryData1]; - - FSTQueryData *queryData2 = - [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:2]; - [self.queryCache addQueryData:queryData2]; - - FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; - XCTAssertNotEqualObjects(queryData2.resumeToken, queryData1.resumeToken); - XCTAssertNotEqualObjects(queryData2.snapshotVersion, queryData1.snapshotVersion); - XCTAssertEqualObjects(result.resumeToken, queryData2.resumeToken); - XCTAssertEqualObjects(result.snapshotVersion, queryData2.snapshotVersion); - [self.persistence commitGroup:group]; + self.persistence.run("testSetQueryToNewValue", [&]() { + FSTQueryData *queryData1 = + [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:1]; + [self.queryCache addQueryData:queryData1]; + + FSTQueryData *queryData2 = + [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:2]; + [self.queryCache addQueryData:queryData2]; + + FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; + XCTAssertNotEqualObjects(queryData2.resumeToken, queryData1.resumeToken); + XCTAssertNotEqualObjects(queryData2.snapshotVersion, queryData1.snapshotVersion); + XCTAssertEqualObjects(result.resumeToken, queryData2.resumeToken); + XCTAssertEqualObjects(result.snapshotVersion, queryData2.snapshotVersion); + }); } - (void)testRemoveQuery { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveQuery"]; - FSTQueryData *queryData1 = [self queryDataWithQuery:_queryRooms]; - [self.queryCache addQueryData:queryData1]; + self.persistence.run("testRemoveQuery", [&]() { + FSTQueryData *queryData1 = [self queryDataWithQuery:_queryRooms]; + [self.queryCache addQueryData:queryData1]; - [self.queryCache removeQueryData:queryData1]; + [self.queryCache removeQueryData:queryData1]; - FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; - XCTAssertNil(result); - [self.persistence commitGroup:group]; + FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; + XCTAssertNil(result); + }); } - (void)testRemoveNonExistentQuery { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveNonExistentQuery"]; - FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms]; + self.persistence.run("testRemoveNonExistentQuery", [&]() { + FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms]; - // no-op, but make sure it doesn't throw. - XCTAssertNoThrow([self.queryCache removeQueryData:queryData]); - [self.persistence commitGroup:group]; + // no-op, but make sure it doesn't throw. + XCTAssertNoThrow([self.queryCache removeQueryData:queryData]); + }); } - (void)testRemoveQueryRemovesMatchingKeysToo { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = - [self.persistence startGroupWithAction:@"RemoveQueryRemovesMatchingKeysToo"]; - FSTQueryData *rooms = [self queryDataWithQuery:_queryRooms]; - [self.queryCache addQueryData:rooms]; + self.persistence.run("testRemoveQueryRemovesMatchingKeysToo", [&]() { + FSTQueryData *rooms = [self queryDataWithQuery:_queryRooms]; + [self.queryCache addQueryData:rooms]; - DocumentKey key1 = testutil::Key("rooms/foo"); - DocumentKey key2 = testutil::Key("rooms/bar"); - [self addMatchingKey:key1 forTargetID:rooms.targetID]; - [self addMatchingKey:key2 forTargetID:rooms.targetID]; + DocumentKey key1 = testutil::Key("rooms/foo"); + DocumentKey key2 = testutil::Key("rooms/bar"); + [self addMatchingKey:key1 forTargetID:rooms.targetID]; + [self addMatchingKey:key2 forTargetID:rooms.targetID]; - XCTAssertTrue([self.queryCache containsKey:key1]); - XCTAssertTrue([self.queryCache containsKey:key2]); + XCTAssertTrue([self.queryCache containsKey:key1]); + XCTAssertTrue([self.queryCache containsKey:key2]); - [self.queryCache removeQueryData:rooms]; - XCTAssertFalse([self.queryCache containsKey:key1]); - XCTAssertFalse([self.queryCache containsKey:key2]); - [self.persistence commitGroup:group]; + [self.queryCache removeQueryData:rooms]; + XCTAssertFalse([self.queryCache containsKey:key1]); + XCTAssertFalse([self.queryCache containsKey:key2]); + }); } - (void)testAddOrRemoveMatchingKeys { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"AddOrRemoveMatchingKeys"]; - DocumentKey key = testutil::Key("foo/bar"); + self.persistence.run("testAddOrRemoveMatchingKeys", [&]() { + DocumentKey key = testutil::Key("foo/bar"); - XCTAssertFalse([self.queryCache containsKey:key]); + XCTAssertFalse([self.queryCache containsKey:key]); - [self addMatchingKey:key forTargetID:1]; - XCTAssertTrue([self.queryCache containsKey:key]); + [self addMatchingKey:key forTargetID:1]; + XCTAssertTrue([self.queryCache containsKey:key]); - [self addMatchingKey:key forTargetID:2]; - XCTAssertTrue([self.queryCache containsKey:key]); + [self addMatchingKey:key forTargetID:2]; + XCTAssertTrue([self.queryCache containsKey:key]); - [self removeMatchingKey:key forTargetID:1]; - XCTAssertTrue([self.queryCache containsKey:key]); + [self removeMatchingKey:key forTargetID:1]; + XCTAssertTrue([self.queryCache containsKey:key]); - [self removeMatchingKey:key forTargetID:2]; - XCTAssertFalse([self.queryCache containsKey:key]); - [self.persistence commitGroup:group]; + [self removeMatchingKey:key forTargetID:2]; + XCTAssertFalse([self.queryCache containsKey:key]); + }); } - (void)testRemoveMatchingKeysForTargetID { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveMatchingKeysForTargetID"]; - DocumentKey key1 = testutil::Key("foo/bar"); - DocumentKey key2 = testutil::Key("foo/baz"); - DocumentKey key3 = testutil::Key("foo/blah"); - - [self addMatchingKey:key1 forTargetID:1]; - [self addMatchingKey:key2 forTargetID:1]; - [self addMatchingKey:key3 forTargetID:2]; - XCTAssertTrue([self.queryCache containsKey:key1]); - XCTAssertTrue([self.queryCache containsKey:key2]); - XCTAssertTrue([self.queryCache containsKey:key3]); - - [self.queryCache removeMatchingKeysForTargetID:1]; - XCTAssertFalse([self.queryCache containsKey:key1]); - XCTAssertFalse([self.queryCache containsKey:key2]); - XCTAssertTrue([self.queryCache containsKey:key3]); - - [self.queryCache removeMatchingKeysForTargetID:2]; - XCTAssertFalse([self.queryCache containsKey:key1]); - XCTAssertFalse([self.queryCache containsKey:key2]); - XCTAssertFalse([self.queryCache containsKey:key3]); - [self.persistence commitGroup:group]; + self.persistence.run("testRemoveMatchingKeysForTargetID", [&]() { + DocumentKey key1 = testutil::Key("foo/bar"); + DocumentKey key2 = testutil::Key("foo/baz"); + DocumentKey key3 = testutil::Key("foo/blah"); + + [self addMatchingKey:key1 forTargetID:1]; + [self addMatchingKey:key2 forTargetID:1]; + [self addMatchingKey:key3 forTargetID:2]; + XCTAssertTrue([self.queryCache containsKey:key1]); + XCTAssertTrue([self.queryCache containsKey:key2]); + XCTAssertTrue([self.queryCache containsKey:key3]); + + [self.queryCache removeMatchingKeysForTargetID:1]; + XCTAssertFalse([self.queryCache containsKey:key1]); + XCTAssertFalse([self.queryCache containsKey:key2]); + XCTAssertTrue([self.queryCache containsKey:key3]); + + [self.queryCache removeMatchingKeysForTargetID:2]; + XCTAssertFalse([self.queryCache containsKey:key1]); + XCTAssertFalse([self.queryCache containsKey:key2]); + XCTAssertFalse([self.queryCache containsKey:key3]); + }); } - (void)testRemoveEmitsGarbageEvents { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveEmitsGarbageEvents"]; - FSTEagerGarbageCollector *garbageCollector = [[FSTEagerGarbageCollector alloc] init]; - [garbageCollector addGarbageSource:self.queryCache]; - XCTAssertEqual([garbageCollector collectGarbage], std::set({})); + self.persistence.run("testRemoveEmitsGarbageEvents", [&]() { + FSTEagerGarbageCollector *garbageCollector = [[FSTEagerGarbageCollector alloc] init]; + [garbageCollector addGarbageSource:self.queryCache]; + XCTAssertEqual([garbageCollector collectGarbage], std::set({})); - FSTQueryData *rooms = [self queryDataWithQuery:FSTTestQuery("rooms")]; - DocumentKey room1 = testutil::Key("rooms/bar"); - DocumentKey room2 = testutil::Key("rooms/foo"); - [self.queryCache addQueryData:rooms]; - [self addMatchingKey:room1 forTargetID:rooms.targetID]; - [self addMatchingKey:room2 forTargetID:rooms.targetID]; + FSTQueryData *rooms = [self queryDataWithQuery:FSTTestQuery("rooms")]; + DocumentKey room1 = testutil::Key("rooms/bar"); + DocumentKey room2 = testutil::Key("rooms/foo"); + [self.queryCache addQueryData:rooms]; + [self addMatchingKey:room1 forTargetID:rooms.targetID]; + [self addMatchingKey:room2 forTargetID:rooms.targetID]; - FSTQueryData *halls = [self queryDataWithQuery:FSTTestQuery("halls")]; - DocumentKey hall1 = testutil::Key("halls/bar"); - DocumentKey hall2 = testutil::Key("halls/foo"); - [self.queryCache addQueryData:halls]; - [self addMatchingKey:hall1 forTargetID:halls.targetID]; - [self addMatchingKey:hall2 forTargetID:halls.targetID]; + FSTQueryData *halls = [self queryDataWithQuery:FSTTestQuery("halls")]; + DocumentKey hall1 = testutil::Key("halls/bar"); + DocumentKey hall2 = testutil::Key("halls/foo"); + [self.queryCache addQueryData:halls]; + [self addMatchingKey:hall1 forTargetID:halls.targetID]; + [self addMatchingKey:hall2 forTargetID:halls.targetID]; - XCTAssertEqual([garbageCollector collectGarbage], std::set({})); + XCTAssertEqual([garbageCollector collectGarbage], std::set({})); - [self removeMatchingKey:room1 forTargetID:rooms.targetID]; - XCTAssertEqual([garbageCollector collectGarbage], std::set({room1})); + [self removeMatchingKey:room1 forTargetID:rooms.targetID]; + XCTAssertEqual([garbageCollector collectGarbage], std::set({room1})); - [self.queryCache removeQueryData:rooms]; - XCTAssertEqual([garbageCollector collectGarbage], std::set({room2})); + [self.queryCache removeQueryData:rooms]; + XCTAssertEqual([garbageCollector collectGarbage], std::set({room2})); - [self.queryCache removeMatchingKeysForTargetID:halls.targetID]; - XCTAssertEqual([garbageCollector collectGarbage], std::set({hall1, hall2})); - [self.persistence commitGroup:group]; + [self.queryCache removeMatchingKeysForTargetID:halls.targetID]; + XCTAssertEqual([garbageCollector collectGarbage], std::set({hall1, hall2})); + }); } - (void)testMatchingKeysForTargetID { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"MatchingKeysForTargetID"]; - DocumentKey key1 = testutil::Key("foo/bar"); - DocumentKey key2 = testutil::Key("foo/baz"); - DocumentKey key3 = testutil::Key("foo/blah"); + self.persistence.run("testMatchingKeysForTargetID", [&]() { + DocumentKey key1 = testutil::Key("foo/bar"); + DocumentKey key2 = testutil::Key("foo/baz"); + DocumentKey key3 = testutil::Key("foo/blah"); - [self addMatchingKey:key1 forTargetID:1]; - [self addMatchingKey:key2 forTargetID:1]; - [self addMatchingKey:key3 forTargetID:2]; + [self addMatchingKey:key1 forTargetID:1]; + [self addMatchingKey:key2 forTargetID:1]; + [self addMatchingKey:key3 forTargetID:2]; - FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:1], (@[ key1, key2 ])); - FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:2], @[ key3 ]); + FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:1], (@[ key1, key2 ])); + FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:2], @[ key3 ]); - [self addMatchingKey:key1 forTargetID:2]; - FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:1], (@[ key1, key2 ])); - FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:2], (@[ key1, key3 ])); - [self.persistence commitGroup:group]; + [self addMatchingKey:key1 forTargetID:2]; + FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:1], (@[ key1, key2 ])); + FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:2], (@[ key1, key3 ])); + }); } - (void)testHighestListenSequenceNumber { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"HighestListenSequenceNumber"]; - FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("rooms") - targetID:1 - listenSequenceNumber:10 - purpose:FSTQueryPurposeListen]; - [self.queryCache addQueryData:query1]; - FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("halls") - targetID:2 - listenSequenceNumber:20 - purpose:FSTQueryPurposeListen]; - [self.queryCache addQueryData:query2]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20); - - // TargetIDs never come down. - [self.queryCache removeQueryData:query2]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20); - - // A query with an empty result set still counts. - FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("garages") - targetID:42 - listenSequenceNumber:100 - purpose:FSTQueryPurposeListen]; - [self.queryCache addQueryData:query3]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); - - [self.queryCache removeQueryData:query1]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); - - [self.queryCache removeQueryData:query3]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); - [self.persistence commitGroup:group]; + self.persistence.run("testHighestListenSequenceNumber", [&]() { + FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("rooms") + targetID:1 + listenSequenceNumber:10 + purpose:FSTQueryPurposeListen]; + [self.queryCache addQueryData:query1]; + FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("halls") + targetID:2 + listenSequenceNumber:20 + purpose:FSTQueryPurposeListen]; + [self.queryCache addQueryData:query2]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20); + + // TargetIDs never come down. + [self.queryCache removeQueryData:query2]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20); + + // A query with an empty result set still counts. + FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("garages") + targetID:42 + listenSequenceNumber:100 + purpose:FSTQueryPurposeListen]; + [self.queryCache addQueryData:query3]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + + [self.queryCache removeQueryData:query1]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + + [self.queryCache removeQueryData:query3]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + }); // Verify that the highestTargetID even survives restarts. - self.queryCache = [self.persistence queryCache]; - [self.queryCache start]; - XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + self.persistence.run("testHighestListenSequenceNumber restart", [&]() { + self.queryCache = [self.persistence queryCache]; + [self.queryCache start]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + }); } - (void)testHighestTargetID { if ([self isTestBaseClass]) return; - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"RemoveMatchingKeysForTargetID"]; - XCTAssertEqual([self.queryCache highestTargetID], 0); - - FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("rooms") - targetID:1 - listenSequenceNumber:10 - purpose:FSTQueryPurposeListen]; - DocumentKey key1 = testutil::Key("rooms/bar"); - DocumentKey key2 = testutil::Key("rooms/foo"); - [self.queryCache addQueryData:query1]; - [self addMatchingKey:key1 forTargetID:1]; - [self addMatchingKey:key2 forTargetID:1]; - - FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("halls") - targetID:2 - listenSequenceNumber:20 - purpose:FSTQueryPurposeListen]; - DocumentKey key3 = testutil::Key("halls/foo"); - [self.queryCache addQueryData:query2]; - [self addMatchingKey:key3 forTargetID:2]; - XCTAssertEqual([self.queryCache highestTargetID], 2); - - // TargetIDs never come down. - [self.queryCache removeQueryData:query2]; - XCTAssertEqual([self.queryCache highestTargetID], 2); - - // A query with an empty result set still counts. - FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("garages") - targetID:42 - listenSequenceNumber:100 - purpose:FSTQueryPurposeListen]; - [self.queryCache addQueryData:query3]; - XCTAssertEqual([self.queryCache highestTargetID], 42); - - [self.queryCache removeQueryData:query1]; - XCTAssertEqual([self.queryCache highestTargetID], 42); - - [self.queryCache removeQueryData:query3]; - XCTAssertEqual([self.queryCache highestTargetID], 42); - [self.persistence commitGroup:group]; + self.persistence.run("testHighestTargetID", [&]() { + XCTAssertEqual([self.queryCache highestTargetID], 0); + + FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("rooms") + targetID:1 + listenSequenceNumber:10 + purpose:FSTQueryPurposeListen]; + DocumentKey key1 = testutil::Key("rooms/bar"); + DocumentKey key2 = testutil::Key("rooms/foo"); + [self.queryCache addQueryData:query1]; + [self addMatchingKey:key1 forTargetID:1]; + [self addMatchingKey:key2 forTargetID:1]; + + FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("halls") + targetID:2 + listenSequenceNumber:20 + purpose:FSTQueryPurposeListen]; + DocumentKey key3 = testutil::Key("halls/foo"); + [self.queryCache addQueryData:query2]; + [self addMatchingKey:key3 forTargetID:2]; + XCTAssertEqual([self.queryCache highestTargetID], 2); + + // TargetIDs never come down. + [self.queryCache removeQueryData:query2]; + XCTAssertEqual([self.queryCache highestTargetID], 2); + + // A query with an empty result set still counts. + FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery("garages") + targetID:42 + listenSequenceNumber:100 + purpose:FSTQueryPurposeListen]; + [self.queryCache addQueryData:query3]; + XCTAssertEqual([self.queryCache highestTargetID], 42); + + [self.queryCache removeQueryData:query1]; + XCTAssertEqual([self.queryCache highestTargetID], 42); + + [self.queryCache removeQueryData:query3]; + XCTAssertEqual([self.queryCache highestTargetID], 42); + }); + // Verify that the highestTargetID even survives restarts. - self.queryCache = [self.persistence queryCache]; - [self.queryCache start]; - XCTAssertEqual([self.queryCache highestTargetID], 42); + self.persistence.run("testHighestTargetID restart", [&]() { + self.queryCache = [self.persistence queryCache]; + [self.queryCache start]; + XCTAssertEqual([self.queryCache highestTargetID], 42); + }); } - (void)testLastRemoteSnapshotVersion { if ([self isTestBaseClass]) return; - XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], - [FSTSnapshotVersion noVersion]); + self.persistence.run("testLastRemoteSnapshotVersion", [&]() { + XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], + [FSTSnapshotVersion noVersion]); - // Can set the snapshot version. - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"setLastRemoteSnapshotVersion"]; - [self.queryCache setLastRemoteSnapshotVersion:FSTTestVersion(42)]; - [self.persistence commitGroup:group]; - XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], FSTTestVersion(42)); + // Can set the snapshot version. + [self.queryCache setLastRemoteSnapshotVersion:FSTTestVersion(42)]; + XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], FSTTestVersion(42)); + }); // Snapshot version persists restarts. self.queryCache = [self.persistence queryCache]; - [self.queryCache start]; - XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], FSTTestVersion(42)); + self.persistence.run("testLastRemoteSnapshotVersion restart", [&]() { + [self.queryCache start]; + XCTAssertEqualObjects([self.queryCache lastRemoteSnapshotVersion], FSTTestVersion(42)); + }); } #pragma mark - Helpers diff --git a/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm b/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm index e4c68fe..2e32591 100644 --- a/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm +++ b/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm @@ -18,7 +18,6 @@ #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Local/FSTPersistence.h" -#import "Firestore/Source/Local/FSTWriteGroup.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTDocumentSet.h" @@ -52,14 +51,18 @@ static const int kVersion = 42; - (void)testReadDocumentNotInCache { if (!self.remoteDocumentCache) return; - XCTAssertNil([self readEntryAtPath:kDocPath]); + self.persistence.run("testReadDocumentNotInCache", [&]() { + XCTAssertNil([self.remoteDocumentCache entryForKey:testutil::Key(kDocPath)]); + }); } // Helper for next two tests. - (void)setAndReadADocumentAtPath:(const absl::string_view)path { - FSTDocument *written = [self setTestDocumentAtPath:path]; - FSTMaybeDocument *read = [self readEntryAtPath:path]; - XCTAssertEqualObjects(read, written); + self.persistence.run("setAndReadADocumentAtPath", [&]() { + FSTDocument *written = [self setTestDocumentAtPath:path]; + FSTMaybeDocument *read = [self.remoteDocumentCache entryForKey:testutil::Key(path)]; + XCTAssertEqualObjects(read, written); + }); } - (void)testSetAndReadADocument { @@ -77,58 +80,67 @@ static const int kVersion = 42; - (void)testSetAndReadDeletedDocument { if (!self.remoteDocumentCache) return; - FSTDeletedDocument *deletedDoc = FSTTestDeletedDoc(kDocPath, kVersion); - [self addEntry:deletedDoc]; + self.persistence.run("testSetAndReadDeletedDocument", [&]() { + FSTDeletedDocument *deletedDoc = FSTTestDeletedDoc(kDocPath, kVersion); + [self.remoteDocumentCache addEntry:deletedDoc]; - XCTAssertEqualObjects([self readEntryAtPath:kDocPath], deletedDoc); + XCTAssertEqualObjects([self.remoteDocumentCache entryForKey:testutil::Key(kDocPath)], + deletedDoc); + }); } - (void)testSetDocumentToNewValue { if (!self.remoteDocumentCache) return; - [self setTestDocumentAtPath:kDocPath]; - FSTDocument *newDoc = FSTTestDoc(kDocPath, kVersion, @{ @"data" : @2 }, NO); - [self addEntry:newDoc]; - XCTAssertEqualObjects([self readEntryAtPath:kDocPath], newDoc); + self.persistence.run("testSetDocumentToNewValue", [&]() { + [self setTestDocumentAtPath:kDocPath]; + FSTDocument *newDoc = FSTTestDoc(kDocPath, kVersion, @{ @"data" : @2 }, NO); + [self.remoteDocumentCache addEntry:newDoc]; + XCTAssertEqualObjects([self.remoteDocumentCache entryForKey:testutil::Key(kDocPath)], newDoc); + }); } - (void)testRemoveDocument { if (!self.remoteDocumentCache) return; - [self setTestDocumentAtPath:kDocPath]; - [self removeEntryAtPath:kDocPath]; + self.persistence.run("testRemoveDocument", [&]() { + [self setTestDocumentAtPath:kDocPath]; + [self.remoteDocumentCache removeEntryForKey:testutil::Key(kDocPath)]; - XCTAssertNil([self readEntryAtPath:kDocPath]); + XCTAssertNil([self.remoteDocumentCache entryForKey:testutil::Key(kDocPath)]); + }); } - (void)testRemoveNonExistentDocument { if (!self.remoteDocumentCache) return; - // no-op, but make sure it doesn't throw. - XCTAssertNoThrow([self removeEntryAtPath:kDocPath]); + self.persistence.run("testRemoveNonExistentDocument", [&]() { + // no-op, but make sure it doesn't throw. + XCTAssertNoThrow([self.remoteDocumentCache removeEntryForKey:testutil::Key(kDocPath)]); + }); } // TODO(mikelehen): Write more elaborate tests once we have more elaborate implementations. - (void)testDocumentsMatchingQuery { if (!self.remoteDocumentCache) return; - // TODO(rsgowman): This just verifies that we do a prefix scan against the - // query path. We'll need more tests once we add index support. - [self setTestDocumentAtPath:"a/1"]; - [self setTestDocumentAtPath:"b/1"]; - [self setTestDocumentAtPath:"b/2"]; - [self setTestDocumentAtPath:"c/1"]; - - FSTQuery *query = FSTTestQuery("b"); - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"DocumentsMatchingQuery"]; - FSTDocumentDictionary *results = [self.remoteDocumentCache documentsMatchingQuery:query]; - NSArray *expected = - @[ FSTTestDoc("b/1", kVersion, _kDocData, NO), FSTTestDoc("b/2", kVersion, _kDocData, NO) ]; - XCTAssertEqual([results count], [expected count]); - for (FSTDocument *doc in expected) { - XCTAssertEqualObjects([results objectForKey:doc.key], doc); - } - [self.persistence commitGroup:group]; + self.persistence.run("testDocumentsMatchingQuery", [&]() { + // TODO(rsgowman): This just verifies that we do a prefix scan against the + // query path. We'll need more tests once we add index support. + [self setTestDocumentAtPath:"a/1"]; + [self setTestDocumentAtPath:"b/1"]; + [self setTestDocumentAtPath:"b/2"]; + [self setTestDocumentAtPath:"c/1"]; + + FSTQuery *query = FSTTestQuery("b"); + FSTDocumentDictionary *results = [self.remoteDocumentCache documentsMatchingQuery:query]; + NSArray *expected = + @[ FSTTestDoc("b/1", kVersion, _kDocData, NO), FSTTestDoc("b/2", kVersion, _kDocData, NO) ]; + XCTAssertEqual([results count], [expected count]); + for (FSTDocument *doc in expected) { + XCTAssertEqualObjects([results objectForKey:doc.key], doc); + } + }); } #pragma mark - Helpers @@ -136,29 +148,10 @@ static const int kVersion = 42; - (FSTDocument *)setTestDocumentAtPath:(const absl::string_view)path { FSTDocument *doc = FSTTestDoc(path, kVersion, _kDocData, NO); - [self addEntry:doc]; + [self.remoteDocumentCache addEntry:doc]; return doc; } -- (void)addEntry:(FSTMaybeDocument *)maybeDoc { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"addEntry"]; - [self.remoteDocumentCache addEntry:maybeDoc]; - [self.persistence commitGroup:group]; -} - -- (FSTMaybeDocument *_Nullable)readEntryAtPath:(const absl::string_view)path { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"ReadEntryAtPath"]; - FSTMaybeDocument *result = [self.remoteDocumentCache entryForKey:testutil::Key(path)]; - [self.persistence commitGroup:group]; - return result; -} - -- (void)removeEntryAtPath:(const absl::string_view)path { - FSTWriteGroup *group = [self.persistence startGroupWithAction:@"removeEntryAtPath"]; - [self.remoteDocumentCache removeEntryForKey:testutil::Key(path)]; - [self.persistence commitGroup:group]; -} - @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm b/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm index 59def0f..ae50e3e 100644 --- a/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm +++ b/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm @@ -46,14 +46,14 @@ NS_ASSUME_NONNULL_BEGIN _remoteDocumentCache = [_db remoteDocumentCache]; // Add a couple initial items to the cache. - FSTWriteGroup *group = [_db startGroupWithAction:@"Add initial docs."]; - _kInitialADoc = FSTTestDoc("coll/a", 42, @{@"test" : @"data"}, NO); - [_remoteDocumentCache addEntry:_kInitialADoc]; + _db.run("Test setup", [&]() { + _kInitialADoc = FSTTestDoc("coll/a", 42, @{@"test" : @"data"}, NO); + [_remoteDocumentCache addEntry:_kInitialADoc]; - _kInitialBDoc = - [FSTDeletedDocument documentWithKey:FSTTestDocKey(@"coll/b") version:FSTTestVersion(314)]; - [_remoteDocumentCache addEntry:_kInitialBDoc]; - [_db commitGroup:group]; + _kInitialBDoc = + [FSTDeletedDocument documentWithKey:FSTTestDocKey(@"coll/b") version:FSTTestVersion(314)]; + [_remoteDocumentCache addEntry:_kInitialBDoc]; + }); _remoteDocumentBuffer = [FSTRemoteDocumentChangeBuffer changeBufferWithCache:_remoteDocumentCache]; @@ -68,10 +68,10 @@ NS_ASSUME_NONNULL_BEGIN } - (void)testReadUnchangedEntry { - FSTWriteGroup *group = [_db startGroupWithAction:@"ReadUnchangedEntry"]; - XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")], - _kInitialADoc); - [_db commitGroup:group]; + _db.run("testReadUnchangedEntry", [&]() { + XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")], + _kInitialADoc); + }); } - (void)testAddEntryAndReadItBack { @@ -80,38 +80,37 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")], newADoc); // B should still be unchanged. - FSTWriteGroup *group = [_db startGroupWithAction:@"AddEntryAndReadItBack"]; - XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/b")], - _kInitialBDoc); - [_db commitGroup:group]; + _db.run("testAddEntryAndReadItBack", [&]() { + XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/b")], + _kInitialBDoc); + }); } - (void)testApplyChanges { FSTMaybeDocument *newADoc = FSTTestDoc("coll/a", 43, @{@"new" : @"data"}, NO); [_remoteDocumentBuffer addEntry:newADoc]; - FSTWriteGroup *group = [_db startGroupWithAction:@"Apply Changes"]; - XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")], newADoc); + _db.run("testApplyChanges setup", [&]() { + XCTAssertEqualObjects([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")], newADoc); - // Reading directly against the cache should still yield the old result. - XCTAssertEqualObjects([_remoteDocumentCache entryForKey:FSTTestDocKey(@"coll/a")], _kInitialADoc); - [_db commitGroup:group]; + // Reading directly against the cache should still yield the old result. + XCTAssertEqualObjects([_remoteDocumentCache entryForKey:FSTTestDocKey(@"coll/a")], + _kInitialADoc); + }); - group = [_db startGroupWithAction:@"Apply changes"]; - [_remoteDocumentBuffer applyToWriteGroup:group]; + _db.run("testApplyChanges", [&]() { + [_remoteDocumentBuffer apply]; - // Reading against the cache should now yield the new result. - XCTAssertEqualObjects([_remoteDocumentCache entryForKey:FSTTestDocKey(@"coll/a")], newADoc); - [_db commitGroup:group]; + // Reading against the cache should now yield the new result. + XCTAssertEqualObjects([_remoteDocumentCache entryForKey:FSTTestDocKey(@"coll/a")], newADoc); + }); } - (void)testMethodsThrowAfterApply { - FSTWriteGroup *group = [_db startGroupWithAction:@"Apply changes"]; - [_remoteDocumentBuffer applyToWriteGroup:group]; - [_db commitGroup:group]; + _db.run("testMethodsThrowAfterApply", [&]() { [_remoteDocumentBuffer apply]; }); XCTAssertThrows([_remoteDocumentBuffer entryForKey:FSTTestDocKey(@"coll/a")]); XCTAssertThrows([_remoteDocumentBuffer addEntry:_kInitialADoc]); - XCTAssertThrows([_remoteDocumentBuffer applyToWriteGroup:group]); + XCTAssertThrows([_remoteDocumentBuffer apply]); } @end diff --git a/Firestore/Example/Tests/Local/FSTWriteGroupTests.mm b/Firestore/Example/Tests/Local/FSTWriteGroupTests.mm deleted file mode 100644 index 871bd99..0000000 --- a/Firestore/Example/Tests/Local/FSTWriteGroupTests.mm +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "Firestore/Source/Local/FSTWriteGroup.h" - -#import -#include - -#import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" -#import "Firestore/Source/Local/FSTLevelDB.h" -#import "Firestore/Source/Local/FSTLevelDBKey.h" - -#import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" - -using leveldb::ReadOptions; -using leveldb::Status; - -NS_ASSUME_NONNULL_BEGIN - -@interface FSTWriteGroupTests : XCTestCase -@end - -@implementation FSTWriteGroupTests { - FSTLevelDB *_db; -} - -- (void)setUp { - [super setUp]; - - _db = [FSTPersistenceTestHelpers levelDBPersistence]; -} - -- (void)tearDown { - _db = nil; - - [super tearDown]; -} - -- (void)testCommit { - std::string key = [FSTLevelDBMutationKey keyWithUserID:"user1" batchID:42]; - FSTPBWriteBatch *message = [FSTPBWriteBatch message]; - message.batchId = 42; - - // This is a test that shows that committing an empty group does not fail. There are no side - // effects to verify though. - FSTWriteGroup *group = [_db startGroupWithAction:@"Empty commit"]; - XCTAssertNoThrow([_db commitGroup:group]); - - group = [_db startGroupWithAction:@"Put"]; - [group setMessage:message forKey:key]; - - std::string value; - Status status = _db.ptr->Get(ReadOptions(), key, &value); - XCTAssertTrue(status.IsNotFound()); - - [_db commitGroup:group]; - status = _db.ptr->Get(ReadOptions(), key, &value); - XCTAssertTrue(status.ok()); - - group = [_db startGroupWithAction:@"Delete"]; - [group removeMessageForKey:key]; - status = _db.ptr->Get(ReadOptions(), key, &value); - XCTAssertTrue(status.ok()); - - [_db commitGroup:group]; - status = _db.ptr->Get(ReadOptions(), key, &value); - XCTAssertTrue(status.IsNotFound()); -} - -- (void)testCommittingWrongGroupThrows { - // If you don't create the group through persistence, it should throw. - FSTWriteGroup *group = [FSTWriteGroup groupWithAction:@"group"]; - XCTAssertThrows([_db commitGroup:group]); -} - -- (void)testCommittingTwiceThrows { - FSTWriteGroup *group = [_db startGroupWithAction:@"group"]; - [_db commitGroup:group]; - XCTAssertThrows([_db commitGroup:group]); -} - -- (void)testNestingGroupsThrows { - [_db startGroupWithAction:@"group1"]; - XCTAssertThrows([_db startGroupWithAction:@"group2"]); -} -@end - -NS_ASSUME_NONNULL_END -- cgit v1.2.3