diff --git a/Sources/TrackerRadarKit/TrackerData.swift b/Sources/TrackerRadarKit/TrackerData.swift index 614d025..0b7cdc6 100644 --- a/Sources/TrackerRadarKit/TrackerData.swift +++ b/Sources/TrackerRadarKit/TrackerData.swift @@ -174,6 +174,18 @@ public struct KnownTracker: Codable, Equatable { self.rules = rules } + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + domain = try? container.decode(String.self, forKey: .domain) + defaultAction = try? container.decode(KnownTracker.ActionType.self, forKey: .defaultAction) + owner = try? container.decode(KnownTracker.Owner.self, forKey: .owner) + prevalence = try? container.decode(Double.self, forKey: .prevalence) + subdomains = try? container.decode([String]?.self, forKey: .subdomains) + categories = try? container.decode([String].self, forKey: .categories) + + let customRules = try? container.decode([OptionalValue].self, forKey: .rules) + rules = customRules?.compactMap { $0.value } as? [KnownTracker.Rule] + } } extension KnownTracker { @@ -205,3 +217,16 @@ public struct Entity: Codable, Hashable { } } + +private struct OptionalValue: Decodable { + public let value: Value? + + public init(from decoder: Decoder) throws { + do { + let container = try decoder.singleValueContainer() + self.value = try container.decode(Value.self) + } catch { + self.value = nil + } + } +} diff --git a/Tests/TrackerRadarKitTests/ContentBlockerRulesBuilderTests.swift b/Tests/TrackerRadarKitTests/ContentBlockerRulesBuilderTests.swift index 226a441..19b3fa9 100644 --- a/Tests/TrackerRadarKitTests/ContentBlockerRulesBuilderTests.swift +++ b/Tests/TrackerRadarKitTests/ContentBlockerRulesBuilderTests.swift @@ -47,6 +47,29 @@ class ContentBlockerRulesBuilderTests: XCTestCase { } } + func testLoadingUnsupportedRules() throws { + let data = JSONTestDataLoader.mockTrackerData + guard let mockData = try? JSONDecoder().decode(TrackerData.self, from: data) else { + XCTFail("Failed to decode tracker data") + return + } + + guard let tracker = mockData.findTracker(byCname: "tracker-4.com") else { + XCTFail("Failed to find tracker") + return + } + + let expectedNumberOfRules = 1 + XCTAssertEqual(tracker.rules?.count, expectedNumberOfRules) + } + + func testTrackerDataParserPerformance () { + let data = JSONTestDataLoader.trackerData + measure { + _ = try? JSONDecoder().decode(TrackerData.self, from: data) + } + } + func testLoadingRulesIsDeterministic() { let firstGeneration = ContentBlockerRulesBuilder(trackerData: trackerData).buildRules( withExceptions: [], diff --git a/Tests/TrackerRadarKitTests/Resources/mockTrackerData.json b/Tests/TrackerRadarKitTests/Resources/mockTrackerData.json index 673da44..04314c4 100644 --- a/Tests/TrackerRadarKitTests/Resources/mockTrackerData.json +++ b/Tests/TrackerRadarKitTests/Resources/mockTrackerData.json @@ -86,6 +86,30 @@ "cookies": 0.01 } ] + }, + "tracker-4.com": { + "domain": "tracker-4.com", + "owner": { + "name": "Test Site for Tracker Blocking", + "displayName": "Bad Third Party Site", + "privacyPolicy": "", + "url": "http://tracker.test" + }, + "prevalence": 0.1, + "fingerprinting": 3, + "cookies": 0.1, + "categories": [], + "default": "block", + "rules": [ + { + "action": "ignore", + "rule": "tracker-4\\.com\\/breakage" + }, + { + "action": "unsupported-action", + "rule": "tracker-4\\.com\\/unsupported-action" + } + ] } }, "entities": { @@ -109,16 +133,25 @@ ], "prevalence": 1, "displayName": "Tracker 3" + }, + "Tracker 4, Inc.": { + "domains": [ + "tracker-4.com" + ], + "prevalence": 1, + "displayName": "Tracker 4" } }, "domains": { "tracker-1.com": "Tracker 1", "tracker-2.com": "Tracker 2", - "tracker-3.com": "Tracker 3" + "tracker-3.com": "Tracker 3", + "tracker-4.com": "Tracker 4" }, "cnames": { "tracker-1.com": "cname.tracker-1.com", "tracker-2.com": "cname.tracker-2.com", "tracker-3.com": "cname.tracker-3.com", + "tracker-4.com": "cname.tracker-4.com" } }