From 495985d3e18ccdae0d51f734c85dc0e3ed63e166 Mon Sep 17 00:00:00 2001 From: amddg44 Date: Sun, 8 Oct 2023 14:03:21 +0200 Subject: [PATCH] Removes iOS phased rollout dry run pixel (#2064) Task/Issue URL: https://app.asana.com/0/0/1205515530563288/f Tech Design URL: CC: Description: Removes the incremental feature flag tester and associated pixel & user default key --- Core/PixelEvent.swift | 3 - DuckDuckGo.xcodeproj/project.pbxproj | 10 +- .../xcshareddata/swiftpm/Package.resolved | 6 +- DuckDuckGo/AppDelegate.swift | 12 +- .../PhasedRolloutFeatureFlagTester.swift | 84 ---------- .../PhasedRolloutFeatureFlagTesterTests.swift | 153 ------------------ 6 files changed, 9 insertions(+), 259 deletions(-) delete mode 100644 DuckDuckGo/PhasedRolloutFeatureFlagTester.swift delete mode 100644 DuckDuckGoTests/PhasedRolloutFeatureFlagTesterTests.swift diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index d050a4d9e3..1b1193ddc2 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -502,7 +502,6 @@ extension Pixel { case emailIncontextModalExitEarlyContinue case compilationFailed - case incrementalRolloutTest } } @@ -985,8 +984,6 @@ extension Pixel.Event { case .debugReturnUserAddATB: return "m_debug_return_user_add_atb" case .debugReturnUserReadATB: return "m_debug_return_user_read_atb" case .debugReturnUserUpdateATB: return "m_debug_return_user_update_atb" - - case .incrementalRolloutTest: return "m_autofill_incremental_rollout_test" } } diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 7f1d4a3cab..bc8e986dc4 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -696,9 +696,7 @@ C18ED43C2AB8364400BF3805 /* FileTextPreviewDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C18ED43B2AB8364400BF3805 /* FileTextPreviewDebugViewController.swift */; }; C18ED43A2AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C18ED4392AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift */; }; C1963863283794A000298D4D /* BookmarksCachingSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1963862283794A000298D4D /* BookmarksCachingSearch.swift */; }; - C1B0F63E2AB08904001EAF05 /* PhasedRolloutFeatureFlagTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B0F63D2AB08904001EAF05 /* PhasedRolloutFeatureFlagTester.swift */; }; C1B0F6422AB08BE9001EAF05 /* MockPrivacyConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B0F6412AB08BE9001EAF05 /* MockPrivacyConfiguration.swift */; }; - C1B0F6442AB0D47A001EAF05 /* PhasedRolloutFeatureFlagTesterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B0F63F2AB08A53001EAF05 /* PhasedRolloutFeatureFlagTesterTests.swift */; }; C1B7B51C28941E980098FD6A /* HomeMessageViewModelBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B7B51B28941E980098FD6A /* HomeMessageViewModelBuilder.swift */; }; C1B7B52328941F2A0098FD6A /* RemoteMessagingStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B7B51F28941F2A0098FD6A /* RemoteMessagingStore.swift */; }; C1B7B52428941F2A0098FD6A /* RemoteMessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B7B52028941F2A0098FD6A /* RemoteMessageRequest.swift */; }; @@ -2270,8 +2268,6 @@ C18ED43B2AB8364400BF3805 /* FileTextPreviewDebugViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileTextPreviewDebugViewController.swift; sourceTree = ""; }; C18ED4392AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillSettingsEnableFooterView.swift; sourceTree = ""; }; C1963862283794A000298D4D /* BookmarksCachingSearch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksCachingSearch.swift; sourceTree = ""; }; - C1B0F63D2AB08904001EAF05 /* PhasedRolloutFeatureFlagTester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhasedRolloutFeatureFlagTester.swift; sourceTree = ""; }; - C1B0F63F2AB08A53001EAF05 /* PhasedRolloutFeatureFlagTesterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhasedRolloutFeatureFlagTesterTests.swift; sourceTree = ""; }; C1B0F6412AB08BE9001EAF05 /* MockPrivacyConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPrivacyConfiguration.swift; sourceTree = ""; }; C1B7B51B28941E980098FD6A /* HomeMessageViewModelBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeMessageViewModelBuilder.swift; sourceTree = ""; }; C1B7B51F28941F2A0098FD6A /* RemoteMessagingStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteMessagingStore.swift; sourceTree = ""; }; @@ -5148,7 +5144,6 @@ C1BF0BA629B63E0400482B73 /* AutofillLoginUI */, F40F843528C938370081AE75 /* AutofillLoginListViewModelTests.swift */, C1D21E2E293A599C006E5A05 /* AutofillLoginSessionTests.swift */, - C1B0F63F2AB08A53001EAF05 /* PhasedRolloutFeatureFlagTesterTests.swift */, ); name = Autofill; sourceTree = ""; @@ -5167,7 +5162,6 @@ C17B59552A03AAC40055F2D1 /* PasswordGeneration */, 31951E9328230D8900CAF535 /* Shared */, F407605428131923006B1E0B /* SaveLogin */, - C1B0F63D2AB08904001EAF05 /* PhasedRolloutFeatureFlagTester.swift */, ); name = Autofill; sourceTree = ""; @@ -6354,7 +6348,6 @@ 98B31292218CCB8C00E54DE1 /* AppDependencyProvider.swift in Sources */, 02C57C4B2514FEFB009E5129 /* DoNotSellSettingsViewController.swift in Sources */, 02A54A9C2A097C95000C8FED /* AppTPHomeViewSectionRenderer.swift in Sources */, - C1B0F63E2AB08904001EAF05 /* PhasedRolloutFeatureFlagTester.swift in Sources */, 8540BBA22440857A00017FE4 /* PreserveLoginsWorker.swift in Sources */, 85DFEDF924CF3D0E00973FE7 /* TabsBarCell.swift in Sources */, F17922DB1E717C8D006E3D97 /* Suggestion.swift in Sources */, @@ -6512,7 +6505,6 @@ 987130C5294AAB9F00AB05E0 /* BookmarkEditorViewModelTests.swift in Sources */, 8341D807212D5E8D000514C2 /* HashExtensionTest.swift in Sources */, C1D21E2F293A599C006E5A05 /* AutofillLoginSessionTests.swift in Sources */, - C1B0F6442AB0D47A001EAF05 /* PhasedRolloutFeatureFlagTesterTests.swift in Sources */, 85D2187924BF6B8B004373D2 /* FaviconSourcesProviderTests.swift in Sources */, 1E8146AD28C8ABF000D1AF63 /* TrackerAnimationLogicTests.swift in Sources */, B6AD9E3A28D456820019CDE9 /* PrivacyConfigurationManagerMock.swift in Sources */, @@ -8970,7 +8962,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 81.0.0; + version = 81.1.0; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4a2755a27d..ce52c2c554 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/DuckDuckGo/BrowserServicesKit", "state": { "branch": null, - "revision": "5a77dc747a1bee25947b1d3ae3831922be05fd22", - "version": "81.0.0" + "revision": "43fd829917f61f605fc110b68bb1bf1841276b3c", + "version": "81.1.0" } }, { @@ -156,7 +156,7 @@ }, { "package": "TrackerRadarKit", - "repositoryURL": "https://github.com/duckduckgo/TrackerRadarKit", + "repositoryURL": "https://github.com/duckduckgo/TrackerRadarKit.git", "state": { "branch": null, "revision": "4684440d03304e7638a2c8086895367e90987463", diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 3aa43b1fd5..c41c64d7ce 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -82,6 +82,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Can be removed after a couple of versions cleanUpMacPromoExperiment2() + cleanUpIncrementalRolloutPixelTest() APIRequest.Headers.setUserAgent(DefaultUserAgentManager.duckDuckGoUserAgent) Configuration.setURLProvider(AppConfigurationURLProvider()) @@ -246,6 +247,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { UserDefaults.standard.removeObject(forKey: "com.duckduckgo.ios.macPromoMay23.exp2.cohort") } + private func cleanUpIncrementalRolloutPixelTest() { + UserDefaults.standard.removeObject(forKey: "network-protection.incremental-feature-flag-test.has-sent-pixel") + } + private func clearTmp() { let tmp = FileManager.default.temporaryDirectory do { @@ -264,12 +269,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { }) } - // Temporary feature flag tester, to validate that phased rollouts are working as intended. - // This is to be removed before the end of August 2023. - lazy var featureFlagTester: PhasedRolloutFeatureFlagTester = { - return PhasedRolloutFeatureFlagTester() - }() - func applicationDidBecomeActive(_ application: UIApplication) { guard !testing else { return } @@ -323,7 +322,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { syncService.scheduler.notifyAppLifecycleEvent() fireFailedCompilationsPixelIfNeeded() - featureFlagTester.sendFeatureFlagEnabledPixelIfNecessary() } private func fireAppLaunchPixel() { diff --git a/DuckDuckGo/PhasedRolloutFeatureFlagTester.swift b/DuckDuckGo/PhasedRolloutFeatureFlagTester.swift deleted file mode 100644 index 12e94300dd..0000000000 --- a/DuckDuckGo/PhasedRolloutFeatureFlagTester.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// PhasedRolloutFeatureFlagTester.swift -// DuckDuckGo -// -// Copyright © 2023 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 Foundation -import BrowserServicesKit -import Core - -protocol PhasedRolloutPixelSender: AnyObject { - func sendPixel(completion: @escaping (Error?) -> Void) -} - -final class DefaultPhasedRolloutPixelSender: PhasedRolloutPixelSender { - func sendPixel(completion: @escaping (Error?) -> Void) { - Pixel.fire(pixel: .incrementalRolloutTest) { error in - completion(error) - } - } -} - -final class PhasedRolloutFeatureFlagTester { - - enum Constants { - static let hasSentPixelKey = "network-protection.incremental-feature-flag-test.has-sent-pixel" - } - - static let shared = PhasedRolloutFeatureFlagTester() - - private let privacyConfigurationManager: PrivacyConfigurationManaging - private let pixelSender: PhasedRolloutPixelSender - private let userDefaults: UserDefaults - - init(privacyConfigurationManager: PrivacyConfigurationManaging = ContentBlocking.shared.privacyConfigurationManager, - pixelSender: PhasedRolloutPixelSender = DefaultPhasedRolloutPixelSender(), - userDefaults: UserDefaults = .standard) { - self.privacyConfigurationManager = privacyConfigurationManager - self.pixelSender = pixelSender - self.userDefaults = userDefaults - } - - func sendFeatureFlagEnabledPixelIfNecessary(completion: (() -> Void)? = nil) { - guard !hasSentPixelBefore(), privacyConfigurationManager.privacyConfig.isSubfeatureEnabled(IncrementalRolloutTestSubfeature2.rollout) else { - completion?() - return - } - - markPixelAsSent() - - pixelSender.sendPixel { [weak self] error in - if error != nil { - self?.markPixelAsUnsent() - } - - completion?() - } - } - - private func hasSentPixelBefore() -> Bool { - return userDefaults.bool(forKey: Constants.hasSentPixelKey) - } - - private func markPixelAsSent() { - userDefaults.setValue(true, forKey: Constants.hasSentPixelKey) - } - - private func markPixelAsUnsent() { - userDefaults.removeObject(forKey: Constants.hasSentPixelKey) - } - -} diff --git a/DuckDuckGoTests/PhasedRolloutFeatureFlagTesterTests.swift b/DuckDuckGoTests/PhasedRolloutFeatureFlagTesterTests.swift deleted file mode 100644 index f087433c69..0000000000 --- a/DuckDuckGoTests/PhasedRolloutFeatureFlagTesterTests.swift +++ /dev/null @@ -1,153 +0,0 @@ -// -// PhasedRolloutFeatureFlagTesterTests.swift -// DuckDuckGo -// -// Copyright © 2023 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 BrowserServicesKit -@testable import DuckDuckGo - -private class MockPixelSender: PhasedRolloutPixelSender { - - enum MockPixelSenderError: Error { - case mockPixelSenderError - } - - let returnError: Bool - var receivedPixelCall: Bool = false - - init(returnError: Bool = false) { - self.returnError = returnError - } - - func sendPixel(completion: @escaping (Error?) -> Void) { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - self.receivedPixelCall = true - - if self.returnError { - completion(MockPixelSenderError.mockPixelSenderError) - } else { - completion(nil) - } - } - } - -} - -final class PhasedRolloutFeatureFlagTesterTests: XCTestCase { - - private static let testSuiteName = "\(#file)" - - override func setUp() { - super.setUp() - - let defaults = UserDefaults(suiteName: Self.testSuiteName)! - defaults.removePersistentDomain(forName: Self.testSuiteName) - } - - override func tearDown() { - super.tearDown() - - let defaults = UserDefaults(suiteName: Self.testSuiteName)! - defaults.removePersistentDomain(forName: Self.testSuiteName) - } - - func testWhenAttemptingToSendPixel_AndRolloutSubfeatureIsDisabled_ThenPixelDoesNotSend() { - let mockManager = MockPrivacyConfigurationManager() - mockManager.privacyConfig = mockConfiguration(subfeatureEnabled: false) - let pixelSender = MockPixelSender(returnError: false) - let userDefaults = UserDefaults(suiteName: Self.testSuiteName)! - - let tester = PhasedRolloutFeatureFlagTester(privacyConfigurationManager: mockManager, - pixelSender: pixelSender, - userDefaults: userDefaults) - - let expectation = expectation(description: #function) - tester.sendFeatureFlagEnabledPixelIfNecessary { - expectation.fulfill() - } - wait(for: [expectation]) - - XCTAssertFalse(pixelSender.receivedPixelCall) - XCTAssertFalse(userDefaults.bool(forKey: PhasedRolloutFeatureFlagTester.Constants.hasSentPixelKey)) - } - - func testWhenAttemptingToSendPixel_AndRolloutSubfeatureIsEnabled_AndPixelHasNotBeenSentBefore_ThenPixelSends() { - let mockManager = MockPrivacyConfigurationManager() - mockManager.privacyConfig = mockConfiguration(subfeatureEnabled: true) - let pixelSender = MockPixelSender(returnError: false) - let userDefaults = UserDefaults(suiteName: Self.testSuiteName)! - - let tester = PhasedRolloutFeatureFlagTester(privacyConfigurationManager: mockManager, - pixelSender: pixelSender, - userDefaults: userDefaults) - - let expectation = expectation(description: #function) - tester.sendFeatureFlagEnabledPixelIfNecessary { - expectation.fulfill() - } - wait(for: [expectation]) - - XCTAssert(pixelSender.receivedPixelCall) - XCTAssert(userDefaults.bool(forKey: PhasedRolloutFeatureFlagTester.Constants.hasSentPixelKey)) - } - - func testWhenAttemptingToSendPixel_AndRolloutSubfeatureIsEnabled_AndPixelHasBeenSentBefore_ThenPixelDoesNotSend() { - let mockManager = MockPrivacyConfigurationManager() - mockManager.privacyConfig = mockConfiguration(subfeatureEnabled: true) - let pixelSender = MockPixelSender(returnError: false) - let userDefaults = UserDefaults(suiteName: Self.testSuiteName)! - - let tester = PhasedRolloutFeatureFlagTester(privacyConfigurationManager: mockManager, - pixelSender: pixelSender, - userDefaults: userDefaults) - - let firstExpectation = expectation(description: #function) - tester.sendFeatureFlagEnabledPixelIfNecessary { - firstExpectation.fulfill() - } - wait(for: [firstExpectation]) - - XCTAssert(pixelSender.receivedPixelCall) - XCTAssert(userDefaults.bool(forKey: PhasedRolloutFeatureFlagTester.Constants.hasSentPixelKey)) - - // Test the second call: - - pixelSender.receivedPixelCall = false - - let secondExpectation = expectation(description: #function) - tester.sendFeatureFlagEnabledPixelIfNecessary { - secondExpectation.fulfill() - } - wait(for: [secondExpectation]) - - XCTAssertFalse(pixelSender.receivedPixelCall) - XCTAssert(userDefaults.bool(forKey: PhasedRolloutFeatureFlagTester.Constants.hasSentPixelKey)) - } - - // MARK: - Mock Creation - - private func mockConfiguration(subfeatureEnabled: Bool) -> PrivacyConfiguration { - let mockPrivacyConfiguration = MockPrivacyConfiguration() - mockPrivacyConfiguration.isSubfeatureKeyEnabled = { _, _ in - return subfeatureEnabled - } - - return mockPrivacyConfiguration - } - -}