Check Application state before sending RR (#1960)

* check the state

* better handling

* protocol to mock the application state

* test improvement
This commit is contained in:
Mauro 2023-10-25 19:16:37 +02:00 committed by GitHub
parent af76be2426
commit d457736673
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 235 additions and 106 deletions

View File

@ -634,6 +634,7 @@
A722F426FD81FC67706BB1E0 /* CustomLayoutLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */; };
A743841F91B62B0E56217B04 /* SecureBackupKeyBackupScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DCB219D7B7B0299358FF81 /* SecureBackupKeyBackupScreenUITests.swift */; };
A74438ED16F8683A4B793E6A /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */; };
A7BACE682AE97D4500FFBBEA /* ApplicationMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7BACE672AE97D4500FFBBEA /* ApplicationMock.swift */; };
A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; };
A7FD7B992E6EE6E5A8429197 /* RoomSummaryDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142808B69851451AC32A2CEA /* RoomSummaryDetails.swift */; };
A816F7087C495D85048AC50E /* RoomMemberDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */; };
@ -1030,7 +1031,7 @@
033DB41C51865A2E83174E87 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = "<group>"; };
0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = "<group>"; };
0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; path = test_voice_message.m4a; sourceTree = "<group>"; };
0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_voice_message.m4a; sourceTree = "<group>"; };
03BA7958A4BB9C22CA8884EF /* WaveformViewDragGestureModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaveformViewDragGestureModifier.swift; sourceTree = "<group>"; };
03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = "<group>"; };
@ -1585,6 +1586,7 @@
A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = "<group>"; };
A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallWidgetDriverProtocol.swift; sourceTree = "<group>"; };
A73A07BAEDD74C48795A996A /* AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequence.swift; sourceTree = "<group>"; };
A7BACE672AE97D4500FFBBEA /* ApplicationMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationMock.swift; sourceTree = "<group>"; };
A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleTimelineItem.swift; sourceTree = "<group>"; };
A861DA5932B128FE1DCB5CE2 /* InviteUsersScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreenCoordinator.swift; sourceTree = "<group>"; };
A8903A9F615BBD0E6D7CD133 /* ApplicationProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationProtocol.swift; sourceTree = "<group>"; };
@ -2439,6 +2441,7 @@
AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */,
B23135B06B044CB811139D2F /* Generated */,
E5E545F92D01588360A9BAC5 /* SDK */,
A7BACE672AE97D4500FFBBEA /* ApplicationMock.swift */,
);
path = Mocks;
sourceTree = "<group>";
@ -5289,6 +5292,7 @@
0F6C8033FA60CFD36F7CA205 /* AppLockSetupPINScreenViewModel.swift in Sources */,
4C356F5CCB4CDC99BFA45185 /* AppLockSetupPINScreenViewModelProtocol.swift in Sources */,
4E8A2A2CFEB212F14E49E1A1 /* AppLockSetupSettingsScreen.swift in Sources */,
A7BACE682AE97D4500FFBBEA /* ApplicationMock.swift in Sources */,
8DCD9CC5361FF22A5B2C20F1 /* AppLockSetupSettingsScreenCoordinator.swift in Sources */,
6E4E401BE97AC241DA7C7716 /* AppLockSetupSettingsScreenModels.swift in Sources */,
4807E8F51DB54F56B25E1C7E /* AppLockSetupSettingsScreenViewModel.swift in Sources */,

View File

@ -0,0 +1,73 @@
//
// Copyright 2023 New Vector Ltd
//
// 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 UIKit
extension ApplicationMock {
static var `default`: ApplicationProtocol {
ApplicationMock(withState: .active,
backgroundTimeRemaining: 10,
allowTasks: true)
}
static var mockBroken: ApplicationProtocol {
ApplicationMock(withState: .inactive,
backgroundTimeRemaining: 0,
allowTasks: false)
}
static var mockAboutToSuspend: ApplicationProtocol {
ApplicationMock(withState: .background,
backgroundTimeRemaining: 2,
allowTasks: false)
}
private static var bgTaskIdentifier = 0
convenience init(withState applicationState: UIApplication.State,
backgroundTimeRemaining: TimeInterval,
allowTasks: Bool) {
self.init()
underlyingApplicationState = applicationState
underlyingBackgroundTimeRemaining = backgroundTimeRemaining
beginBackgroundTaskExpirationHandlerClosure = { [weak self] handler in
guard let self else {
return .invalid
}
guard allowTasks else {
return .invalid
}
return beginBackgroundTask(withName: nil, expirationHandler: handler)
}
beginBackgroundTaskWithNameExpirationHandlerClosure = { _, handler in
guard allowTasks else {
return .invalid
}
Self.bgTaskIdentifier += 1
let identifier = UIBackgroundTaskIdentifier(rawValue: Self.bgTaskIdentifier)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
handler?()
}
return identifier
}
}
}

View File

@ -279,6 +279,85 @@ class AppLockServiceMock: AppLockServiceProtocol {
}
}
}
class ApplicationMock: ApplicationProtocol {
var backgroundTimeRemaining: TimeInterval {
get { return underlyingBackgroundTimeRemaining }
set(value) { underlyingBackgroundTimeRemaining = value }
}
var underlyingBackgroundTimeRemaining: TimeInterval!
var applicationState: UIApplication.State {
get { return underlyingApplicationState }
set(value) { underlyingApplicationState = value }
}
var underlyingApplicationState: UIApplication.State!
//MARK: - beginBackgroundTask
var beginBackgroundTaskExpirationHandlerCallsCount = 0
var beginBackgroundTaskExpirationHandlerCalled: Bool {
return beginBackgroundTaskExpirationHandlerCallsCount > 0
}
var beginBackgroundTaskExpirationHandlerReturnValue: UIBackgroundTaskIdentifier!
var beginBackgroundTaskExpirationHandlerClosure: (((() -> Void)?) -> UIBackgroundTaskIdentifier)?
func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
beginBackgroundTaskExpirationHandlerCallsCount += 1
if let beginBackgroundTaskExpirationHandlerClosure = beginBackgroundTaskExpirationHandlerClosure {
return beginBackgroundTaskExpirationHandlerClosure(handler)
} else {
return beginBackgroundTaskExpirationHandlerReturnValue
}
}
//MARK: - beginBackgroundTask
var beginBackgroundTaskWithNameExpirationHandlerCallsCount = 0
var beginBackgroundTaskWithNameExpirationHandlerCalled: Bool {
return beginBackgroundTaskWithNameExpirationHandlerCallsCount > 0
}
var beginBackgroundTaskWithNameExpirationHandlerReturnValue: UIBackgroundTaskIdentifier!
var beginBackgroundTaskWithNameExpirationHandlerClosure: ((String?, (() -> Void)?) -> UIBackgroundTaskIdentifier)?
func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
beginBackgroundTaskWithNameExpirationHandlerCallsCount += 1
if let beginBackgroundTaskWithNameExpirationHandlerClosure = beginBackgroundTaskWithNameExpirationHandlerClosure {
return beginBackgroundTaskWithNameExpirationHandlerClosure(taskName, handler)
} else {
return beginBackgroundTaskWithNameExpirationHandlerReturnValue
}
}
//MARK: - endBackgroundTask
var endBackgroundTaskCallsCount = 0
var endBackgroundTaskCalled: Bool {
return endBackgroundTaskCallsCount > 0
}
var endBackgroundTaskReceivedIdentifier: UIBackgroundTaskIdentifier?
var endBackgroundTaskReceivedInvocations: [UIBackgroundTaskIdentifier] = []
var endBackgroundTaskClosure: ((UIBackgroundTaskIdentifier) -> Void)?
func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) {
endBackgroundTaskCallsCount += 1
endBackgroundTaskReceivedIdentifier = identifier
endBackgroundTaskReceivedInvocations.append(identifier)
endBackgroundTaskClosure?(identifier)
}
//MARK: - open
var openCallsCount = 0
var openCalled: Bool {
return openCallsCount > 0
}
var openReceivedUrl: URL?
var openReceivedInvocations: [URL] = []
var openClosure: ((URL) -> Void)?
func open(_ url: URL) {
openCallsCount += 1
openReceivedUrl = url
openReceivedInvocations.append(url)
openClosure?(url)
}
}
class AudioConverterMock: AudioConverterProtocol {
//MARK: - convertToOpusOgg

View File

@ -65,7 +65,8 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
roomProxy: parameters.roomProxy,
appSettings: parameters.appSettings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
userIndicatorController: ServiceLocator.shared.userIndicatorController,
application: UIApplication.shared)
wysiwygViewModel = WysiwygComposerViewModel(minHeight: ComposerConstant.minHeight,
maxCompressedHeight: ComposerConstant.maxHeight,

View File

@ -32,6 +32,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
private let roomProxy: RoomProxyProtocol
private let appSettings: AppSettings
private let analytics: AnalyticsService
private let application: ApplicationProtocol
private unowned let userIndicatorController: UserIndicatorControllerProtocol
private let notificationCenterProtocol: NotificationCenterProtocol
private let voiceMessageRecorder: VoiceMessageRecorderProtocol
@ -48,6 +49,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
appSettings: AppSettings,
analytics: AnalyticsService,
userIndicatorController: UserIndicatorControllerProtocol,
application: ApplicationProtocol,
notificationCenterProtocol: NotificationCenterProtocol = NotificationCenter.default) {
self.roomProxy = roomProxy
self.timelineController = timelineController
@ -56,6 +58,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
self.userIndicatorController = userIndicatorController
self.notificationCenterProtocol = notificationCenterProtocol
self.mediaPlayerProvider = mediaPlayerProvider
self.application = application
voiceMessageRecorder = VoiceMessageRecorder(audioRecorder: AudioRecorder(), mediaPlayerProvider: mediaPlayerProvider)
super.init(initialViewState: RoomScreenViewState(roomID: timelineController.roomID,
@ -327,16 +330,19 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
/// The ID of the newest item in the room that the user has seen.
/// This includes both event based items and virtual items.
private var lastReadItemID: TimelineItemIdentifier?
private func sendReadReceiptIfNeeded(for lastVisibleItemID: TimelineItemIdentifier) async -> Result<Void, RoomTimelineControllerError> {
private func sendReadReceiptIfNeeded(for lastVisibleItemID: TimelineItemIdentifier) async {
guard application.applicationState == .active else { return }
guard lastReadItemID != lastVisibleItemID,
let eventItemID = eventBasedItem(nearest: lastVisibleItemID)
else { return .success(()) }
let eventItemID = eventBasedItem(nearest: lastVisibleItemID) else {
return
}
// Make sure the item is newer than the item that was last marked as read.
if let lastReadItemIndex = state.timelineViewState.timelineIDs.firstIndex(of: lastReadItemID?.timelineID ?? ""),
let lastVisibleItemIndex = state.timelineViewState.timelineIDs.firstIndex(of: eventItemID.timelineID),
lastReadItemIndex > lastVisibleItemIndex {
return .success(())
return
}
// Update the last read item ID to avoid attempting duplicate requests.
@ -348,10 +354,10 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
}
switch await timelineController.sendReadReceipt(for: eventItemID) {
case .success:
return .success(())
case .failure:
return .failure(.generic)
case .success():
break
case let .failure(error):
MXLog.error("[TimelineViewController] Failed to send read receipt: \(error)")
}
}
@ -1028,7 +1034,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
private func openSystemSettings() {
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
UIApplication.shared.open(url)
application.open(url)
}
}
@ -1061,7 +1067,8 @@ extension RoomScreenViewModel {
roomProxy: RoomProxyMock(with: .init(displayName: "Preview room")),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
userIndicatorController: ServiceLocator.shared.userIndicatorController,
application: ApplicationMock.default)
}
private struct ReplyInfo {

View File

@ -61,7 +61,8 @@ struct RoomHeaderView_Previews: PreviewProvider, TestablePreview {
roomProxy: RoomProxyMock(with: .init(displayName: "Some Room name", avatarURL: URL.picturesDirectory)),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
userIndicatorController: ServiceLocator.shared.userIndicatorController,
application: ApplicationMock.default)
RoomHeaderView(context: viewModel.context)
.previewLayout(.sizeThatFits)
@ -76,7 +77,8 @@ struct RoomHeaderView_Previews: PreviewProvider, TestablePreview {
roomProxy: RoomProxyMock(with: .init(displayName: "Some Room name")),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
userIndicatorController: ServiceLocator.shared.userIndicatorController,
application: ApplicationMock.default)
RoomHeaderView(context: viewModel.context)
.previewLayout(.sizeThatFits)

View File

@ -195,7 +195,8 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview {
roomProxy: RoomProxyMock(with: .init(displayName: "Preview room", isCallOngoing: true)),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
userIndicatorController: ServiceLocator.shared.userIndicatorController,
application: ApplicationMock.default)
static var previews: some View {
NavigationStack {

View File

@ -65,7 +65,8 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview {
members: members)),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
userIndicatorController: ServiceLocator.shared.userIndicatorController,
application: ApplicationMock.default)
static let singleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now")]
static let doubleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now"),

View File

@ -86,7 +86,8 @@ struct UITimelineView_Previews: PreviewProvider, TestablePreview {
roomProxy: RoomProxyMock(with: .init(displayName: "Preview room")),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
userIndicatorController: ServiceLocator.shared.userIndicatorController,
application: ApplicationMock.default)
static var previews: some View {
NavigationStack {

View File

@ -174,7 +174,8 @@ struct TimelineView_Previews: PreviewProvider, TestablePreview {
roomProxy: RoomProxyMock(with: .init(displayName: "Preview room")),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
userIndicatorController: ServiceLocator.shared.userIndicatorController,
application: ApplicationMock.default)
static var previews: some View {
NavigationStack {

View File

@ -17,16 +17,23 @@
import Foundation
import UIKit
// sourcery: AutoMockable
protocol ApplicationProtocol {
func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier
func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier
func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier)
func open(_ url: URL)
var backgroundTimeRemaining: TimeInterval { get }
var applicationState: UIApplication.State { get }
}
extension UIApplication: ApplicationProtocol { }
extension UIApplication: ApplicationProtocol {
func open(_ url: URL) {
open(url, options: [:], completionHandler: nil)
}
}

View File

@ -35,7 +35,7 @@ class BackgroundTaskTests: XCTestCase {
func testInitAndStop() {
let service = UIKitBackgroundTaskService {
UIApplication.mockHealty
ApplicationMock.default
}
guard let task = service.startBackgroundTask(withName: Constants.bgTaskName) else {
XCTFail("Failed to setup test conditions")
@ -53,7 +53,7 @@ class BackgroundTaskTests: XCTestCase {
func testNotReusableInit() {
let service = UIKitBackgroundTaskService {
UIApplication.mockHealty
ApplicationMock.default
}
// create two not reusable task with the same name
@ -70,7 +70,7 @@ class BackgroundTaskTests: XCTestCase {
func testReusableInit() {
let service = UIKitBackgroundTaskService {
UIApplication.mockHealty
ApplicationMock.default
}
// create two reusable task with the same name
@ -91,7 +91,7 @@ class BackgroundTaskTests: XCTestCase {
func testMultipleStops() {
let service = UIKitBackgroundTaskService {
UIApplication.mockHealty
ApplicationMock.default
}
// create two reusable task with the same name
@ -114,7 +114,7 @@ class BackgroundTaskTests: XCTestCase {
func testNotValidReuse() {
let service = UIKitBackgroundTaskService {
UIApplication.mockHealty
ApplicationMock.default
}
// create two reusable task with the same name
@ -136,7 +136,7 @@ class BackgroundTaskTests: XCTestCase {
func testValidReuse() {
let service = UIKitBackgroundTaskService {
UIApplication.mockHealty
ApplicationMock.default
}
// create two reusable task with the same name
@ -162,7 +162,7 @@ class BackgroundTaskTests: XCTestCase {
func testBrokenApp() {
let service = UIKitBackgroundTaskService {
UIApplication.mockBroken
ApplicationMock.mockBroken
}
// create two reusable task with the same name
@ -173,7 +173,7 @@ class BackgroundTaskTests: XCTestCase {
func testNoTimeApp() {
let service = UIKitBackgroundTaskService {
UIApplication.mockAboutToSuspend
ApplicationMock.mockAboutToSuspend
}
// create two reusable task with the same name
@ -182,67 +182,3 @@ class BackgroundTaskTests: XCTestCase {
XCTAssertNil(task, "Task should not be created")
}
}
private extension UIApplication {
static var mockHealty: ApplicationProtocol {
MockApplication()
}
static var mockBroken: ApplicationProtocol {
MockApplication(withState: .inactive,
backgroundTimeRemaining: 0,
allowTasks: false)
}
static var mockAboutToSuspend: ApplicationProtocol {
MockApplication(withState: .background,
backgroundTimeRemaining: 2,
allowTasks: false)
}
}
private class MockApplication: ApplicationProtocol {
let applicationState: UIApplication.State
let backgroundTimeRemaining: TimeInterval
private let allowTasks: Bool
init(withState applicationState: UIApplication.State = .active,
backgroundTimeRemaining: TimeInterval = 10,
allowTasks: Bool = true) {
self.applicationState = applicationState
self.backgroundTimeRemaining = backgroundTimeRemaining
self.allowTasks = allowTasks
}
private static var bgTaskIdentifier = 0
private var bgTasks: [UIBackgroundTaskIdentifier: Bool] = [:]
func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
guard allowTasks else {
return .invalid
}
return beginBackgroundTask(withName: nil, expirationHandler: handler)
}
func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
guard allowTasks else {
return .invalid
}
Self.bgTaskIdentifier += 1
let identifier = UIBackgroundTaskIdentifier(rawValue: Self.bgTaskIdentifier)
bgTasks[identifier] = true
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
handler?()
}
return identifier
}
func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) {
guard allowTasks else {
return
}
bgTasks.removeValue(forKey: identifier)
}
}

View File

@ -32,7 +32,8 @@ class PillContextTests: XCTestCase {
roomProxy: proxyMock,
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
userIndicatorController: ServiceLocator.shared.userIndicatorController,
application: ApplicationMock.default)
let context = PillContext(roomContext: mock.context, data: PillTextAttachmentData(type: .user(userID: id), font: .preferredFont(forTextStyle: .body)))
XCTAssertFalse(context.viewState.isOwnMention)
@ -64,7 +65,8 @@ class PillContextTests: XCTestCase {
roomProxy: proxyMock,
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
userIndicatorController: ServiceLocator.shared.userIndicatorController,
application: ApplicationMock.default)
let context = PillContext(roomContext: mock.context, data: PillTextAttachmentData(type: .user(userID: id), font: .preferredFont(forTextStyle: .body)))
XCTAssertTrue(context.viewState.isOwnMention)
@ -83,7 +85,8 @@ class PillContextTests: XCTestCase {
roomProxy: proxyMock,
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
userIndicatorController: ServiceLocator.shared.userIndicatorController,
application: ApplicationMock.default)
let context = PillContext(roomContext: mock.context, data: PillTextAttachmentData(type: .allUsers, font: .preferredFont(forTextStyle: .body)))
XCTAssertTrue(context.viewState.isOwnMention)

View File

@ -55,7 +55,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: RoomProxyMock(with: .init(displayName: "")),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
// Then the messages should be grouped together.
XCTAssertEqual(viewModel.state.timelineViewState.itemViewStates[0].groupStyle, .first, "Nothing should prevent the first message from being grouped.")
@ -89,7 +90,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: RoomProxyMock(with: .init(displayName: "")),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
// Then the messages should be grouped by sender.
XCTAssertEqual(viewModel.state.timelineViewState.itemViewStates[0].groupStyle, .single, "A message should not be grouped when the sender changes.")
@ -121,7 +123,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: RoomProxyMock(with: .init(displayName: "")),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
// Then the first message should not be grouped but the other two should.
XCTAssertEqual(viewModel.state.timelineViewState.itemViewStates[0].groupStyle, .single, "When the first message has reactions it should not be grouped.")
@ -150,7 +153,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: RoomProxyMock(with: .init(displayName: "")),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
// Then the first and second messages should be grouped and the last one should not.
XCTAssertEqual(viewModel.state.timelineViewState.itemViewStates[0].groupStyle, .first, "Nothing should prevent the first message from being grouped.")
@ -179,7 +183,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: RoomProxyMock(with: .init(displayName: "")),
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
// Then the messages should be grouped together.
XCTAssertEqual(viewModel.state.timelineViewState.itemViewStates[0].groupStyle, .first, "Nothing should prevent the first message from being grouped.")
@ -204,7 +209,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: roomProxyMock,
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
viewModel.actions
.sink { action in
switch action {
@ -243,7 +249,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: roomProxyMock,
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
viewModel.actions
.sink { action in
@ -283,7 +290,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: roomProxyMock,
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
viewModel.actions
.sink { _ in
XCTFail("Should not receive any action")
@ -315,7 +323,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: roomProxyMock,
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
viewModel.context.send(viewAction: .retrySend(itemID: .init(timelineID: UUID().uuidString, transactionID: "test retry send id")))
@ -335,7 +344,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: roomProxyMock,
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
viewModel.context.send(viewAction: .retrySend(itemID: .random))
@ -354,7 +364,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: roomProxyMock,
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
viewModel.context.send(viewAction: .cancelSend(itemID: .init(timelineID: UUID().uuidString, transactionID: "test cancel send id")))
@ -374,7 +385,8 @@ class RoomScreenViewModelTests: XCTestCase {
roomProxy: roomProxyMock,
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock)
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default)
viewModel.context.send(viewAction: .cancelSend(itemID: .random))
@ -512,6 +524,7 @@ class RoomScreenViewModelTests: XCTestCase {
appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: userIndicatorControllerMock,
application: ApplicationMock.default,
notificationCenterProtocol: notificationCenter)
return (viewModel, roomProxy, timelineController, notificationCenter)