diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index a4fdbed69..5f8e0474a 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -892,6 +892,7 @@ FDC438CD27BC641200C60D73 /* Set+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438CC27BC641200C60D73 /* Set+Utilities.swift */; }; FDC438CF27BCA45400C60D73 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438CE27BCA45400C60D73 /* Server.swift */; }; FDCDB8E42817819600352A0C /* TSYapDatabaseObject.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA90255A57FD00E217F9 /* TSYapDatabaseObject.m */; }; + FDCDB8F12817ABE600352A0C /* Optional+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCDB8F02817ABE600352A0C /* Optional+Utilities.swift */; }; FDFD645927F26C6800808CA1 /* Array+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D12553860800C340D1 /* Array+Utilities.swift */; }; FDFD645B27F26D4600808CA1 /* Data+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFD645A27F26D4600808CA1 /* Data+Utilities.swift */; }; FDFD645D27F273F300808CA1 /* MockGeneralCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */; }; @@ -1031,6 +1032,20 @@ remoteGlobalIDString = C3C2A6EF25539DE700C340D1; remoteInfo = SessionMessagingKit; }; + FDCDB8EB28179EAF00352A0C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D221A080169C9E5E00537ABF /* Project object */; + proxyType = 1; + remoteGlobalIDString = D221A088169C9E5E00537ABF; + remoteInfo = Session; + }; + FDCDB8ED28179EB200352A0C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D221A080169C9E5E00537ABF /* Project object */; + proxyType = 1; + remoteGlobalIDString = D221A088169C9E5E00537ABF; + remoteInfo = Session; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -2062,6 +2077,7 @@ FDC438CA27BB7DB100C60D73 /* UpdateMessageRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateMessageRequest.swift; sourceTree = ""; }; FDC438CC27BC641200C60D73 /* Set+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Set+Utilities.swift"; sourceTree = ""; }; FDC438CE27BCA45400C60D73 /* Server.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Server.swift; sourceTree = ""; }; + FDCDB8F02817ABE600352A0C /* Optional+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Utilities.swift"; sourceTree = ""; }; FDFD645A27F26D4600808CA1 /* Data+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = ""; }; FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGeneralCache.swift; sourceTree = ""; }; FEDBAE1B98C49BBE8C87F575 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; sourceTree = ""; }; @@ -3622,6 +3638,7 @@ B8A582B9258C696200AFD84C /* Messaging */, B8A582AE258C65D000AFD84C /* Networking */, B8A582AD258C655E00AFD84C /* PromiseKit */, + FDCDB8EF2817ABCE00352A0C /* Utilities */, C3D9E43025676D3D0040E4F3 /* Configuration.swift */, ); path = SessionUtilitiesKit; @@ -4194,6 +4211,14 @@ path = Models; sourceTree = ""; }; + FDCDB8EF2817ABCE00352A0C /* Utilities */ = { + isa = PBXGroup; + children = ( + FDCDB8F02817ABE600352A0C /* Optional+Utilities.swift */, + ); + path = Utilities; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -4529,6 +4554,7 @@ ); dependencies = ( FD83B9B527CF200A005E1583 /* PBXTargetDependency */, + FDCDB8EE28179EB200352A0C /* PBXTargetDependency */, ); name = SessionUtilitiesKitTests; productName = SessionUtilitiesKitTests; @@ -4549,6 +4575,7 @@ ); dependencies = ( FDC4389427B9FFC700C60D73 /* PBXTargetDependency */, + FDCDB8EC28179EAF00352A0C /* PBXTargetDependency */, ); name = SessionMessagingKitTests; productName = SessionMessagingKitTests; @@ -4654,9 +4681,11 @@ }; FD83B9AE27CF200A005E1583 = { CreatedOnToolsVersion = 13.2.1; + TestTargetID = D221A088169C9E5E00537ABF; }; FDC4388D27B9FFC700C60D73 = { CreatedOnToolsVersion = 13.2.1; + TestTargetID = D221A088169C9E5E00537ABF; }; }; }; @@ -5332,6 +5361,7 @@ C3BBE0A82554D4DE0050F1E3 /* JSON.swift in Sources */, C352A36D2557858E00338F3E /* NSTimer+Proxying.m in Sources */, 7BAF54DA27ACD0E3003D12F8 /* UITableView+ReusableView.swift in Sources */, + FDCDB8F12817ABE600352A0C /* Optional+Utilities.swift in Sources */, C32C5A2D256DB849003C73A2 /* LKGroupUtilities.m in Sources */, 7B7CB192271508AD0079FF93 /* CallRingTonePlayer.swift in Sources */, C3C2ABD22553C6C900C340D1 /* Data+SecureRandom.swift in Sources */, @@ -5926,6 +5956,16 @@ target = C3C2A6EF25539DE700C340D1 /* SessionMessagingKit */; targetProxy = FDC4389327B9FFC700C60D73 /* PBXContainerItemProxy */; }; + FDCDB8EC28179EAF00352A0C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D221A088169C9E5E00537ABF /* Session */; + targetProxy = FDCDB8EB28179EAF00352A0C /* PBXContainerItemProxy */; + }; + FDCDB8EE28179EB200352A0C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D221A088169C9E5E00537ABF /* Session */; + targetProxy = FDCDB8ED28179EB200352A0C /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -7309,6 +7349,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Session.app/Session"; }; name = Debug; }; @@ -7373,6 +7414,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Session.app/Session"; VALIDATE_PRODUCT = YES; }; name = "App Store Release"; @@ -7415,6 +7457,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Session.app/Session"; }; name = Debug; }; @@ -7479,6 +7522,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Session.app/Session"; VALIDATE_PRODUCT = YES; }; name = "App Store Release"; diff --git a/SessionMessagingKit/Open Groups/OpenGroupManager.swift b/SessionMessagingKit/Open Groups/OpenGroupManager.swift index 5dd854261..922ad2277 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupManager.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupManager.swift @@ -241,6 +241,7 @@ public final class OpenGroupManager: NSObject { publicKey maybePublicKey: String?, for roomToken: String, on server: String, + waitForImageToComplete: Bool = false, using transaction: YapDatabaseReadWriteTransaction, dependencies: OGMDependencies = OGMDependencies(), completion: (() -> ())? = nil @@ -336,10 +337,25 @@ public final class OpenGroupManager: NSObject { let thread = TSGroupThread.getOrCreateThread(with: initialModel, transaction: transaction) thread.groupModel.groupImage = UIImage(data: data) thread.save(with: transaction) + + if waitForImageToComplete { + completion?() + } + } + } + .catch { _ in + if waitForImageToComplete { + completion?() } } .retainUntilComplete() } + else if waitForImageToComplete { + completion?() + } + + // If we want to wait for the image to complete then don't call the completion here + guard !waitForImageToComplete else { return } // Finish completion?() diff --git a/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift b/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift index 98e6ad7a1..d730e9138 100644 --- a/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift +++ b/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift @@ -1060,7 +1060,8 @@ class OpenGroupManagerSpec: QuickSpec { } it("does not stop the poller") { - mockOGMCache.when { $0.pollers }.thenReturn(["testserver": OpenGroupAPI.Poller(for: "testserver")]) + mockOGMCache.when { $0.pollers } + .thenReturn(["testserver": OpenGroupAPI.Poller(for: "testserver")]) openGroupManager .delete( @@ -1082,7 +1083,8 @@ class OpenGroupManagerSpec: QuickSpec { dependencies: dependencies ) - expect(mockStorage).toNot(call { $0.removeOpenGroupServer(name: any(), using: anyAny()) }) + expect(mockStorage) + .toNot(call { $0.removeOpenGroupServer(name: any(), using: anyAny()) }) } it("does not remove the open group public key") { @@ -1234,9 +1236,64 @@ class OpenGroupManagerSpec: QuickSpec { on: "testServer", using: testTransaction, dependencies: dependencies - ) { - didCallComplete = true - } + ) { didCallComplete = true } + + expect(didCallComplete) + .toEventually( + beTrue(), + timeout: .milliseconds(50) + ) + } + + it("calls the room image completion block when waiting but there is no image") { + var didCallComplete: Bool = false + + OpenGroupManager.handlePollInfo( + testPollInfo, + publicKey: TestConstants.publicKey, + for: "testRoom", + on: "testServer", + waitForImageToComplete: true, + using: testTransaction, + dependencies: dependencies + ) { didCallComplete = true } + + expect(didCallComplete) + .toEventually( + beTrue(), + timeout: .milliseconds(50) + ) + } + + it("calls the room image completion block when waiting and there is an image") { + var didCallComplete: Bool = false + + mockStorage.when { $0.getOpenGroupImage(for: any(), on: any()) }.thenReturn(nil) + mockOGMCache.when { $0.groupImagePromises } + .thenReturn(["testServer.testRoom": Promise.value(Data())]) + mockStorage + .when { $0.getOpenGroup(for: any()) } + .thenReturn( + OpenGroup( + server: "testServer", + room: "testRoom", + publicKey: TestConstants.publicKey, + name: "Test", + groupDescription: nil, + imageID: "12", + infoUpdates: 10 + ) + ) + + OpenGroupManager.handlePollInfo( + testPollInfo, + publicKey: TestConstants.publicKey, + for: "testRoom", + on: "testServer", + waitForImageToComplete: true, + using: testTransaction, + dependencies: dependencies + ) { didCallComplete = true } expect(didCallComplete) .toEventually( @@ -1629,6 +1686,7 @@ class OpenGroupManagerSpec: QuickSpec { publicKey: TestConstants.publicKey, for: "testRoom", on: "testServer", + waitForImageToComplete: true, using: testTransaction, dependencies: dependencies ) { didComplete = true } @@ -1700,6 +1758,7 @@ class OpenGroupManagerSpec: QuickSpec { publicKey: TestConstants.publicKey, for: "testRoom", on: "testServer", + waitForImageToComplete: true, using: testTransaction, dependencies: dependencies ) { didComplete = true } @@ -1806,6 +1865,7 @@ class OpenGroupManagerSpec: QuickSpec { publicKey: TestConstants.publicKey, for: "testRoom", on: "testServer", + waitForImageToComplete: true, using: testTransaction, dependencies: dependencies ) { didComplete = true } @@ -1852,6 +1912,7 @@ class OpenGroupManagerSpec: QuickSpec { publicKey: TestConstants.publicKey, for: "testRoom", on: "testServer", + waitForImageToComplete: true, using: testTransaction, dependencies: dependencies ) { didComplete = true } @@ -1923,6 +1984,7 @@ class OpenGroupManagerSpec: QuickSpec { publicKey: TestConstants.publicKey, for: "testRoom", on: "testServer", + waitForImageToComplete: true, using: testTransaction, dependencies: dependencies ) { didComplete = true } @@ -1991,6 +2053,7 @@ class OpenGroupManagerSpec: QuickSpec { publicKey: TestConstants.publicKey, for: "testRoom", on: "testServer", + waitForImageToComplete: true, using: testTransaction, dependencies: dependencies ) { didComplete = true } diff --git a/SessionUtilitiesKit/Utilities/Optional+Utilities.swift b/SessionUtilitiesKit/Utilities/Optional+Utilities.swift new file mode 100644 index 000000000..cd6374b96 --- /dev/null +++ b/SessionUtilitiesKit/Utilities/Optional+Utilities.swift @@ -0,0 +1,23 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import Foundation + +extension Optional { + public func map(_ transform: (Wrapped) throws -> U?) rethrows -> U? { + switch self { + case .some(let value): return try transform(value) + default: return nil + } + } + + public func asType(_ type: R.Type) -> R? { + switch self { + case .some(let value): return (value as? R) + default: return nil + } + } + + public func defaulting(to value: Wrapped) -> Wrapped { + return (self ?? value) + } +} diff --git a/SharedTest/NimbleExtensions.swift b/SharedTest/NimbleExtensions.swift index b18c5160b..d4f820ec9 100644 --- a/SharedTest/NimbleExtensions.swift +++ b/SharedTest/NimbleExtensions.swift @@ -2,6 +2,7 @@ import Foundation import Nimble +import SessionUtilitiesKit public enum CallAmount { case atLeast(times: Int) @@ -192,11 +193,21 @@ fileprivate func generateCallInfo(_ actualExpression: Expression, _ allFunctionsCalled = Array(validInstance.functionConsumer.calls.wrappedValue.keys) - let builder: MockFunctionBuilder = builderCreator(validInstance) - validInstance.functionConsumer.trackCalls = false - maybeFunction = try? builder.build() - desiredFunctionCalls = (validInstance.functionConsumer.calls.wrappedValue[maybeFunction?.name ?? ""] ?? []) - validInstance.functionConsumer.trackCalls = true + // Only check for the specific function calls if there was at least a single + // call (if there weren't any this will likely throw errors when attempting + // to build) + if !allFunctionsCalled.isEmpty { + let builder: MockFunctionBuilder = builderCreator(validInstance) + validInstance.functionConsumer.trackCalls = false + maybeFunction = try? builder.build() + desiredFunctionCalls = validInstance.functionConsumer.calls + .wrappedValue[maybeFunction?.name ?? ""] + .defaulting(to: []) + validInstance.functionConsumer.trackCalls = true + } + else { + desiredFunctionCalls = [] + } } catch { didError = true @@ -216,11 +227,21 @@ fileprivate func generateCallInfo(_ actualExpression: Expression, _ allFunctionsCalled = Array(validInstance.functionConsumer.calls.wrappedValue.keys) - let builder: MockExpectationBuilder = builderCreator(validInstance) - validInstance.functionConsumer.trackCalls = false - maybeFunction = try? builder.build() - desiredFunctionCalls = (validInstance.functionConsumer.calls.wrappedValue[maybeFunction?.name ?? ""] ?? []) - validInstance.functionConsumer.trackCalls = true + // Only check for the specific function calls if there was at least a single + // call (if there weren't any this will likely throw errors when attempting + // to build) + if !allFunctionsCalled.isEmpty { + let builder: MockExpectationBuilder = builderCreator(validInstance) + validInstance.functionConsumer.trackCalls = false + maybeFunction = try? builder.build() + desiredFunctionCalls = validInstance.functionConsumer.calls + .wrappedValue[maybeFunction?.name ?? ""] + .defaulting(to: []) + validInstance.functionConsumer.trackCalls = true + } + else { + desiredFunctionCalls = [] + } #endif return CallInfo(