/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @testable import Shared import XCTest class FeatureSwitchTests: XCTestCase { let buildChannel = AppConstants.BuildChannel func testPersistent() { let featureSwitch = FeatureSwitch(named: "test-persistent-over-restarts", allowPercentage: 50, buildChannel: buildChannel) let prefs = MockProfilePrefs() var membership = featureSwitch.isMember(prefs) var changed = 0 for _ in 0..<100 { if featureSwitch.isMember(prefs) != membership { membership = !membership changed += 1 } } XCTAssertEqual(changed, 0, "Users should get and keep the feature over restarts") } func testConsistentWhenChangingPercentage() { let featureID = "test-persistent-over-releases" let prefs = MockProfilePrefs() let guardFeatureSwitch = FeatureSwitch(named: featureID, allowPercentage: 50, buildChannel: buildChannel) while guardFeatureSwitch.alwaysMembership(prefs) { guardFeatureSwitch.resetMembership(prefs) } var membership = false var changed = 0 for percent in 0..<100 { let featureSwitch = FeatureSwitch(named: featureID, allowPercentage: percent, buildChannel: buildChannel) if featureSwitch.isMember(prefs) != membership { membership = !membership changed += 1 } } XCTAssertEqual(changed, 1, "Users should get and keep the feature if the feature is becoming successful") } func testReallyConsistentWhenChangingPercentage() { for _ in 0..<1000 { testConsistentWhenChangingPercentage() } } func testUserEnabled() { let prefs = MockProfilePrefs() let featureSwitch = FeatureSwitch(named: "test-user-enabled", allowPercentage: 0, buildChannel: buildChannel) XCTAssertFalse(featureSwitch.isMember(prefs), "The feature should be disabled") featureSwitch.setMembership(true, for: prefs) // enable the feature XCTAssertTrue(featureSwitch.isMember(prefs), "The feature should be enabled") featureSwitch.setMembership(false, for: prefs) // disable the feature XCTAssertFalse(featureSwitch.isMember(prefs), "The feature should be disabled again") } func testForceDisabled() { let prefs = MockProfilePrefs() let featureSwitch = FeatureSwitch(named: "test-user-disabled", allowPercentage: 100, buildChannel: buildChannel) XCTAssertTrue(featureSwitch.isMember(prefs), "The feature should be enabled") featureSwitch.setMembership(false, for: prefs) // disable the feature XCTAssertFalse(featureSwitch.isMember(prefs), "The feature should be disabled again") } } extension FeatureSwitchTests { func test0Percent() { let featureSwitch = FeatureSwitch(named: "test-never", allowPercentage: 0, buildChannel: buildChannel) testExactly(featureSwitch, expected: 0) testApprox(featureSwitch, expected: 0) } func test100Percent() { let featureSwitch = FeatureSwitch(named: "test-always", allowPercentage: 100, buildChannel: buildChannel) testExactly(featureSwitch, expected: 100) testApprox(featureSwitch, expected: 100) } func test50Percent() { let featureSwitch = FeatureSwitch(named: "test-half-the-population", allowPercentage: 50, buildChannel: buildChannel) testApprox(featureSwitch, expected: 50) } func test30Percent() { let featureSwitch = FeatureSwitch(named: "test-30%-population", allowPercentage: 30, buildChannel: buildChannel) testApprox(featureSwitch, expected: 30) } func testPerformance() { let featureSwitch = FeatureSwitch(named: "test-30%-population", allowPercentage: 30, buildChannel: buildChannel) let prefs = MockProfilePrefs() measure { for _ in 0..<1000 { _ = featureSwitch.isMember(prefs) } } } func testAppConstantsWin() { // simulate in release channel, but switched off in AppConstants. let featureFlaggedOff = FeatureSwitch(named: "test-release-flagged-off", false, allowPercentage: 100, buildChannel: buildChannel) testExactly(featureFlaggedOff, expected: 0) // simulate in non-release channel, but switched on in AppConstants. let buildChannelAndFlaggedOn = FeatureSwitch(named: "test-flagged-on", true, allowPercentage: 0) testExactly(buildChannelAndFlaggedOn, expected: 100) // simulate in non-release channel, but switched off in AppConstants. let buildChannelAndFlaggedOff = FeatureSwitch(named: "test-flagged-off", false, allowPercentage: 100) testExactly(buildChannelAndFlaggedOff, expected: 0) } } private extension FeatureSwitchTests { func sampleN(_ featureSwitch: FeatureSwitch, testCount: Int = 1000) -> Int { var count = 0 for _ in 0..