Skip to content

Commit

Permalink
make tds depend on privacy config
Browse files Browse the repository at this point in the history
  • Loading branch information
SabrinaTardio committed Jan 10, 2025
1 parent 724a3bd commit ace4e47
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 7 deletions.
6 changes: 6 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,8 @@
56BA1E802BAB2E43001CF69F /* ErrorPageTabExtensionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BA1E7C2BAB290E001CF69F /* ErrorPageTabExtensionTest.swift */; };
56BA1E8A2BB1CB5B001CF69F /* CertificateTrustEvaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BA1E892BB1CB5B001CF69F /* CertificateTrustEvaluator.swift */; };
56BA1E8B2BB1CB5B001CF69F /* CertificateTrustEvaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BA1E892BB1CB5B001CF69F /* CertificateTrustEvaluator.swift */; };
56BC8F012D312B320046059D /* ConfigurationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BC8F002D312B320046059D /* ConfigurationManagerTests.swift */; };
56BC8F022D312B320046059D /* ConfigurationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BC8F002D312B320046059D /* ConfigurationManagerTests.swift */; };
56CE77612C7DFCF800AC1ED2 /* OnboardingSuggestedSearchesProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56CE77602C7DFCF800AC1ED2 /* OnboardingSuggestedSearchesProviderTests.swift */; };
56CE77622C7DFCF800AC1ED2 /* OnboardingSuggestedSearchesProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56CE77602C7DFCF800AC1ED2 /* OnboardingSuggestedSearchesProviderTests.swift */; };
56CEE90E2B7A725B00CF10AA /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 56CEE90D2B7A6DE100CF10AA /* InfoPlist.xcstrings */; };
Expand Down Expand Up @@ -4119,6 +4121,7 @@
56BA1E742BAAF70F001CF69F /* SpecialErrorPageTabExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpecialErrorPageTabExtension.swift; sourceTree = "<group>"; };
56BA1E7C2BAB290E001CF69F /* ErrorPageTabExtensionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorPageTabExtensionTest.swift; sourceTree = "<group>"; };
56BA1E892BB1CB5B001CF69F /* CertificateTrustEvaluator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateTrustEvaluator.swift; sourceTree = "<group>"; };
56BC8F002D312B320046059D /* ConfigurationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationManagerTests.swift; sourceTree = "<group>"; };
56CE77602C7DFCF800AC1ED2 /* OnboardingSuggestedSearchesProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingSuggestedSearchesProviderTests.swift; sourceTree = "<group>"; };
56CEE9092B7A66C500CF10AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
56CEE90D2B7A6DE100CF10AA /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -7480,6 +7483,7 @@
isa = PBXGroup;
children = (
85AC3B4825DAC9BD00C7D2AA /* ConfigurationStorageTests.swift */,
56BC8F002D312B320046059D /* ConfigurationManagerTests.swift */,
);
path = Configuration;
sourceTree = "<group>";
Expand Down Expand Up @@ -12435,6 +12439,7 @@
3706FE2F293F661700E42796 /* WebViewMock.swift in Sources */,
3706FE30293F661700E42796 /* CollectionExtension.swift in Sources */,
B630E80129C887ED00363609 /* NSErrorAdditionalInfo.swift in Sources */,
56BC8F012D312B320046059D /* ConfigurationManagerTests.swift in Sources */,
3706FE31293F661700E42796 /* TabCollectionViewModelDelegateMock.swift in Sources */,
3706FE32293F661700E42796 /* BookmarksHTMLReaderTests.swift in Sources */,
9F0FFFBC2BCCAEC2007C87DD /* AddEditBookmarkFolderDialogViewModelMock.swift in Sources */,
Expand Down Expand Up @@ -14269,6 +14274,7 @@
B6CA4824298CDC2E0067ECCE /* AdClickAttributionTabExtensionTests.swift in Sources */,
AAEC74B22642C57200C2EFBC /* HistoryCoordinatingMock.swift in Sources */,
37D046A12C7DA9A200AEAA50 /* UserBackgroundImagesManagerTests.swift in Sources */,
56BC8F022D312B320046059D /* ConfigurationManagerTests.swift in Sources */,
56A214AF2CB583BF00E5BC0E /* TrackerMessageProviderTests.swift in Sources */,
37CD54B927F1F8AC00F1F7B9 /* AppearancePreferencesTests.swift in Sources */,
EEF53E182950CED5002D78F4 /* JSAlertViewModelTests.swift in Sources */,
Expand Down
59 changes: 52 additions & 7 deletions DuckDuckGo/Configuration/ConfigurationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import PixelKit

final class ConfigurationManager: DefaultConfigurationManager {

private var trackerDataManager: TrackerDataManager?
private var privacyConfigurationManager: PrivacyConfigurationManaging?
private var contentBlockingManager: ContentBlockerRulesManagerProtocol?

private enum Constants {
static let lastConfigurationInstallDateKey = "config.last.installed"
}
Expand All @@ -44,6 +48,7 @@ final class ConfigurationManager: DefaultConfigurationManager {
}

static let configurationDebugEvents = EventMapping<ConfigurationDebugEvents> { event, error, _, _ in
print("🚨 configurationDebugEvents initialized")
let domainEvent: GeneralPixel
switch event {
case .invalidPayload(let configuration):
Expand All @@ -56,6 +61,7 @@ final class ConfigurationManager: DefaultConfigurationManager {
override init(fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents),
store: ConfigurationStoring = ConfigurationStore(),
defaults: KeyValueStoring = UserDefaults.appConfiguration) {
print("🚨 Initializing ConfigurationManager")
self.defaults = defaults
super.init(fetcher: fetcher, store: store, defaults: defaults)
}
Expand Down Expand Up @@ -107,10 +113,31 @@ final class ConfigurationManager: DefaultConfigurationManager {
private func fetchTrackerBlockingDependencies(isDebug: Bool) async -> Bool {
var didFetchAnyTrackerBlockingDependencies = false

var tasks = [Configuration: Task<(), Swift.Error>]()
tasks[.trackerDataSet] = Task { try await fetcher.fetch(.trackerDataSet, isDebug: isDebug) }
tasks[.surrogates] = Task { try await fetcher.fetch(.surrogates, isDebug: isDebug) }
tasks[.privacyConfiguration] = Task { try await fetcher.fetch(.privacyConfiguration, isDebug: isDebug) }
// Start surrogates fetch task
let surrogatesTask = Task { try await fetcher.fetch(.surrogates, isDebug: isDebug) }

// Perform privacyConfiguration fetch and update
do {
try await fetcher.fetch(.privacyConfiguration, isDebug: isDebug)
didFetchAnyTrackerBlockingDependencies = true
let privacyConfigurationManager = self.privacyConfigurationManager ?? ContentBlocking.shared.privacyConfigurationManager
privacyConfigurationManager.reload(etag: store.loadEtag(for: .privacyConfiguration),
data: store.loadData(for: .privacyConfiguration))
} catch {
Logger.config.error(
"Failed to complete configuration update to \(Configuration.privacyConfiguration.rawValue, privacy: .public): \(error.localizedDescription, privacy: .public)"
)
tryAgainSoon()
}

// Start trackerDataSet fetch task after privacyConfiguration completes
let trackerDataSetTask = Task { try await fetcher.fetch(.trackerDataSet, isDebug: isDebug) }

// Wait for surrogates and trackerDataSet tasks
let tasks: [(Configuration, Task<(), Swift.Error>)] = [
(.surrogates, surrogatesTask),
(.trackerDataSet, trackerDataSetTask)
]

for (configuration, task) in tasks {
do {
Expand Down Expand Up @@ -141,11 +168,15 @@ final class ConfigurationManager: DefaultConfigurationManager {

private func updateTrackerBlockingDependencies() {
lastConfigurationInstallDate = Date()
ContentBlocking.shared.trackerDataManager.reload(etag: store.loadEtag(for: .trackerDataSet),
let trackerDataManager = self.trackerDataManager ?? ContentBlocking.shared.trackerDataManager
let privacyConfigurationManager = self.privacyConfigurationManager ?? ContentBlocking.shared.privacyConfigurationManager
let contentBlockingManager = self.contentBlockingManager ?? ContentBlocking.shared.contentBlockingManager

trackerDataManager.reload(etag: store.loadEtag(for: .trackerDataSet),
data: store.loadData(for: .trackerDataSet))
ContentBlocking.shared.privacyConfigurationManager.reload(etag: store.loadEtag(for: .privacyConfiguration),
privacyConfigurationManager.reload(etag: store.loadEtag(for: .privacyConfiguration),
data: store.loadData(for: .privacyConfiguration))
ContentBlocking.shared.contentBlockingManager.scheduleCompilation()
contentBlockingManager.scheduleCompilation()
}

private func updateBloomFilter() async throws {
Expand Down Expand Up @@ -199,3 +230,17 @@ extension ConfigurationManager {
updateTrackerBlockingDependencies()
}
}

#if DEBUG
extension ConfigurationManager {
func setContentBlockingManagers(trackerDataManager: TrackerDataManager,
privacyConfigurationManager: PrivacyConfigurationManager,
contentBlockingManager: ContentBlockerRulesManagerProtocol) {
if Application.runType == .unitTests {
self.trackerDataManager = trackerDataManager
self.privacyConfigurationManager = privacyConfigurationManager
self.contentBlockingManager = contentBlockingManager
}
}
}
#endif
159 changes: 159 additions & 0 deletions UnitTests/Configuration/ConfigurationManagerTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//
// ConfigurationManagerTests.swift
//
// Copyright © 2025 DuckDuckGo. All rights reserved.
//
// 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 XCTest
import Configuration
@testable import BrowserServicesKit
@testable import DuckDuckGo_Privacy_Browser
import Combine
import TrackerRadarKit

final class ConfigurationManagerTests: XCTestCase {
private var operationLog: [ConfigurationStep] = []
private var sut: ConfigurationManager!
private var mockFetcher: MockConfigurationFetcher!
private var mockStore: MockConfigurationStore!
private var mockTrackerDataManager: MockTrackerDataManager!
private var mockPrivacyConfigManager: MockPrivacyConfigurationManager!
private var mockContentBlockingManager: MockContentBlockerRulesManager!

override func setUpWithError() throws {
let userDefaults = UserDefaults(suiteName: "ConfigurationManagerTests")!
userDefaults.removePersistentDomain(forName: "ConfigurationManagerTests")
mockFetcher = MockConfigurationFetcher(operationLog: &operationLog)
mockPrivacyConfigManager = MockPrivacyConfigurationManager(fetchedETag: nil, fetchedData: nil, embeddedDataProvider: MockEmbeddedDataProvider(), localProtection: MockDomainsProtectionStore(), internalUserDecider: DefaultInternalUserDecider())
mockPrivacyConfigManager.operationLog = operationLog
mockTrackerDataManager = MockTrackerDataManager(etag: nil, data: nil, embeddedDataProvider: MockEmbeddedDataProvider())
mockContentBlockingManager = MockContentBlockerRulesManager()
sut = ConfigurationManager(fetcher: mockFetcher, store: mockStore, defaults: userDefaults)
sut.setContentBlockingManagers(
trackerDataManager: mockTrackerDataManager,
privacyConfigurationManager: mockPrivacyConfigManager,
contentBlockingManager: mockContentBlockingManager
)
}

override func tearDownWithError() throws {
operationLog = []
sut = nil
mockStore = nil
mockFetcher = nil
mockTrackerDataManager = nil
mockPrivacyConfigManager = nil
mockContentBlockingManager = nil
}

func testPrivacyConfigFetchAndReloadBeforeTrackerDataSetFetch() async {
// GIVEN
let expectedOrder: [ConfigurationStep] = [
.fetchSurrogates,
.fetchPrivacyConfig,
.reloadPrivacyConfig,
.fetchTrackerDataSet
]

// WHEN
await sut.refreshNow(isDebug: false)

XCTAssertEqual(operationLog, expectedOrder, "Operations did not occur in the expected order.")
}

}

// Step enum to track operations
private enum ConfigurationStep: String, Equatable {
case fetchSurrogates
case fetchPrivacyConfig
case reloadPrivacyConfig
case fetchTrackerDataSet
}

private class MockConfigurationFetcher: ConfigurationFetching {
var operationLog: [ConfigurationStep]
var shouldFailPrivacyFetch = false
var shouldFailSurrogatesFetch = false
var shouldFailTdsFetch = false

init(operationLog: inout [ConfigurationStep]) {
self.operationLog = operationLog
}

func fetch(_ configuration: Configuration, isDebug: Bool) async throws {
switch configuration {
case .bloomFilterBinary:
break
case .bloomFilterSpec:
break
case .bloomFilterExcludedDomains:
break
case .privacyConfiguration:
operationLog.append(.fetchPrivacyConfig)
if shouldFailPrivacyFetch {
throw NSError(domain: "TestError", code: 1, userInfo: nil)
}
try await Task.sleep(nanoseconds: 50_000_000)
case .surrogates:
operationLog.append(.fetchTrackerDataSet)
if shouldFailSurrogatesFetch {
throw NSError(domain: "TestError", code: 1, userInfo: nil)
}
case .trackerDataSet:
operationLog.append(.fetchTrackerDataSet)
if shouldFailTdsFetch {
throw NSError(domain: "TestError", code: 1, userInfo: nil)
}
case .remoteMessagingConfig:
break
}
}

func fetch(all configurations: [Configuration]) async throws {}
}

private class MockPrivacyConfigurationManager: PrivacyConfigurationManager {
var operationLog: [ConfigurationStep] = []

override func reload(etag: String?, data: Data?) -> ReloadResult {
operationLog.append(.reloadPrivacyConfig)
return .embedded
}
}

class MockTrackerDataManager: TrackerDataManager {
func reload(etag: String?, data: Data?) {}
}

class MockContentBlockerRulesManager: ContentBlockerRulesManagerProtocol {
var updatesPublisher: AnyPublisher<ContentBlockerRulesManager.UpdateEvent, Never> = Empty<ContentBlockerRulesManager.UpdateEvent, Never>().eraseToAnyPublisher()

var currentRules: [ContentBlockerRulesManager.Rules] = []

func scheduleCompilation() -> ContentBlockerRulesManager.CompletionToken {
return ""
}

var currentMainRules: ContentBlockerRulesManager.Rules?

var currentAttributionRules: BrowserServicesKit.ContentBlockerRulesManager.Rules?

func entity(forHost host: String) -> Entity? {
return nil
}

func scheduleCompilation() {}
}

0 comments on commit ace4e47

Please sign in to comment.