Skip to content

Commit

Permalink
IOS-8391: Fix data race in UpgradeAccountPlanViewModel
Browse files Browse the repository at this point in the history
  • Loading branch information
mega-bl committed Oct 11, 2024
1 parent 0216a33 commit 66773c4
Show file tree
Hide file tree
Showing 13 changed files with 126 additions and 38 deletions.
2 changes: 1 addition & 1 deletion MEGADataTests/Mocks/MEGAPurchase/MockMEGAPurchase.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
final class MockMEGAPurchase: MEGAPurchase {
final class MockMEGAPurchase: MEGAPurchase, @unchecked Sendable {
private(set) var restorePurchaseCalled = 0
private(set) var purchasePlanCalled = 0

Expand Down
2 changes: 2 additions & 0 deletions MEGAUnitTests/Mocks/Router/MockWarningViewRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ final class MockWarningViewRouter: WarningBannerViewRouting {
var goToSettings_calledTimes = 0
var presentUpgradeScreen_calledTimes = 0

nonisolated init() {}

func goToSettings() {
goToSettings_calledTimes += 1
}
Expand Down
3 changes: 3 additions & 0 deletions MEGAUnitTests/Plan/Mocks/MockUpgradeAccountRouter.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
@testable import MEGA
final class MockUpgradeAccountRouter: UpgradeAccountRouting, @unchecked Sendable {
var presentUpgradeTVCRecorder = FuncCallRecorder<Void, Void>()

nonisolated init() {}

func presentUpgradeTVC() {
presentUpgradeTVCRecorder.call(())
}
Expand Down
54 changes: 54 additions & 0 deletions MEGAUnitTests/Plan/UpgradeAccountPlanViewModelTests.swift

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ final class UpgradeAccountPlanViewModel_createAccountPlanViewModelTests: XCTestC
private let proIII_monthly = PlanEntity(type: .proII, name: "Pro III", subscriptionCycle: .monthly)
private let proIII_yearly = PlanEntity(type: .proII, name: "Pro III", subscriptionCycle: .yearly)

@MainActor
func testCreateAccountPlanViewModel_withSelectedPlanType_shouldReturnViewModel() {
let details = AccountDetailsEntity.build(proLevel: .free)
let planList = [proI_monthly, proIII_monthly]
Expand All @@ -37,6 +38,7 @@ final class UpgradeAccountPlanViewModel_createAccountPlanViewModelTests: XCTestC
XCTAssertEqual(viewModel.planTag, .none)
}

@MainActor
func testCreateAccountPlanViewModel_noSelectedPlan_shouldReturnViewModel() {
let details = AccountDetailsEntity.build(proLevel: .free)
let planList = [proI_monthly, proII_yearly]
Expand All @@ -58,6 +60,7 @@ final class UpgradeAccountPlanViewModel_createAccountPlanViewModelTests: XCTestC
XCTAssertEqual(viewModel.planTag, .none)
}

@MainActor
func disable_testCreateAccountPlanViewModel_recurringPlanMonthly_shouldReturnViewModel() {
let details = AccountDetailsEntity.build(proLevel: .proI, subscriptionCycle: .monthly)
let planList = [proI_monthly, proII_yearly]
Expand All @@ -84,6 +87,7 @@ final class UpgradeAccountPlanViewModel_createAccountPlanViewModelTests: XCTestC
XCTAssertEqual(yearlyPlanViewModel.planTag, .none)
}

@MainActor
func testCreateAccountPlanViewModel_recurringPlanYearly_shouldReturnViewModel() {
let details = AccountDetailsEntity.build(proLevel: .proI, subscriptionCycle: .yearly)
let planList = [proI_monthly, proI_yearly]
Expand All @@ -110,6 +114,7 @@ final class UpgradeAccountPlanViewModel_createAccountPlanViewModelTests: XCTestC
XCTAssertEqual(yearlyPlanViewModel.planTag, .currentPlan)
}

@MainActor
func testCreateAccountPlanViewModel_oneTimePlanPurchase_shouldReturnViewModel() {
let details = AccountDetailsEntity.build(proLevel: .proI, subscriptionCycle: .none)
let planList = [proI_monthly, proI_yearly, proII_yearly]
Expand All @@ -136,6 +141,7 @@ final class UpgradeAccountPlanViewModel_createAccountPlanViewModelTests: XCTestC
XCTAssertEqual(yearlyPlanViewModel.planTag, .currentPlan)
}

@MainActor
func testCreateAccountPlanViewModel_withRecommendedPlanType_onRecommendedPlan_shouldReturnViewModel() {
let details = AccountDetailsEntity.build(proLevel: .free)
let planList = [proI_monthly, proI_yearly, proII_yearly]
Expand All @@ -162,6 +168,7 @@ final class UpgradeAccountPlanViewModel_createAccountPlanViewModelTests: XCTestC
XCTAssertEqual(yearlyPlanViewModel.planTag, .recommended)
}

@MainActor
func testCreateAccountPlanViewModel_withRecommendedPlanType_onNotRecommendedPlan_shouldReturnViewModel() {
let details = AccountDetailsEntity.build(proLevel: .free)
let planList = [proI_monthly, proI_yearly, proII_monthly, proII_yearly]
Expand All @@ -188,6 +195,7 @@ final class UpgradeAccountPlanViewModel_createAccountPlanViewModelTests: XCTestC
XCTAssertEqual(yearlyPlanViewModel.planTag, .none)
}

@MainActor
func testCreateAccountPlanViewModel_withNoRecommendedPlanType_shouldReturnViewModel() {
let details = AccountDetailsEntity.build(proLevel: .proIII)
let planList = [proIII_monthly, proIII_yearly]
Expand Down Expand Up @@ -215,6 +223,7 @@ final class UpgradeAccountPlanViewModel_createAccountPlanViewModelTests: XCTestC
}

// MARK: - Helper
@MainActor
private func makeSUT(
accountDetails: AccountDetailsEntity,
currentAccountDetails: AccountDetailsEntity? = nil,
Expand All @@ -232,9 +241,9 @@ final class UpgradeAccountPlanViewModel_createAccountPlanViewModelTests: XCTestC
viewType: viewType,
router: MockUpgradeAccountPlanRouter()
)
trackForMemoryLeaks(on: mockSubscriptionsUseCase)
trackForMemoryLeaks(on: mockPurchaseUseCase)
trackForMemoryLeaks(on: sut)
trackForMemoryLeaks(on: mockSubscriptionsUseCase, timeoutNanoseconds: 1_000_000_000)
trackForMemoryLeaks(on: mockPurchaseUseCase, timeoutNanoseconds: 1_000_000_000)
trackForMemoryLeaks(on: sut, timeoutNanoseconds: 1_000_000_000)
return sut
}
}
8 changes: 8 additions & 0 deletions MEGAUnitTests/Plan/UpgradeEncouragementViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ final class UpgradeEncouragementViewModelTests: XCTestCase {
randomNumberGenerator = nil
}

@MainActor
func testEncourageUpgradeIfNeeded_whenAlreadyPresented_shouldNotTriggerRouter() {
// given
showTimeTracker.alreadyPresented = true
Expand All @@ -42,6 +43,7 @@ final class UpgradeEncouragementViewModelTests: XCTestCase {
XCTAssertFalse(router.presentUpgradeTVCRecorder.called)
}

@MainActor
func testEncourageUpgradeIfNeeded_whenIsProAccount_shouldNotTriggerRouter() {
// given
let accountUseCase = MockAccountUseCase(currentAccountDetails: MockMEGAAccountDetails.proAccounDetailsEntity)
Expand All @@ -55,6 +57,7 @@ final class UpgradeEncouragementViewModelTests: XCTestCase {
XCTAssertFalse(router.presentUpgradeTVCRecorder.called)
}

@MainActor
func testEncourageUpgradeIfNeeded_whenIsFreeAccount_createdLessThanThreeDays_shouldNotTriggerRouter() {
// given
let accountUseCase = MockAccountUseCase(accountCreationDate: Date().daysAgo(1), currentAccountDetails: MockMEGAAccountDetails.freeAccountDetailsEntity)
Expand All @@ -69,6 +72,7 @@ final class UpgradeEncouragementViewModelTests: XCTestCase {
}

// MARK: - From here, test functions are implicitly have inputs with free account and created more than 3 days
@MainActor
func testEncourageUpgradeIfNeeded_randomNumberIsTwenty_shouldNotTriggerRouter() {
// given
let accountUseCase = MockAccountUseCase(accountCreationDate: Date().daysAgo(6), currentAccountDetails: MockMEGAAccountDetails.freeAccountDetailsEntity)
Expand All @@ -86,6 +90,7 @@ final class UpgradeEncouragementViewModelTests: XCTestCase {
XCTAssertFalse(router.presentUpgradeTVCRecorder.called)
}

@MainActor
func testEncourageUpgradeIfNeeded_randomNumberIsOne_lastEncourageUgradeLessThanOneWeek_shouldNotTriggerRouter() {
// given
let accountUseCase = MockAccountUseCase(accountCreationDate: Date().daysAgo(5), currentAccountDetails: MockMEGAAccountDetails.freeAccountDetailsEntity)
Expand All @@ -103,6 +108,7 @@ final class UpgradeEncouragementViewModelTests: XCTestCase {
XCTAssertFalse(router.presentUpgradeTVCRecorder.called)
}

@MainActor
func testEncourageUpgradeIfNeeded_randomNumberIsOne_lastEncourageUgradeDateIsNil_shouldTriggerRouter() {
// given
let accountUseCase = MockAccountUseCase(accountCreationDate: Date().daysAgo(5), currentAccountDetails: MockMEGAAccountDetails.freeAccountDetailsEntity)
Expand All @@ -121,6 +127,7 @@ final class UpgradeEncouragementViewModelTests: XCTestCase {
XCTAssertTrue(router.presentUpgradeTVCRecorder.called)
}

@MainActor
func testEncourageUpgradeIfNeeded_randomNumberIsOne_lastEncourageUgradeMoreThanOneWeek_shouldTriggerRouter() {
// given
let accountUseCase = MockAccountUseCase(accountCreationDate: Date().daysAgo(6), currentAccountDetails: MockMEGAAccountDetails.freeAccountDetailsEntity)
Expand All @@ -146,6 +153,7 @@ final class UpgradeEncouragementViewModelTests: XCTestCase {
XCTAssertEqual(router.presentUpgradeTVCRecorder.callCount, 1)
}

@MainActor
private func makeSUT(
accountUseCase: some AccountUseCaseProtocol = MockAccountUseCase(),
preferenceUseCase: some PreferenceUseCaseProtocol = MockPreferenceUseCase()
Expand Down
20 changes: 20 additions & 0 deletions MEGAUnitTests/Warning/WarningBannerViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import MEGAPresentationMock
import XCTest

final class WarningBannerViewModelTests: XCTestCase {
@MainActor
private func makeSUT(
warningType: WarningBannerType,
isShowCloseButton: Bool = false,
Expand Down Expand Up @@ -34,16 +35,19 @@ final class WarningBannerViewModelTests: XCTestCase {
].randomElement()!
}

@MainActor
func testWarningType_noInternetConnection_shouldReturnCorrectDescription() {
let sut = makeSUT(warningType: .noInternetConnection)
XCTAssertEqual(sut.warningType.description, Strings.Localizable.General.noIntenerConnection)
}

@MainActor
func testWarningType_limitedPhotoAccess_shouldReturnCorrectDescription() {
let sut = makeSUT(warningType: .limitedPhotoAccess)
XCTAssertEqual(sut.warningType.description, Strings.Localizable.CameraUploads.Warning.limitedAccessToPhotoMessage)
}

@MainActor
func testTapAction_limitedPhotoAccess_shouldCallGoToSettings() {
let router = MockWarningViewRouter()
let sut = makeSUT(
Expand All @@ -54,6 +58,7 @@ final class WarningBannerViewModelTests: XCTestCase {
XCTAssertEqual(router.goToSettings_calledTimes, 1)
}

@MainActor
func testTapAction_fullStorageOverQuota_shouldCallGoToSettings() {
let router = MockWarningViewRouter()
let sut = makeSUT(
Expand All @@ -64,23 +69,27 @@ final class WarningBannerViewModelTests: XCTestCase {
XCTAssertEqual(router.presentUpgradeScreen_calledTimes, 1)
}

@MainActor
func testWarningType_contactsNotVerified_shouldReturnCorrectDescription() {
let sut = makeSUT(warningType: .contactsNotVerified)
XCTAssertEqual(sut.warningType.description, Strings.Localizable.ShareFolder.contactsNotVerified)
}

@MainActor
func testWarningType_contactNotVerifiedSharedFolder_shouldReturnCorrectDescription() {
let folderName = "Test Folder"
let sut = makeSUT(warningType: .contactNotVerifiedSharedFolder(folderName))
XCTAssertEqual(sut.warningType.description, Strings.Localizable.SharedItems.ContactVerification.contactNotVerifiedBannerMessage(folderName))
}

@MainActor
func testWarningType_backupStatusError_shouldReturnErrorMessage() {
let errorMessage = "Backup failed"
let sut = makeSUT(warningType: .backupStatusError(errorMessage))
XCTAssertEqual(sut.warningType.description, errorMessage)
}

@MainActor
func testWarningType_fullStorageOverQuota_shouldReturnCorrectValues() {
let sut = makeSUT(warningType: .fullStorageOverQuota)

Expand All @@ -91,6 +100,7 @@ final class WarningBannerViewModelTests: XCTestCase {
XCTAssertEqual(sut.warningType.severity, .critical)
}

@MainActor
func testWarningType_almostFullStorageOverQuota_shouldReturnCorrectValues() {
let sut = makeSUT(warningType: .almostFullStorageOverQuota)

Expand All @@ -101,6 +111,7 @@ final class WarningBannerViewModelTests: XCTestCase {
XCTAssertEqual(sut.warningType.severity, .warning)
}

@MainActor
func testOnViewAppear_whenWarningTypeIsFullStorageOverQuota_shouldTrackFullStorageOverQuotaBannerDisplayedEvent() {
let tracker = MockTracker()
let sut = makeSUT(warningType: .fullStorageOverQuota, tracker: tracker)
Expand All @@ -112,6 +123,7 @@ final class WarningBannerViewModelTests: XCTestCase {
)
}

@MainActor
func testTapAction_whenWarningTypeIsFullStorageOverQuota_shouldTrackFullStorageOverQuotaBannerUpgradeButtonPressedEvent() {
let tracker = MockTracker()
let sut = makeSUT(warningType: .fullStorageOverQuota, tracker: tracker)
Expand All @@ -123,6 +135,7 @@ final class WarningBannerViewModelTests: XCTestCase {
)
}

@MainActor
func testWarningType_whenCloseButtonEnabled_shouldShowCloseButton() {
let randomWarningType = WarningBannerType.random()
let sut = makeSUT(
Expand All @@ -132,6 +145,7 @@ final class WarningBannerViewModelTests: XCTestCase {
XCTAssertTrue(sut.shouldShowCloseButton, "The close button should be visible for warning type \(randomWarningType)")
}

@MainActor
func testCloseButtonAction_shouldCallCloseButtonAction() {
var actionCalled = false
let sut = makeSUT(warningType: .noInternetConnection) {
Expand All @@ -141,6 +155,7 @@ final class WarningBannerViewModelTests: XCTestCase {
XCTAssertTrue(actionCalled)
}

@MainActor
func testHeightChangeCallback_shouldInvokeOnHeightChangeClosure() {
var heightChangeCalled = false
let sut = makeSUT(
Expand All @@ -152,21 +167,25 @@ final class WarningBannerViewModelTests: XCTestCase {
XCTAssertTrue(heightChangeCalled)
}

@MainActor
func testApplyNewDesign_whenAlmostFullStorageOverQuota_shouldBeTrue() {
let sut = makeSUT(warningType: .almostFullStorageOverQuota)
XCTAssertTrue(sut.applyNewDesign)
}

@MainActor
func testApplyNewDesign_whenFullStorageOverQuota_shouldBeTrue() {
let sut = makeSUT(warningType: .fullStorageOverQuota)
XCTAssertTrue(sut.applyNewDesign)
}

@MainActor
func testApplyNewDesign_whenOtherWarningTypes_shouldBeFalse() {
let sut = makeSUT(warningType: .noInternetConnection)
XCTAssertFalse(sut.applyNewDesign)
}

@MainActor
func testActionButtonTapped_whenWarningTypeHasNoAction_shouldNotTriggerRouterAction() {
let router = MockWarningViewRouter()
let sut = makeSUT(
Expand All @@ -179,6 +198,7 @@ final class WarningBannerViewModelTests: XCTestCase {
XCTAssertEqual(router.goToSettings_calledTimes, 0)
}

@MainActor
func testActionButtonTapped_whenWarningTypeHasNoAction_shouldNotTrackAnalytics() {
let tracker = MockTracker()
let sut = makeSUT(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
public protocol SubscriptionsUseCaseProtocol {
public protocol SubscriptionsUseCaseProtocol: Sendable {
/// Cancels credit card subscriptions with optional parameters for providing a reason, subscription ID, and contact preference.
///
/// - Default Values:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import MEGADomain
import MEGASDKRepo

@MainActor
final class UpgradeEncouragementViewModel {

private enum Constants {
Expand Down
Loading

0 comments on commit 66773c4

Please sign in to comment.