diff --git a/Split/Api/SplitDatabaseHelper.swift b/Split/Api/SplitDatabaseHelper.swift index 12eb27e9c..c30801b19 100644 --- a/Split/Api/SplitDatabaseHelper.swift +++ b/Split/Api/SplitDatabaseHelper.swift @@ -103,7 +103,8 @@ struct SplitDatabaseHelper { let flagSetsCache: FlagSetsCache = DefaultFlagSetsCache(setsInFilter: splitClientConfig.bySetsFilter()?.values.asSet()) let persistentSplitsStorage = DefaultPersistentSplitsStorage(database: splitDatabase) - let splitsStorage = openSplitsStorage(database: splitDatabase, flagSetsCache: flagSetsCache) + let generalInfoStorage = openGeneralInfoStorage(database: splitDatabase) + let splitsStorage = openSplitsStorage(database: splitDatabase, flagSetsCache: flagSetsCache, generalInfoStorage: generalInfoStorage) let persistentImpressionsStorage = openPersistentImpressionsStorage(database: splitDatabase) let impressionsStorage = openImpressionsStorage(persistentStorage: persistentImpressionsStorage) @@ -111,8 +112,6 @@ struct SplitDatabaseHelper { let persistentEventsStorage = openPersistentEventsStorage(database: splitDatabase) let eventsStorage = openEventsStorage(persistentStorage: persistentEventsStorage) - - let generalInfoStorage = openGeneralInfoStorage(database: splitDatabase) let mySegmentsStorage = openMySegmentsStorage(database: splitDatabase, generalInfoStorage: generalInfoStorage) let myLargeSegmentsStorage = openMyLargeSegmentsStorage(database: splitDatabase, generalInfoStorage: generalInfoStorage) @@ -133,7 +132,7 @@ struct SplitDatabaseHelper { generalInfoStorage: generalInfoStorage) let ruleBasedSegmentsStorage = DefaultRuleBasedSegmentsStorage( - persistentStorage: persistentRuleBasedSegmentsStorage) + persistentStorage: persistentRuleBasedSegmentsStorage, generalInfoStorage: generalInfoStorage) return SplitStorageContainer(splitDatabase: splitDatabase, splitsStorage: splitsStorage, @@ -175,9 +174,9 @@ struct SplitDatabaseHelper { } static func openSplitsStorage(database: SplitDatabase, - flagSetsCache: FlagSetsCache) -> SplitsStorage { + flagSetsCache: FlagSetsCache, generalInfoStorage: GeneralInfoStorage) -> SplitsStorage { return DefaultSplitsStorage(persistentSplitsStorage: openPersistentSplitsStorage(database: database), - flagSetsCache: flagSetsCache) + flagSetsCache: flagSetsCache, GeneralInfoStorage: generalInfoStorage) } static func openPersistentMySegmentsStorage(database: SplitDatabase) -> PersistentMySegmentsStorage { diff --git a/Split/Events/SplitEventsManager.swift b/Split/Events/SplitEventsManager.swift index b66564a2e..61bca0036 100644 --- a/Split/Events/SplitEventsManager.swift +++ b/Split/Events/SplitEventsManager.swift @@ -148,7 +148,9 @@ class DefaultSplitEventsManager: SplitEventsManager { self.triggered.append(event) switch event { case .splitsUpdated, .mySegmentsUpdated, .myLargeSegmentsUpdated: + print(" EVENT !! **************\n\(event)\n EVENT !! **************") if isTriggered(external: .sdkReady) { + print(" SDK UPDATED ") trigger(event: .sdkUpdated) continue } @@ -181,7 +183,7 @@ class DefaultSplitEventsManager: SplitEventsManager { var triggered = false dataAccessQueue.sync { if let times = executionTimes[event.toString()] { - triggered = (times == 0) + triggered = (times == 0) } else { triggered = false } @@ -197,6 +199,7 @@ class DefaultSplitEventsManager: SplitEventsManager { if !isTriggered(external: .sdkReadyFromCache) { self.trigger(event: .sdkReadyFromCache) } + print(" --------- *** TRIGGER READy") self.trigger(event: .sdkReady) } } diff --git a/Split/FetcherEngine/Refresh/BackgroundSyncWorker.swift b/Split/FetcherEngine/Refresh/BackgroundSyncWorker.swift index 035c2a410..ce5e04709 100644 --- a/Split/FetcherEngine/Refresh/BackgroundSyncWorker.swift +++ b/Split/FetcherEngine/Refresh/BackgroundSyncWorker.swift @@ -57,7 +57,8 @@ class BackgroundSplitsSyncWorker: BackgroundSyncWorker { splitChangeProcessor: SplitChangeProcessor, ruleBasedSegmentsChangeProcessor: RuleBasedSegmentChangeProcessor, cacheExpiration: Int64, - splitConfig: SplitClientConfig) { + splitConfig: SplitClientConfig, + generalInfoStorage: GeneralInfoStorage) { self.persistenSplitsStorage = persistentSplitsStorage self.persistentRuleBasedSegmentsStorage = persistentRuleBasedSegmentsStorage @@ -66,7 +67,7 @@ class BackgroundSplitsSyncWorker: BackgroundSyncWorker { self.cacheExpiration = cacheExpiration self.syncHelper = SplitsSyncHelper(splitFetcher: splitFetcher, splitsStorage: BackgroundSyncSplitsStorage(persistentSplitsStorage: persistentSplitsStorage), - ruleBasedSegmentsStorage: DefaultRuleBasedSegmentsStorage(persistentStorage: persistentRuleBasedSegmentsStorage), + ruleBasedSegmentsStorage: DefaultRuleBasedSegmentsStorage(persistentStorage: persistentRuleBasedSegmentsStorage, generalInfoStorage: generalInfoStorage), splitChangeProcessor: splitChangeProcessor, ruleBasedSegmentsChangeProcessor: ruleBasedSegmentsChangeProcessor, generalInfoStorage: nil, // Pass nil to disable proxy handling for background sync diff --git a/Split/FetcherEngine/Refresh/SplitBgSynchronizer.swift b/Split/FetcherEngine/Refresh/SplitBgSynchronizer.swift index 5f21f0c10..0cd4c02a7 100644 --- a/Split/FetcherEngine/Refresh/SplitBgSynchronizer.swift +++ b/Split/FetcherEngine/Refresh/SplitBgSynchronizer.swift @@ -198,7 +198,8 @@ struct BackgroundSyncExecutor { splitChangeProcessor: changeProcessor, ruleBasedSegmentsChangeProcessor: ruleBasedSegmentChangeProcessor, cacheExpiration: cacheExpiration, - splitConfig: SplitClientConfig()) + splitConfig: SplitClientConfig(), + generalInfoStorage: generalInfoStorage) let impressionsRecorder = DefaultHttpImpressionsRecorder(restClient: restClient, diff --git a/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift b/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift index 6b5a3e8d2..d1eaee4e8 100644 --- a/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift +++ b/Split/FetcherEngine/Refresh/SplitsSyncHelper.swift @@ -27,7 +27,7 @@ class SplitsSyncHelper { private let splitFetcher: HttpSplitFetcher private let splitsStorage: SyncSplitsStorage - private let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage + private var ruleBasedSegmentsStorage: RuleBasedSegmentsStorage private let splitChangeProcessor: SplitChangeProcessor private let ruleBasedSegmentsChangeProcessor: RuleBasedSegmentChangeProcessor private let splitConfig: SplitClientConfig @@ -202,6 +202,7 @@ class SplitsSyncHelper { ruleBasedSegmentsStorage.clear() } firstFetch = false + if splitsStorage.update(splitChange: splitChangeProcessor.process(targetingRulesChange.featureFlags)) { featureFlagsUpdated = true } diff --git a/Split/Localhost/LocalhostSplitsStorage.swift b/Split/Localhost/LocalhostSplitsStorage.swift index 707aeeb4a..70e41faee 100644 --- a/Split/Localhost/LocalhostSplitsStorage.swift +++ b/Split/Localhost/LocalhostSplitsStorage.swift @@ -9,12 +9,11 @@ import Foundation class LocalhostSplitsStorage: SplitsStorage { - var changeNumber: Int64 = -1 var updateTimestamp: Int64 = 1 var splitsFilterQueryString: String = "" var flagsSpec: String = "" - internal var segmentsInUse: Int64 = 0 + var segmentsInUse: Int64 = 0 private let inMemorySplits = ConcurrentDictionary() @@ -48,6 +47,10 @@ class LocalhostSplitsStorage: SplitsStorage { inMemorySplits.setValues(values) return true } + + func getSegmentsInUse() -> Int64 { + segmentsInUse + } func update(filterQueryString: String) { } diff --git a/Split/Network/Streaming/SyncUpdateWorker.swift b/Split/Network/Streaming/SyncUpdateWorker.swift index 5aede0758..e9c90f2c5 100644 --- a/Split/Network/Streaming/SyncUpdateWorker.swift +++ b/Split/Network/Streaming/SyncUpdateWorker.swift @@ -172,7 +172,6 @@ class SplitsUpdateWorker: UpdateWorker { Logger.v("RBS update received: \(change)") let processedChange = ruleBasedSegmentsChangeProcessor.process(change) - if self.ruleBasedSegmentsStorage.update(toAdd: processedChange.toAdd, toRemove: processedChange.toRemove, changeNumber: processedChange.changeNumber) { diff --git a/Split/Network/Sync/FeatureFlagsSynchronizer.swift b/Split/Network/Sync/FeatureFlagsSynchronizer.swift index d23ef3a17..cdfbbb63a 100644 --- a/Split/Network/Sync/FeatureFlagsSynchronizer.swift +++ b/Split/Network/Sync/FeatureFlagsSynchronizer.swift @@ -89,9 +89,11 @@ class DefaultFeatureFlagsSynchronizer: FeatureFlagsSynchronizer { // MARK: Important. This should be called before loadLocal() // MARK: Part of /memberships hits optimization - if self.storageContainer.generalInfoStorage.getSegmentsInUse() == nil { + if storageContainer.generalInfoStorage.getSegmentsInUse() == nil && storageContainer.splitsStorage.changeNumber > -1 { + Logger.v("Force Parsing flags") splitsStorage.forceParsing() ruleBasedSegmentsStorage.forceParsing() + TimeChecker.logInterval("Time for Force Parsing", startTime: start) } // Load local diff --git a/Split/Storage/GeneralInfo/GeneralInfoStorage.swift b/Split/Storage/GeneralInfo/GeneralInfoStorage.swift index 6193c4e0b..416c3605f 100644 --- a/Split/Storage/GeneralInfo/GeneralInfoStorage.swift +++ b/Split/Storage/GeneralInfo/GeneralInfoStorage.swift @@ -26,6 +26,10 @@ protocol GeneralInfoStorage { class DefaultGeneralInfoStorage: GeneralInfoStorage { private let generalInfoDao: GeneralInfoDao + private var queue = DispatchQueue(label: "io.split.DefaultGeneralInfoStorage") + + // Part of Smart Pausing Optimization Feature + private var segmentsInUse: Int64? = nil init(generalInfoDao: GeneralInfoDao) { self.generalInfoDao = generalInfoDao @@ -84,10 +88,18 @@ class DefaultGeneralInfoStorage: GeneralInfoStorage { } func getSegmentsInUse() -> Int64? { - generalInfoDao.longValue(info: .segmentsInUse) + queue.sync { + if segmentsInUse == nil { // This happens just on start + segmentsInUse = generalInfoDao.longValue(info: .segmentsInUse) + } + } + return segmentsInUse } func setSegmentsInUse(_ count: Int64) { - generalInfoDao.update(info: .segmentsInUse, longValue: count) + segmentsInUse = count + queue.async { [weak self] in + self?.generalInfoDao.update(info: .segmentsInUse, longValue: count) + } } } diff --git a/Split/Storage/RuleBasedSegments/PersistentRuleBasedSegmentsStorage.swift b/Split/Storage/RuleBasedSegments/PersistentRuleBasedSegmentsStorage.swift index 42de8af24..5facb7253 100644 --- a/Split/Storage/RuleBasedSegments/PersistentRuleBasedSegmentsStorage.swift +++ b/Split/Storage/RuleBasedSegments/PersistentRuleBasedSegmentsStorage.swift @@ -13,9 +13,6 @@ protocol PersistentRuleBasedSegmentsStorage { func update(toAdd: Set, toRemove: Set, changeNumber: Int64) func clear() func getChangeNumber() -> Int64 - - func getSegmentsInUse() -> Int64? - func setSegmentsInUse(_ segmentsInUse: Int64) } class DefaultPersistentRuleBasedSegmentsStorage: PersistentRuleBasedSegmentsStorage { @@ -58,12 +55,4 @@ class DefaultPersistentRuleBasedSegmentsStorage: PersistentRuleBasedSegmentsStor func getChangeNumber() -> Int64 { return generalInfoStorage.getRuleBasedSegmentsChangeNumber() } - - func getSegmentsInUse() -> Int64? { - generalInfoStorage.getSegmentsInUse() - } - - func setSegmentsInUse(_ segmentsInUse: Int64) { - generalInfoStorage.setSegmentsInUse(segmentsInUse) - } } diff --git a/Split/Storage/RuleBasedSegments/RuleBasedSegmentsStorage.swift b/Split/Storage/RuleBasedSegments/RuleBasedSegmentsStorage.swift index a9a11b165..2e57ccbde 100644 --- a/Split/Storage/RuleBasedSegments/RuleBasedSegmentsStorage.swift +++ b/Split/Storage/RuleBasedSegments/RuleBasedSegmentsStorage.swift @@ -10,7 +10,6 @@ import Foundation protocol RuleBasedSegmentsStorage: RolloutDefinitionsCache { var changeNumber: Int64 { get } - var segmentsInUse: Int64 { get } func get(segmentName: String) -> RuleBasedSegment? func contains(segmentNames: Set) -> Bool @@ -22,28 +21,28 @@ protocol RuleBasedSegmentsStorage: RolloutDefinitionsCache { class DefaultRuleBasedSegmentsStorage: RuleBasedSegmentsStorage { private var persistentStorage: PersistentRuleBasedSegmentsStorage + private var generalInfoStorage: GeneralInfoStorage private var inMemorySegments: ConcurrentDictionary private(set) var changeNumber: Int64 = -1 - - internal var segmentsInUse: Int64 = 0 - init(persistentStorage: PersistentRuleBasedSegmentsStorage) { + init(persistentStorage: PersistentRuleBasedSegmentsStorage, generalInfoStorage: GeneralInfoStorage) { self.persistentStorage = persistentStorage self.inMemorySegments = ConcurrentDictionary() + self.generalInfoStorage = generalInfoStorage } func loadLocal() { - segmentsInUse = persistentStorage.getSegmentsInUse() ?? 0 + var segmentsInUse: Int64 = generalInfoStorage.getSegmentsInUse() ?? 0 let snapshot = persistentStorage.getSnapshot() let active = snapshot.segments.filter { $0.status == .active } let archived = snapshot.segments.filter { $0.status == .archived } - _ = processToAdd(Set(active)) - _ = processToRemove(Set(archived)) + _ = processToAdd(Set(active), &segmentsInUse) + _ = processToRemove(Set(archived), &segmentsInUse) changeNumber = snapshot.changeNumber - persistentStorage.setSegmentsInUse(segmentsInUse) + generalInfoStorage.setSegmentsInUse(segmentsInUse) } func get(segmentName: String) -> RuleBasedSegment? { @@ -68,39 +67,40 @@ class DefaultRuleBasedSegmentsStorage: RuleBasedSegmentsStorage { func update(toAdd: Set, toRemove: Set, changeNumber: Int64) -> Bool { - segmentsInUse = persistentStorage.getSegmentsInUse() ?? 0 + var segmentsInUse = generalInfoStorage.getSegmentsInUse() ?? 0 self.changeNumber = changeNumber // Process - let addResult = processToAdd(toAdd) - let removeResult = processToRemove(toRemove) + let addResult = processToAdd(toAdd, &segmentsInUse) + let removeResult = processToRemove(toRemove, &segmentsInUse) // Update persistent storage persistentStorage.update(toAdd: toAdd, toRemove: toRemove, changeNumber: changeNumber) - persistentStorage.setSegmentsInUse(segmentsInUse) + generalInfoStorage.setSegmentsInUse(segmentsInUse) return addResult || removeResult } - private func processToAdd(_ toAdd: Set) -> Bool { // Process segments to add + private func processToAdd(_ toAdd: Set, _ segmentsInUse: inout Int64) -> Bool { // Process segments to add var result = false for segment in toAdd { if let segmentName = segment.name?.lowercased() { - updateSegmentsCount(segment) + segmentsInUse += updateSegmentsCount(segment) inMemorySegments.setValue(segment, forKey: segmentName) result = true } } + return result } - private func processToRemove(_ toRemove: Set) -> Bool { // Process segments to remove + private func processToRemove(_ toRemove: Set, _ segmentsInUse: inout Int64) -> Bool { // Process segments to remove var result = false for segment in toRemove { if let segmentName = segment.name?.lowercased(), inMemorySegments.value(forKey: segmentName) != nil { - updateSegmentsCount(segment) + segmentsInUse += updateSegmentsCount(segment) inMemorySegments.removeValue(forKey: segmentName) result = true } @@ -115,19 +115,19 @@ class DefaultRuleBasedSegmentsStorage: RuleBasedSegmentsStorage { } func forceParsing() { - segmentsInUse = persistentStorage.getSegmentsInUse() ?? 0 + var segmentsInUse = generalInfoStorage.getSegmentsInUse() ?? 0 let activeSegments = persistentStorage.getSnapshot().segments.filter { $0.status == .active } for i in 0.. RuleBasedSegment? { @@ -135,12 +135,16 @@ class DefaultRuleBasedSegmentsStorage: RuleBasedSegmentsStorage { return parsedSegment } - fileprivate func updateSegmentsCount(_ segment: RuleBasedSegment) { + fileprivate func updateSegmentsCount(_ segment: RuleBasedSegment) -> Int64 { + var segmentsInUse: Int64 = 0 + if let segmentName = segment.name?.lowercased(), segment.status == .active, inMemorySegments.value(forKey: segmentName) == nil, StorageHelper.usesSegments(segment.conditions) { segmentsInUse += 1 } else if inMemorySegments.value(forKey: segment.name?.lowercased() ?? "") != nil, segment.status != .active, StorageHelper.usesSegments(segment.conditions) { segmentsInUse -= 1 } + + return segmentsInUse } #if DEBUG diff --git a/Split/Storage/Splits/PersistentSplitsStorage.swift b/Split/Storage/Splits/PersistentSplitsStorage.swift index e6faf2567..b082b5825 100644 --- a/Split/Storage/Splits/PersistentSplitsStorage.swift +++ b/Split/Storage/Splits/PersistentSplitsStorage.swift @@ -12,12 +12,10 @@ protocol PersistentSplitsStorage { func update(splitChange: ProcessedSplitChange) func update(split: Split) func update(bySetsFilter: SplitFilter?) - func update(segmentsInUse: Int64) func getBySetsFilter() -> SplitFilter? func getSplitsSnapshot() -> SplitsSnapshot func getChangeNumber() -> Int64 func getUpdateTimestamp() -> Int64 - func getSegmentsInUse() -> Int64? func getAll() -> [Split] func delete(splitNames: [String]) func clear() @@ -51,10 +49,6 @@ class DefaultPersistentSplitsStorage: PersistentSplitsStorage { func update(flagsSpec: String) { generalInfoDao.update(info: .flagsSpec, stringValue: flagsSpec) } - - func update(segmentsInUse: Int64) { - generalInfoDao.update(info: .segmentsInUse, longValue: segmentsInUse) - } func getFilterQueryString() -> String { return generalInfoDao.stringValue(info: .splitsFilterQueryString) ?? "" @@ -63,10 +57,6 @@ class DefaultPersistentSplitsStorage: PersistentSplitsStorage { func getFlagsSpec() -> String { return generalInfoDao.stringValue(info: .flagsSpec) ?? "" } - - func getSegmentsInUse() -> Int64? { - generalInfoDao.longValue(info: .segmentsInUse) - } func update(bySetsFilter filter: SplitFilter?) { guard let filter = filter else { diff --git a/Split/Storage/Splits/SplitsStorage.swift b/Split/Storage/Splits/SplitsStorage.swift index 8574846b7..8ef9d41a7 100644 --- a/Split/Storage/Splits/SplitsStorage.swift +++ b/Split/Storage/Splits/SplitsStorage.swift @@ -15,7 +15,6 @@ protocol SyncSplitsStorage: RolloutDefinitionsCache { protocol SplitsStorage: SyncSplitsStorage { var changeNumber: Int64 { get } var updateTimestamp: Int64 { get } - var segmentsInUse: Int64 { get } func loadLocal() func get(name: String) -> Split? @@ -36,21 +35,22 @@ class DefaultSplitsStorage: SplitsStorage { private var inMemorySplits: SynchronizedDictionary private var trafficTypes: SynchronizedDictionary private let flagSetsCache: FlagSetsCache - internal var segmentsInUse: Int64 = 0 + private let generalInfoStorage: GeneralInfoStorage private(set) var changeNumber: Int64 = -1 private(set) var updateTimestamp: Int64 = -1 init(persistentSplitsStorage: PersistentSplitsStorage, - flagSetsCache: FlagSetsCache) { + flagSetsCache: FlagSetsCache, + GeneralInfoStorage: GeneralInfoStorage) { self.persistentStorage = persistentSplitsStorage self.inMemorySplits = SynchronizedDictionary() self.trafficTypes = SynchronizedDictionary() self.flagSetsCache = flagSetsCache + self.generalInfoStorage = GeneralInfoStorage } func loadLocal() { - segmentsInUse = persistentStorage.getSegmentsInUse() ?? 0 let snapshot = persistentStorage.getSplitsSnapshot() let active = snapshot.splits.filter { $0.status == .active } let archived = snapshot.splits.filter { $0.status == .archived } @@ -126,6 +126,7 @@ class DefaultSplitsStorage: SplitsStorage { var cachedTrafficTypes = trafficTypes.all var splitsUpdated = false var splitsRemoved = false + var segmentsInUse: Int64 = generalInfoStorage.getSegmentsInUse() ?? 0 for split in splits { @@ -147,7 +148,7 @@ class DefaultSplitsStorage: SplitsStorage { } // Smart Pausing optimization - updateSegmentsCount(split: split) + segmentsInUse += updateSegmentsCount(split: split) if loadedSplit != nil, let oldTrafficType = loadedSplit?.trafficTypeName { // Must decrease old traffic type count if a feature flag is updated or removed @@ -174,7 +175,7 @@ class DefaultSplitsStorage: SplitsStorage { } inMemorySplits.setValues(cachedSplits) trafficTypes.setValues(cachedTrafficTypes) - persistentStorage.update(segmentsInUse: segmentsInUse) // Ensure count of Flags with Segments (for optimization feature) + generalInfoStorage.setSegmentsInUse(segmentsInUse) // Ensure count of Flags with Segments (for optimization feature) return splitsUpdated || splitsRemoved } @@ -225,33 +226,35 @@ class DefaultSplitsStorage: SplitsStorage { } func forceParsing() { // Parse all Splits - segmentsInUse = 0 + var segmentsInUse: Int64 = 0 let activeSplits = persistentStorage.getSplitsSnapshot().splits.filter( { $0.status == .active } ) if activeSplits.count > 0 { for i in 0...activeSplits.count-1 { guard let splitName = activeSplits[i].name else { continue } let parsedSplit = parseSplit(activeSplits[i]) - updateSegmentsCount(split: parsedSplit) + segmentsInUse += updateSegmentsCount(split: parsedSplit) inMemorySplits.setValue(parsedSplit, forKey: splitName) } } - persistentStorage.update(segmentsInUse: segmentsInUse) + generalInfoStorage.setSegmentsInUse(segmentsInUse) } - func updateSegmentsCount(split: Split) { // Keep count of Flags with Segments (used to optimize "/memberships" hits) - guard let splitName = split.name else { return } + func updateSegmentsCount(split: Split) -> Int64 { // Keep count of Flags with Segments (used to optimize "/memberships" hits) + guard let splitName = split.name else { return 0 } if inMemorySplits.value(forKey: splitName) == nil, split.status == .active { // If new Split and active if StorageHelper.usesSegments(split.conditions ?? []) { - segmentsInUse += 1 + return 1 } } else if inMemorySplits.value(forKey: splitName) != nil && split.status != .active { // If known Split and archived if StorageHelper.usesSegments(split.conditions ?? []) { - segmentsInUse -= 1 + return -1 } } + + return 0 } #if DEBUG diff --git a/SplitTests/Fake/Storage/GeneralInfoStorageMock.swift b/SplitTests/Fake/Storage/GeneralInfoStorageMock.swift index de21a1670..15e3eab98 100644 --- a/SplitTests/Fake/Storage/GeneralInfoStorageMock.swift +++ b/SplitTests/Fake/Storage/GeneralInfoStorageMock.swift @@ -10,7 +10,7 @@ class GeneralInfoStorageMock: GeneralInfoStorage { var flagsSpec = "" var ruleBasedSegmentsChangeNumber: Int64 = -1 var lastProxyUpdateTimestamp: Int64 = 0 - var segmentsInUse: Int64 = 0 + var segmentsInUse: Int64? = nil func getUpdateTimestamp() -> Int64 { return updateTimestamp diff --git a/SplitTests/Fake/Storage/PersistentRuleBasedSegmentsStorageStub.swift b/SplitTests/Fake/Storage/PersistentRuleBasedSegmentsStorageStub.swift index 2c8fb9138..0ffeee907 100644 --- a/SplitTests/Fake/Storage/PersistentRuleBasedSegmentsStorageStub.swift +++ b/SplitTests/Fake/Storage/PersistentRuleBasedSegmentsStorageStub.swift @@ -59,13 +59,4 @@ class PersistentRuleBasedSegmentsStorageStub: PersistentRuleBasedSegmentsStorage changeNumberCalled = true return delegate?.getChangeNumber() ?? -1 } - - var segmentsInUse: Int64 = 0 - func getSegmentsInUse() -> Int64? { - segmentsInUse - } - - func setSegmentsInUse(_ segmentsInUse: Int64) { - self.segmentsInUse = segmentsInUse - } } diff --git a/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift b/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift index 17454878c..a1bfd7550 100644 --- a/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift +++ b/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift @@ -21,7 +21,6 @@ class PersistentSplitsStorageStub: PersistentSplitsStorage { var getAllCalled = false var updateCalled = false - var getSegmentsInUseCalled = false var deleteCalled = false var clearCalled = false var closeCalled = false @@ -98,15 +97,4 @@ class PersistentSplitsStorageStub: PersistentSplitsStorage { getBySetsFilterCalled = false return nil } - - var segmentsInUse: Int64 = 0 - func update(segmentsInUse: Int64) { - self.segmentsInUse = segmentsInUse - delegate?.update(segmentsInUse: segmentsInUse) - } - - func getSegmentsInUse() -> Int64? { - getSegmentsInUseCalled = true - return segmentsInUse - } } diff --git a/SplitTests/Fake/Storage/SplitsStorageStub.swift b/SplitTests/Fake/Storage/SplitsStorageStub.swift index 5fd1f82e9..1f1ffff1b 100644 --- a/SplitTests/Fake/Storage/SplitsStorageStub.swift +++ b/SplitTests/Fake/Storage/SplitsStorageStub.swift @@ -73,6 +73,10 @@ class SplitsStorageStub: SplitsStorage { updateSplitChangeCalled = true return splitsWereUpdated } + + func getSegmentsInUse() -> Int64 { + segmentsInUse + } var updateFlagsSpecCalled = false func update(flagsSpec: String) { diff --git a/SplitTests/Integration/Sync/SplitSdkUpdatePollingTest.swift b/SplitTests/Integration/Sync/SplitSdkUpdatePollingTest.swift index 9a37c6761..05997d637 100644 --- a/SplitTests/Integration/Sync/SplitSdkUpdatePollingTest.swift +++ b/SplitTests/Integration/Sync/SplitSdkUpdatePollingTest.swift @@ -60,22 +60,11 @@ class SplitSdkUpdatePollingTest: XCTestCase { } else if index == self.spExp.count { self.spExp[index - 1].fulfill() } - return TestDispatcherResponse(code: 200, data: Data(IntegrationHelper.emptySplitChanges(since: 99999999, till: 99999999).utf8)) + let json = IntegrationHelper.loadSplitChangeFileJson(name: "splitchanges_1", sourceClass: IntegrationHelper()) + return TestDispatcherResponse(code: 200, data: Data(json!.utf8)) } if request.isMySegmentsEndpoint() { - self.mySegmentsHits+=1 - let hit = self.mySegmentsHits - var json = IntegrationHelper.emptyMySegments - if hit > 2 { - var mySegments = [String]() - for i in 1...hit { - mySegments.append("segment\(i)") - } - - json = IntegrationHelper.buildSegments(regular: mySegments) - return TestDispatcherResponse(code: 200, data: Data(json.utf8)) - } return TestDispatcherResponse(code: 200, data: Data(IntegrationHelper.emptyMySegments.utf8)) } @@ -93,7 +82,7 @@ class SplitSdkUpdatePollingTest: XCTestCase { return TestDispatcherResponse(code: 200) } - return TestDispatcherResponse(code: 500) + return TestDispatcherResponse(code: 200) } } @@ -165,7 +154,7 @@ class SplitSdkUpdatePollingTest: XCTestCase { let sdkUpdate = XCTestExpectation(description: "SDK Update Expectation") let splitConfig: SplitClientConfig = SplitClientConfig() - splitConfig.segmentsRefreshRate = 99999 + splitConfig.segmentsRefreshRate = 2 splitConfig.featuresRefreshRate = 2 splitConfig.impressionRefreshRate = 99999 splitConfig.sdkReadyTimeOut = 60000 @@ -182,25 +171,16 @@ class SplitSdkUpdatePollingTest: XCTestCase { let client = factory!.client - var sdkReadyFired = false - var sdkUpdatedFired = false - client.on(event: SplitEvent.sdkReady) { - sdkReadyFired = true sdkReady.fulfill() } client.on(event: SplitEvent.sdkUpdated) { - sdkUpdatedFired = true sdkUpdate.fulfill() } wait(for: [sdkReady, sdkUpdate], timeout: 30) - XCTAssertTrue(sdkReadyFired) - XCTAssertTrue(sdkUpdatedFired) - - let semaphore = DispatchSemaphore(value: 0) client.destroy(completion: { _ = semaphore.signal() @@ -217,14 +197,13 @@ class SplitSdkUpdatePollingTest: XCTestCase { let splitConfig: SplitClientConfig = SplitClientConfig() splitConfig.segmentsRefreshRate = 2 - splitConfig.featuresRefreshRate = 999999 + splitConfig.featuresRefreshRate = 2 splitConfig.impressionRefreshRate = 999999 splitConfig.sdkReadyTimeOut = 60000 splitConfig.trafficType = trafficType splitConfig.streamingEnabled = false splitConfig.logLevel = .verbose - splitConfig.serviceEndpoints = ServiceEndpoints.builder() - .set(sdkEndpoint: serverUrl).set(eventsEndpoint: serverUrl).build() + splitConfig.serviceEndpoints = ServiceEndpoints.builder().set(sdkEndpoint: serverUrl).set(eventsEndpoint: serverUrl).build() let key: Key = Key(matchingKey: kMatchingKey, bucketingKey: nil) let builder = DefaultSplitFactoryBuilder() @@ -234,28 +213,19 @@ class SplitSdkUpdatePollingTest: XCTestCase { let client = factory!.client - var sdkReadyFired = false - var sdkUpdatedFired = false - - client.on(event: SplitEvent.sdkReady) { - sdkReadyFired = true + client.on(event: .sdkReady) { sdkReady.fulfill() } - client.on(event: SplitEvent.sdkUpdated) { - sdkUpdatedFired = true + client.on(event: .sdkUpdated) { sdkUpdate.fulfill() } - wait(for: [sdkReady, sdkUpdate], timeout: 30) + wait(for: [sdkReady, sdkUpdate], timeout: 10) // wait for sdk update ThreadUtils.delay(seconds: 1.0) - XCTAssertTrue(sdkReadyFired) - XCTAssertTrue(sdkUpdatedFired) - - let semaphore = DispatchSemaphore(value: 0) client.destroy(completion: { _ = semaphore.signal() diff --git a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift index 31e552aeb..9529f123b 100644 --- a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift +++ b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift @@ -1,4 +1,3 @@ -// // MySegmentUpdateTest.swift // SplitTests // @@ -450,9 +449,9 @@ class MySegmentUpdateTest: XCTestCase { func testSdkRestartMembershipsSyncIfNewFlag() throws { var sdkReadyFired = false - var cacheReadyFired = true let sdkReady = XCTestExpectation(description: "SDK should be ready") let cacheReadyExp = XCTestExpectation(description: "Cache should be ready") + let sdkUpdateExp = XCTestExpectation(description: "SDK Should fire updated") let segmentsHit = XCTestExpectation(description: "/memberships should be hit at least once") var membershipsHit = 0 @@ -467,6 +466,7 @@ class MySegmentUpdateTest: XCTestCase { if request.url.absoluteString.contains("/memberships") { segmentsHit.fulfill() membershipsHit += 1 + print("HITTING MEMBERSHIPS") return TestDispatcherResponse(code: 200, data: Data(IntegrationHelper.emptyMySegments.utf8)) } @@ -509,7 +509,10 @@ class MySegmentUpdateTest: XCTestCase { client?.on(event: .sdkReadyFromCache) { cacheReadyExp.fulfill() - cacheReadyFired = true + } + + client?.on(event: .sdkUpdated) { + sdkUpdateExp.fulfill() } wait(for: [segmentsHit], timeout: 3) @@ -528,7 +531,7 @@ class MySegmentUpdateTest: XCTestCase { waitExp = XCTestExpectation(description: "Just waiting") waitExp.isInverted = true // Inverted expectation - wait(for: [waitExp], timeout: 5) + wait(for: [waitExp, sdkUpdateExp], timeout: 5) XCTAssertGreaterThan(membershipsHit, 2, "If new flags with segments arrive, the mechanism should be restarted and SDK should hit /memberships many times again") // Cleanup @@ -693,7 +696,6 @@ class MySegmentUpdateTest: XCTestCase { wait(for: [sdkUpdExp, sdkUpdMExp], timeout: 15) - // Hits are not asserted because tests will fail if expectations are not fulfilled XCTAssertEqual(4, syncSpy.forceMySegmentsSyncCount[userKey] ?? 0) XCTAssertEqual(4, syncSpy.forceMySegmentsSyncCount[userKeyM] ?? 0) diff --git a/SplitTests/Integration/streaming/StreamingAuthFail4xxTest.swift b/SplitTests/Integration/streaming/StreamingAuthFail4xxTest.swift index 0cd46bee5..545a6361a 100644 --- a/SplitTests/Integration/streaming/StreamingAuthFail4xxTest.swift +++ b/SplitTests/Integration/streaming/StreamingAuthFail4xxTest.swift @@ -53,24 +53,26 @@ class StreamingAuthFail4xxTest: XCTestCase { var timeOutFired = false var sdkReadyFired = false - client.on(event: SplitEvent.sdkReady) { + client.on(event: .sdkReady) { sdkReadyFired = true sdkReadyExpectation.fulfill() } - client.on(event: SplitEvent.sdkReadyTimedOut) { + client.on(event: .sdkReadyTimedOut) { timeOutFired = true sdkReadyExpectation.fulfill() } wait(for: [sdkReadyExpectation, mySegExp, splitsChgExp], timeout: 20) + + ThreadUtils.delay(seconds: 10) XCTAssertTrue(sdkReadyFired) XCTAssertFalse(timeOutFired) XCTAssertEqual(1, sseAuthHits) XCTAssertEqual(0, sseConnHits) - XCTAssertEqual(2, mySegmentsHits) - XCTAssertEqual(2, splitsChangesHits) + XCTAssertEqual(1, mySegmentsHits) // For Smart Pausing + XCTAssertGreaterThan(splitsChangesHits, 5) let semaphore = DispatchSemaphore(value: 0) client.destroy(completion: { @@ -90,9 +92,7 @@ class StreamingAuthFail4xxTest: XCTestCase { } if request.isMySegmentsEndpoint() { self.mySegmentsHits+=1 - if self.mySegmentsHits > 1 { - self.mySegExp.fulfill() - } + self.mySegExp.fulfill() return TestDispatcherResponse(code: 200, data: Data(IntegrationHelper.emptyMySegments.utf8)) } if request.isAuthEndpoint() { @@ -110,6 +110,5 @@ class StreamingAuthFail4xxTest: XCTestCase { return self.streamingBinding! } } - } diff --git a/SplitTests/Integration/streaming/TelemetryIntegrationTest.swift b/SplitTests/Integration/streaming/TelemetryIntegrationTest.swift index 29900b878..3f835d3e5 100644 --- a/SplitTests/Integration/streaming/TelemetryIntegrationTest.swift +++ b/SplitTests/Integration/streaming/TelemetryIntegrationTest.swift @@ -112,7 +112,6 @@ class TelemetryIntegrationTest: XCTestCase { splitConfig.telemetryRefreshRate = 99999 splitConfig.impressionRefreshRate = 99999 splitConfig.eventsPushRate = 99999 - //splitConfig.isDebugModeEnabled = true let key: Key = Key(matchingKey: userKey) let builder = DefaultSplitFactoryBuilder() @@ -127,11 +126,11 @@ class TelemetryIntegrationTest: XCTestCase { let sdkReadyExpectation = XCTestExpectation(description: "SDK READY Expectation") - client.on(event: SplitEvent.sdkReady) { + client.on(event: .sdkReady) { sdkReadyExpectation.fulfill() } - client.on(event: SplitEvent.sdkReadyTimedOut) { + client.on(event: .sdkReadyTimedOut) { IntegrationHelper.tlog("TIMEOUT") } diff --git a/SplitTests/Service/Splits/SplitsBgSyncWorkerTest.swift b/SplitTests/Service/Splits/SplitsBgSyncWorkerTest.swift index a9c8d0889..26967025c 100644 --- a/SplitTests/Service/Splits/SplitsBgSyncWorkerTest.swift +++ b/SplitTests/Service/Splits/SplitsBgSyncWorkerTest.swift @@ -19,6 +19,7 @@ class SplitsBgSyncWorkerTest: XCTestCase { var splitChangeProcessor: SplitChangeProcessorStub! var ruleBasedSegmentChangeProcessor: RuleBasedSegmentChangeProcessorStub! var splitsSyncWorker: BackgroundSyncWorker! + var generalInfoStorage = GeneralInfoStorageMock() override func setUp() { splitFetcher = HttpSplitFetcherStub() @@ -38,7 +39,8 @@ class SplitsBgSyncWorkerTest: XCTestCase { splitChangeProcessor: splitChangeProcessor, ruleBasedSegmentsChangeProcessor: ruleBasedSegmentChangeProcessor, cacheExpiration: 100, - splitConfig: SplitClientConfig()) + splitConfig: SplitClientConfig(), + generalInfoStorage: generalInfoStorage) let change = SplitChange(splits: [], since: 200, till: 200) splitFetcher.splitChanges = [TargetingRulesChange(featureFlags: change)] @@ -57,7 +59,8 @@ class SplitsBgSyncWorkerTest: XCTestCase { splitChangeProcessor: splitChangeProcessor, ruleBasedSegmentsChangeProcessor: ruleBasedSegmentChangeProcessor, cacheExpiration: 100, - splitConfig: SplitClientConfig()) + splitConfig: SplitClientConfig(), + generalInfoStorage: generalInfoStorage) splitFetcher.httpError = HttpError.clientRelated(code: -1, internalCode: -1) @@ -76,8 +79,9 @@ class SplitsBgSyncWorkerTest: XCTestCase { splitChangeProcessor: splitChangeProcessor, ruleBasedSegmentsChangeProcessor: ruleBasedSegmentChangeProcessor, cacheExpiration: 2000, - splitConfig: SplitClientConfig()) - + splitConfig: SplitClientConfig(), + generalInfoStorage: generalInfoStorage) + let change = SplitChange(splits: [], since: 200, till: 200) splitStorage.updateTimestamp = Int64(Date().timeIntervalSince1970) - Int64(expiration / 2) // Non Expired cache splitFetcher.splitChanges = [TargetingRulesChange(featureFlags: change)] diff --git a/SplitTests/Storage/RuleBasedSegmentStorageTest.swift b/SplitTests/Storage/RuleBasedSegmentStorageTest.swift index f2fa32185..4dfca3738 100644 --- a/SplitTests/Storage/RuleBasedSegmentStorageTest.swift +++ b/SplitTests/Storage/RuleBasedSegmentStorageTest.swift @@ -15,11 +15,10 @@ class RuleBasedSegmentStorageTest: XCTestCase { private var persistentStorageStub: PersistentRuleBasedSegmentsStorageStub! private var ruleBasedSegmentsStorage: DefaultRuleBasedSegmentsStorage! private var noLoadedRbs: DefaultRuleBasedSegmentsStorage? + private var generalInfoStorage = GeneralInfoStorageMock() override func setUp() { - ruleBasedSegmentsStorage = DefaultRuleBasedSegmentsStorage( - persistentStorage: createPersistentStorageStub() - ) + ruleBasedSegmentsStorage = DefaultRuleBasedSegmentsStorage(persistentStorage: createPersistentStorageStub(), generalInfoStorage: generalInfoStorage) ruleBasedSegmentsStorage.loadLocal() } @@ -30,9 +29,7 @@ class RuleBasedSegmentStorageTest: XCTestCase { } func testLazyParsing() { - noLoadedRbs = DefaultRuleBasedSegmentsStorage( - persistentStorage: createPersistentStorageStub(isParsed: false) - ) + noLoadedRbs = DefaultRuleBasedSegmentsStorage(persistentStorage: createPersistentStorageStub(isParsed: false), generalInfoStorage: generalInfoStorage) noLoadedRbs?.loadLocal() @@ -71,7 +68,7 @@ class RuleBasedSegmentStorageTest: XCTestCase { let customMock = MockPersistentRuleBasedSegmentsStorage() let persistentStorageStub = PersistentRuleBasedSegmentsStorageStub(delegate: customMock) let storage = DefaultRuleBasedSegmentsStorage( - persistentStorage: persistentStorageStub + persistentStorage: persistentStorageStub, generalInfoStorage: generalInfoStorage ) storage.loadLocal() @@ -110,9 +107,8 @@ class RuleBasedSegmentStorageTest: XCTestCase { ) persistentStorageStub = PersistentRuleBasedSegmentsStorageStub(delegate: customMock) - let storage = DefaultRuleBasedSegmentsStorage( - persistentStorage: persistentStorageStub - ) + let storage = DefaultRuleBasedSegmentsStorage(persistentStorage: persistentStorageStub, generalInfoStorage: generalInfoStorage) + storage.loadLocal() // Verify only active segments are loaded @@ -303,7 +299,7 @@ class RuleBasedSegmentStorageTest: XCTestCase { // 1. Counter should be 3 (ignore the other matcherTypes) _ = ruleBasedSegmentsStorage.update(toAdd: Set([segment1, segment2, segment3, segment4, segment5]), toRemove: [], changeNumber: 123) - XCTAssertEqual(ruleBasedSegmentsStorage.segmentsInUse, 3) + XCTAssertEqual(generalInfoStorage.getSegmentsInUse(), 3) // 2 segment1.status = .archived // Archive of Segments with other matcherTypes should be ignored.. @@ -311,7 +307,7 @@ class RuleBasedSegmentStorageTest: XCTestCase { segment3.status = .archived _ = ruleBasedSegmentsStorage.update(toAdd: Set([]), toRemove: [segment1, segment2, segment3], changeNumber: 1230) - XCTAssertEqual(ruleBasedSegmentsStorage.segmentsInUse, 1) + XCTAssertEqual(generalInfoStorage.getSegmentsInUse(), 1) } diff --git a/SplitTests/Storage/SplitsStorageTests.swift b/SplitTests/Storage/SplitsStorageTests.swift index b7ca0a969..f75fd8ad2 100644 --- a/SplitTests/Storage/SplitsStorageTests.swift +++ b/SplitTests/Storage/SplitsStorageTests.swift @@ -20,11 +20,12 @@ class SplitsStorageTest: XCTestCase { var persistentStorage: PersistentSplitsStorageStub! var splitsStorage: SplitsStorage! var noLoadedStorage: DefaultSplitsStorage? + var generalInfoStorage = GeneralInfoStorageMock() override func setUp() { persistentStorage = PersistentSplitsStorageStub() flagSetsCache = FlagSetsCacheMock() - splitsStorage = DefaultSplitsStorage(persistentSplitsStorage: persistentStorage, flagSetsCache: flagSetsCache) + splitsStorage = DefaultSplitsStorage(persistentSplitsStorage: persistentStorage, flagSetsCache: flagSetsCache, GeneralInfoStorage: generalInfoStorage) } func testNoLocalLoaded() { @@ -39,9 +40,7 @@ class SplitsStorageTest: XCTestCase { } func testLazyParsing() { - noLoadedStorage = DefaultSplitsStorage( - persistentSplitsStorage: createPersistentStorageStub(isParsed: false), flagSetsCache: FlagSetsCacheMock() - ) + noLoadedStorage = DefaultSplitsStorage(persistentSplitsStorage: createPersistentStorageStub(isParsed: false), flagSetsCache: FlagSetsCacheMock(), GeneralInfoStorage: generalInfoStorage) noLoadedStorage?.loadLocal() @@ -279,14 +278,12 @@ class SplitsStorageTest: XCTestCase { let flagSetsCache = FlagSetsCacheMock() flagSetsCache.setsInFilter = ["set1", "set2", "set3"] - splitsStorage = DefaultSplitsStorage(persistentSplitsStorage: persistentStorage, flagSetsCache: flagSetsCache) - persistentStorage.snapshot = getTestSnapshot(count: 3, - sets: [ - ["set1", "set2"], - ["set1"], - ["set3"] - ] - ) + splitsStorage = DefaultSplitsStorage(persistentSplitsStorage: persistentStorage, flagSetsCache: flagSetsCache, GeneralInfoStorage: GeneralInfoStorageMock()) + persistentStorage.snapshot = getTestSnapshot(count: 3, sets: [ + ["set1", "set2"], + ["set1"], + ["set3"] + ]) splitsStorage.loadLocal() @@ -326,7 +323,7 @@ class SplitsStorageTest: XCTestCase { splitsStorage.loadLocal() // 1. Check Segments count is in 0 - XCTAssertEqual(splitsStorage.segmentsInUse, 0) + XCTAssertEqual(generalInfoStorage.getSegmentsInUse(), 0) let splitNotUsingSegments = newSplit(name: "added") @@ -336,9 +333,8 @@ class SplitsStorageTest: XCTestCase { changeNumber: 999, updateTimestamp: 888) _ = splitsStorage.update(splitChange: processedChange) - XCTAssertEqual(splitsStorage.segmentsInUse, 5) // One should have been ignored, so 5 + XCTAssertEqual(generalInfoStorage.getSegmentsInUse(), 5) // One should have been ignored, so 5 XCTAssertTrue(persistentStorage.updateCalled) - XCTAssertTrue(persistentStorage.getSegmentsInUseCalled) // 3. Add 2 previously added (should be ignored by the counter), and a new one processedChange = ProcessedSplitChange(activeSplits: [split, split2, split6], @@ -346,7 +342,7 @@ class SplitsStorageTest: XCTestCase { changeNumber: 9999, updateTimestamp: 8888) _ = splitsStorage.update(splitChange: processedChange) - XCTAssertEqual(splitsStorage.segmentsInUse, 6) // So, count should be 6 + XCTAssertEqual(generalInfoStorage.getSegmentsInUse(), 6) // So, count should be 6 // 4. Remove 3 (one not using segments) split2.status = .archived @@ -357,7 +353,7 @@ class SplitsStorageTest: XCTestCase { changeNumber: 99999, updateTimestamp: 88888) _ = splitsStorage.update(splitChange: processedChange) - XCTAssertEqual(splitsStorage.segmentsInUse, 4) // So, count should be 4 + XCTAssertEqual(generalInfoStorage.getSegmentsInUse(), 4) // So, count should be 4 } func testUnsupportedMatcherHasDefaultCondition() { diff --git a/SplitTests/Storage/SplitsStorageTrafficTypesTests.swift b/SplitTests/Storage/SplitsStorageTrafficTypesTests.swift index 6cd9e98ab..39228a116 100644 --- a/SplitTests/Storage/SplitsStorageTrafficTypesTests.swift +++ b/SplitTests/Storage/SplitsStorageTrafficTypesTests.swift @@ -14,6 +14,7 @@ class SplitsStorageTrafficTypesTests: XCTestCase { var splitsStorage: SplitsStorage! var flagSetsCache: FlagSetsCacheMock! + var generalInfoStorage = GeneralInfoStorageMock() override func setUp() { @@ -26,7 +27,7 @@ class SplitsStorageTrafficTypesTests: XCTestCase { flagSetsCache = FlagSetsCacheMock() persistent.snapshot = SplitsSnapshot(changeNumber: 1, splits: splits, updateTimestamp: 100) - splitsStorage = DefaultSplitsStorage(persistentSplitsStorage: persistent, flagSetsCache: flagSetsCache) + splitsStorage = DefaultSplitsStorage(persistentSplitsStorage: persistent, flagSetsCache: flagSetsCache, GeneralInfoStorage: generalInfoStorage) splitsStorage.loadLocal() } diff --git a/SplitTests/Streaming/FeatureFlagsSynchronizerTest.swift b/SplitTests/Streaming/FeatureFlagsSynchronizerTest.swift index a4d816e4a..c53f7e2fa 100644 --- a/SplitTests/Streaming/FeatureFlagsSynchronizerTest.swift +++ b/SplitTests/Streaming/FeatureFlagsSynchronizerTest.swift @@ -18,6 +18,7 @@ class FeatureFlagsSynchronizerTest: XCTestCase { var splitsSyncWorker: RetryableSyncWorkerStub! var periodicSplitsSyncWorker: PeriodicSyncWorkerStub! var persistentSplitsStorage: PersistentSplitsStorageStub! + var database: SplitDatabase! var splitsStorage: SplitsStorageStub! var ruleBasedSegmentsStorage: RuleBasedSegmentsStorageStub! @@ -35,8 +36,8 @@ class FeatureFlagsSynchronizerTest: XCTestCase { override func setUp() { synchronizer = buildSynchronizer() } - - func buildSynchronizer(syncEnabled: Bool = true, splitFilters: [SplitFilter]? = nil) -> FeatureFlagsSynchronizer { + + func buildSynchronizer(syncEnabled: Bool = true, splitFilters: [SplitFilter]? = nil, generalInfoStorage: GeneralInfoStorageMock? = nil, Database: SplitDatabase? = nil) -> FeatureFlagsSynchronizer { eventsManager = SplitEventsManagerStub() persistentSplitsStorage = PersistentSplitsStorageStub() @@ -49,13 +50,22 @@ class FeatureFlagsSynchronizerTest: XCTestCase { splitsStorage = SplitsStorageStub() ruleBasedSegmentsStorage = RuleBasedSegmentsStorageStub() broadcasterChannel = SyncEventBroadcasterStub() - generalInfoStorage = GeneralInfoStorageMock() + + if generalInfoStorage == nil { + self.generalInfoStorage = GeneralInfoStorageMock() + } + + if Database == nil { + self.database = TestingHelper.createTestDatabase(name: "pepe") + } + telemetryStorageStub = TelemetryStorageStub() _ = splitsStorage.update(splitChange: ProcessedSplitChange(activeSplits: [], archivedSplits: [], changeNumber: 100, updateTimestamp: 100)) - generalInfoStorage.setFlagSpec(flagsSpec: "1.2") + + self.generalInfoStorage.setFlagSpec(flagsSpec: "1.2") - let storageContainer = SplitStorageContainer(splitDatabase: TestingHelper.createTestDatabase(name: "pepe"), + let storageContainer = SplitStorageContainer(splitDatabase: database, splitsStorage: splitsStorage, persistentSplitsStorage: persistentSplitsStorage, impressionsStorage: ImpressionsStorageStub(), @@ -71,7 +81,7 @@ class FeatureFlagsSynchronizerTest: XCTestCase { flagSetsCache: FlagSetsCacheMock(), persistentHashedImpressionsStorage: PersistentHashedImpressionStorageMock(), hashedImpressionsStorage: HashedImpressionsStorageMock(), - generalInfoStorage: generalInfoStorage, + generalInfoStorage: self.generalInfoStorage, ruleBasedSegmentsStorage: ruleBasedSegmentsStorage, persistentRuleBasedSegmentsStorage: PersistentRuleBasedSegmentsStorageStub()) @@ -265,6 +275,7 @@ class FeatureFlagsSynchronizerTest: XCTestCase { ThreadUtils.delay(seconds: 0.5) + XCTAssertTrue(persistentSplitsStorage.clearCalled) XCTAssertTrue(persistentSplitsStorage.clearCalled) XCTAssertEqual(1, broadcasterChannel.pushedEvents.filter { $0 == .splitLoadedFromCache }.count) } @@ -340,4 +351,15 @@ class FeatureFlagsSynchronizerTest: XCTestCase { XCTAssertTrue(sw2.stopCalled) XCTAssertEqual(0, updateWorkerCatalog.count) } + + func testForceParcing() { + var generalInfoStorage = GeneralInfoStorageMock() + + synchronizer = buildSynchronizer(generalInfoStorage: GeneralInfoStorageMock()) // SegmentsInUse = nil, changeNumber = 100 + synchronizer.load() + + ThreadUtils.delay(seconds: 2) + + XCTAssertTrue(splitsStorage.forceReparsingCalled) + } }