Handle media source validation more gracefully. (#3571)

* Handle media source validation more gracefully.

* Fix unit tests.
This commit is contained in:
Doug 2024-11-29 12:42:27 +00:00 committed by GitHub
parent c8627cfd64
commit 49a94e0bdb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 95 additions and 90 deletions

View File

@ -21,7 +21,7 @@ enum LoadableImageMediaType: Equatable {
} }
struct LoadableImage<TransformerView: View, PlaceholderView: View>: View { struct LoadableImage<TransformerView: View, PlaceholderView: View>: View {
private let mediaSource: MediaSourceProxy private let mediaSource: MediaSourceProxy?
private let mediaType: LoadableImageMediaType private let mediaType: LoadableImageMediaType
private let blurhash: String? private let blurhash: String?
private let size: CGSize? private let size: CGSize?
@ -60,27 +60,31 @@ struct LoadableImage<TransformerView: View, PlaceholderView: View>: View {
mediaProvider: MediaProviderProtocol?, mediaProvider: MediaProviderProtocol?,
transformer: @escaping (AnyView) -> TransformerView = { $0 }, transformer: @escaping (AnyView) -> TransformerView = { $0 },
placeholder: @escaping () -> PlaceholderView) { placeholder: @escaping () -> PlaceholderView) {
self.init(mediaSource: MediaSourceProxy(url: url, mimeType: nil), mediaSource = try? MediaSourceProxy(url: url, mimeType: nil)
mediaType: mediaType, self.mediaType = mediaType
blurhash: blurhash, self.blurhash = blurhash
size: size, self.size = size
mediaProvider: mediaProvider, self.mediaProvider = mediaProvider
transformer: transformer, self.transformer = transformer
placeholder: placeholder) self.placeholder = placeholder
} }
var body: some View { var body: some View {
LoadableImageContent(mediaSource: mediaSource, if let mediaSource {
mediaType: mediaType, LoadableImageContent(mediaSource: mediaSource,
blurhash: blurhash, mediaType: mediaType,
size: size, blurhash: blurhash,
mediaProvider: mediaProvider, size: size,
transformer: transformer, mediaProvider: mediaProvider,
placeholder: placeholder) transformer: transformer,
.id(stableMediaIdentifier) placeholder: placeholder)
.id(stableMediaIdentifier)
} else {
placeholder()
}
} }
private var stableMediaIdentifier: String { private var stableMediaIdentifier: String? {
switch mediaType { switch mediaType {
case .timelineItem(let uniqueID): case .timelineItem(let uniqueID):
// Consider media for the same item to be the same view // Consider media for the same item to be the same view
@ -88,7 +92,7 @@ struct LoadableImage<TransformerView: View, PlaceholderView: View>: View {
default: default:
// Binds the lifecycle of the LoadableImage to the associated URL. // Binds the lifecycle of the LoadableImage to the associated URL.
// This fixes the problem of the cache returning old values after a change in the URL. // This fixes the problem of the cache returning old values after a change in the URL.
mediaSource.url.absoluteString mediaSource?.url.absoluteString
} }
} }
} }

View File

@ -363,7 +363,8 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
} }
// We don't actually know the mime type here, assume it's an image. // We don't actually know the mime type here, assume it's an image.
if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: url, mimeType: "image/jpeg")) { if let mediaSource = try? MediaSourceProxy(url: url, mimeType: "image/jpeg"),
case let .success(file) = await mediaProvider.loadFileFromSource(mediaSource) {
state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: roomProxy.infoPublisher.value.displayName) state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: roomProxy.infoPublisher.value.displayName)
} }
} }

View File

@ -165,7 +165,8 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
defer { userIndicatorController.retractIndicatorWithId(loadingIndicatorIdentifier) } defer { userIndicatorController.retractIndicatorWithId(loadingIndicatorIdentifier) }
// We don't actually know the mime type here, assume it's an image. // We don't actually know the mime type here, assume it's an image.
if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: url, mimeType: "image/jpeg")) { if let mediaSource = try? MediaSourceProxy(url: url, mimeType: "image/jpeg"),
case let .success(file) = await mediaProvider.loadFileFromSource(mediaSource) {
state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: roomMemberProxy.displayName) state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: roomMemberProxy.displayName)
} }
} }

View File

@ -104,7 +104,8 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
defer { hideLoadingIndicator() } defer { hideLoadingIndicator() }
// We don't actually know the mime type here, assume it's an image. // We don't actually know the mime type here, assume it's an image.
if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: url, mimeType: "image/jpeg")) { if let mediaSource = try? MediaSourceProxy(url: url, mimeType: "image/jpeg"),
case let .success(file) = await mediaProvider.loadFileFromSource(mediaSource) {
state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: userProfile.displayName) state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: userProfile.displayName)
} }
} }

View File

@ -23,11 +23,8 @@ struct MediaSourceProxy: Hashable {
self.mimeType = mimeType self.mimeType = mimeType
} }
init(url: URL, mimeType: String?) { init(url: URL, mimeType: String?) throws {
guard let mediaSource = try? MediaSource.fromUrl(url: url.absoluteString) else { underlyingSource = try MediaSource.fromUrl(url: url.absoluteString)
fatalError("Unable to create MediaSource from URL: \(url.absoluteString)")
}
underlyingSource = mediaSource
self.url = URL(string: underlyingSource.url()) self.url = URL(string: underlyingSource.url())
self.mimeType = mimeType self.mimeType = mimeType
} }

View File

@ -55,7 +55,7 @@ struct NotificationItemProxy: NotificationItemProxyProtocol {
var senderAvatarMediaSource: MediaSourceProxy? { var senderAvatarMediaSource: MediaSourceProxy? {
if let senderAvatarURLString = notificationItem.senderInfo.avatarUrl, if let senderAvatarURLString = notificationItem.senderInfo.avatarUrl,
let senderAvatarURL = URL(string: senderAvatarURLString) { let senderAvatarURL = URL(string: senderAvatarURLString) {
return MediaSourceProxy(url: senderAvatarURL, mimeType: nil) return try? MediaSourceProxy(url: senderAvatarURL, mimeType: nil)
} }
return nil return nil
} }
@ -63,7 +63,7 @@ struct NotificationItemProxy: NotificationItemProxyProtocol {
var roomAvatarMediaSource: MediaSourceProxy? { var roomAvatarMediaSource: MediaSourceProxy? {
if let roomAvatarURLString = notificationItem.roomInfo.avatarUrl, if let roomAvatarURLString = notificationItem.roomInfo.avatarUrl,
let roomAvatarURL = URL(string: roomAvatarURLString) { let roomAvatarURL = URL(string: roomAvatarURLString) {
return MediaSourceProxy(url: roomAvatarURL, mimeType: nil) return try? MediaSourceProxy(url: roomAvatarURL, mimeType: nil)
} }
return nil return nil
} }

View File

@ -195,9 +195,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
} }
} }
if let avatarURL { if let avatarURL, let mediaSource = try? MediaSourceProxy(url: avatarURL, mimeType: nil) {
let mediaSource = MediaSourceProxy(url: avatarURL, mimeType: nil)
if case let .success(avatarData) = await mediaProvider.loadThumbnailForSource(source: mediaSource, size: .init(width: 100, height: 100)) { if case let .success(avatarData) = await mediaProvider.loadThumbnailForSource(source: mediaSource, size: .init(width: 100, height: 100)) {
sendMessageIntent.setImage(INImage(imageData: avatarData), forParameterNamed: \.speakableGroupName) sendMessageIntent.setImage(INImage(imageData: avatarData), forParameterNamed: \.speakableGroupName)
} else { } else {

View File

@ -243,11 +243,15 @@ struct VideoInfoProxy: Hashable {
} }
static var mockVideo: VideoInfoProxy { static var mockVideo: VideoInfoProxy {
.init(source: .init(url: .mockMXCVideo, mimeType: nil), guard let mediaSource = try? MediaSourceProxy(url: .mockMXCVideo, mimeType: nil) else {
duration: 100, fatalError("Invalid mock media source URL")
size: .init(width: 1920, height: 1080), }
aspectRatio: 1.78,
mimeType: nil) return .init(source: mediaSource,
duration: 100,
size: .init(width: 1920, height: 1080),
aspectRatio: 1.78,
mimeType: nil)
} }
} }
@ -269,10 +273,6 @@ struct ImageInfoProxy: Hashable {
self.init(source: .init(source: source, mimeType: mimeType), width: width, height: height, mimeType: mimeType) self.init(source: .init(source: source, mimeType: mimeType), width: width, height: height, mimeType: mimeType)
} }
init(url: URL, width: UInt64?, height: UInt64?, mimeType: String?) {
self.init(source: .init(url: url, mimeType: mimeType), width: width, height: height, mimeType: mimeType)
}
init(source: MediaSourceProxy, width: UInt64?, height: UInt64?, mimeType: String?) { init(source: MediaSourceProxy, width: UInt64?, height: UInt64?, mimeType: String?) {
self.source = source self.source = source
@ -292,17 +292,25 @@ struct ImageInfoProxy: Hashable {
} }
static var mockImage: ImageInfoProxy { static var mockImage: ImageInfoProxy {
.init(source: .init(url: .mockMXCImage, mimeType: "image/png"), guard let mediaSource = try? MediaSourceProxy(url: .mockMXCImage, mimeType: "image/png") else {
size: .init(width: 100, height: 100), fatalError("Invalid mock media source URL")
aspectRatio: 1, }
mimeType: "image/png")
return .init(source: mediaSource,
size: .init(width: 100, height: 100),
aspectRatio: 1,
mimeType: "image/png")
} }
static var mockThumbnail: ImageInfoProxy { static var mockThumbnail: ImageInfoProxy {
.init(source: .init(url: .mockMXCImage, mimeType: nil), guard let mediaSource = try? MediaSourceProxy(url: .mockMXCImage, mimeType: "image/png") else {
size: nil, fatalError("Invalid mock media source URL")
aspectRatio: nil, }
mimeType: nil)
return .init(source: mediaSource,
size: nil,
aspectRatio: nil,
mimeType: nil)
} }
} }

View File

@ -33,12 +33,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
case .redactedMessage: case .redactedMessage:
return buildRedactedTimelineItem(eventItemProxy, isOutgoing) return buildRedactedTimelineItem(eventItemProxy, isOutgoing)
case .sticker(let body, let imageInfo, let mediaSource): case .sticker(let body, let imageInfo, let mediaSource):
guard let url = URL(string: mediaSource.url()) else { return buildStickerTimelineItem(eventItemProxy, body, imageInfo, mediaSource, isOutgoing)
MXLog.error("Invalid sticker url string: \(mediaSource.url())")
return buildUnsupportedTimelineItem(eventItemProxy, "m.sticker", "Invalid Sticker URL", isOutgoing)
}
return buildStickerTimelineItem(eventItemProxy, body, imageInfo, url, isOutgoing)
case .failedToParseMessageLike(let eventType, let error): case .failedToParseMessageLike(let eventType, let error):
return buildUnsupportedTimelineItem(eventItemProxy, eventType, error, isOutgoing) return buildUnsupportedTimelineItem(eventItemProxy, eventType, error, isOutgoing)
case .failedToParseState(let eventType, _, let error): case .failedToParseState(let eventType, _, let error):
@ -119,9 +114,9 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
private func buildStickerTimelineItem(_ eventItemProxy: EventTimelineItemProxy, private func buildStickerTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
_ body: String, _ body: String,
_ info: MatrixRustSDK.ImageInfo, _ info: MatrixRustSDK.ImageInfo,
_ imageURL: URL, _ mediaSource: MediaSource,
_ isOutgoing: Bool) -> RoomTimelineItemProtocol { _ isOutgoing: Bool) -> RoomTimelineItemProtocol {
let imageInfo = ImageInfoProxy(url: imageURL, width: info.width, height: info.height, mimeType: info.mimetype) let imageInfo = ImageInfoProxy(source: mediaSource, width: info.width, height: info.height, mimeType: info.mimetype)
return StickerRoomTimelineItem(id: eventItemProxy.id, return StickerRoomTimelineItem(id: eventItemProxy.id,
body: body, body: body,

View File

@ -10,12 +10,12 @@ import MatrixRustSDK
import XCTest import XCTest
final class MediaLoaderTests: XCTestCase { final class MediaLoaderTests: XCTestCase {
func testMediaRequestCoalescing() async { func testMediaRequestCoalescing() async throws {
let mediaLoadingClient = ClientSDKMock() let mediaLoadingClient = ClientSDKMock()
mediaLoadingClient.getMediaContentMediaSourceReturnValue = Data() mediaLoadingClient.getMediaContentMediaSourceReturnValue = Data()
let mediaLoader = MediaLoader(client: mediaLoadingClient) let mediaLoader = MediaLoader(client: mediaLoadingClient)
let mediaSource = MediaSourceProxy(url: .mockMXCFile, mimeType: nil) let mediaSource = try MediaSourceProxy(url: .mockMXCFile, mimeType: nil)
do { do {
for _ in 1...10 { for _ in 1...10 {
@ -28,12 +28,12 @@ final class MediaLoaderTests: XCTestCase {
} }
} }
func testMediaThumbnailRequestCoalescing() async { func testMediaThumbnailRequestCoalescing() async throws {
let mediaLoadingClient = ClientSDKMock() let mediaLoadingClient = ClientSDKMock()
mediaLoadingClient.getMediaThumbnailMediaSourceWidthHeightReturnValue = Data() mediaLoadingClient.getMediaThumbnailMediaSourceWidthHeightReturnValue = Data()
let mediaLoader = MediaLoader(client: mediaLoadingClient) let mediaLoader = MediaLoader(client: mediaLoadingClient)
let mediaSource = MediaSourceProxy(url: .mockMXCImage, mimeType: nil) let mediaSource = try MediaSourceProxy(url: .mockMXCImage, mimeType: nil)
do { do {
for _ in 1...10 { for _ in 1...10 {

View File

@ -36,7 +36,7 @@ final class MediaProviderTests: XCTestCase {
return return
} }
let loadTask = mediaProvider.loadImageRetryingOnReconnection(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg")) let loadTask = try mediaProvider.loadImageRetryingOnReconnection(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"))
let connectivitySubject = CurrentValueSubject<NetworkMonitorReachability, Never>(.unreachable) let connectivitySubject = CurrentValueSubject<NetworkMonitorReachability, Never>(.unreachable)
@ -59,7 +59,7 @@ final class MediaProviderTests: XCTestCase {
} }
func testLoadingRetriedOnReconnectionCancelsAfterSecondFailure() async throws { func testLoadingRetriedOnReconnectionCancelsAfterSecondFailure() async throws {
let loadTask = mediaProvider.loadImageRetryingOnReconnection(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg")) let loadTask = try mediaProvider.loadImageRetryingOnReconnection(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"))
let connectivitySubject = CurrentValueSubject<NetworkMonitorReachability, Never>(.reachable) let connectivitySubject = CurrentValueSubject<NetworkMonitorReachability, Never>(.reachable)
@ -73,7 +73,7 @@ final class MediaProviderTests: XCTestCase {
} }
func test_whenImageFromSourceWithSourceNil_nilReturned() throws { func test_whenImageFromSourceWithSourceNil_nilReturned() throws {
let image = mediaProvider.imageFromSource(nil, size: Avatars.Size.room(on: .timeline).scaledSize) let image = try mediaProvider.imageFromSource(nil, size: Avatars.Size.room(on: .timeline).scaledSize)
XCTAssertNil(image) XCTAssertNil(image)
} }
@ -83,14 +83,14 @@ final class MediaProviderTests: XCTestCase {
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}" let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
let imageForKey = UIImage() let imageForKey = UIImage()
imageCache.retrievedImagesInMemory[key] = imageForKey imageCache.retrievedImagesInMemory[key] = imageForKey
let image = mediaProvider.imageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"), let image = try mediaProvider.imageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"),
size: avatarSize.scaledSize) size: avatarSize.scaledSize)
XCTAssertEqual(image, imageForKey) XCTAssertEqual(image, imageForKey)
} }
func test_whenImageFromSourceWithSourceNotNilAndImageNotCached_nilReturned() throws { func test_whenImageFromSourceWithSourceNotNilAndImageNotCached_nilReturned() throws {
let image = mediaProvider.imageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"), let image = try mediaProvider.imageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
size: Avatars.Size.room(on: .timeline).scaledSize) size: Avatars.Size.room(on: .timeline).scaledSize)
XCTAssertNil(image) XCTAssertNil(image)
} }
@ -100,8 +100,8 @@ final class MediaProviderTests: XCTestCase {
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}" let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
let imageForKey = UIImage() let imageForKey = UIImage()
imageCache.retrievedImagesInMemory[key] = imageForKey imageCache.retrievedImagesInMemory[key] = imageForKey
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"), let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"),
size: avatarSize.scaledSize) size: avatarSize.scaledSize)
XCTAssertEqual(Result.success(imageForKey), result) XCTAssertEqual(Result.success(imageForKey), result)
} }
@ -111,8 +111,8 @@ final class MediaProviderTests: XCTestCase {
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}" let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
let imageForKey = UIImage() let imageForKey = UIImage()
imageCache.retrievedImages[key] = imageForKey imageCache.retrievedImages[key] = imageForKey
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"), let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"),
size: avatarSize.scaledSize) size: avatarSize.scaledSize)
XCTAssertEqual(Result.success(imageForKey), result) XCTAssertEqual(Result.success(imageForKey), result)
} }
@ -122,8 +122,8 @@ final class MediaProviderTests: XCTestCase {
mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = expectedImage.pngData() mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = expectedImage.pngData()
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"), let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
size: avatarSize.scaledSize) size: avatarSize.scaledSize)
switch result { switch result {
case .success(let image): case .success(let image):
XCTAssertEqual(image.pngData(), expectedImage.pngData()) XCTAssertEqual(image.pngData(), expectedImage.pngData())
@ -140,8 +140,8 @@ final class MediaProviderTests: XCTestCase {
mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = expectedImage.pngData() mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = expectedImage.pngData()
_ = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"), _ = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"),
size: avatarSize.scaledSize) size: avatarSize.scaledSize)
let storedImage = try XCTUnwrap(imageCache.storedImages[key]) let storedImage = try XCTUnwrap(imageCache.storedImages[key])
XCTAssertEqual(expectedImage.pngData(), storedImage.pngData()) XCTAssertEqual(expectedImage.pngData(), storedImage.pngData())
} }
@ -151,8 +151,8 @@ final class MediaProviderTests: XCTestCase {
mediaLoader.loadMediaContentForSourceReturnValue = expectedImage.pngData() mediaLoader.loadMediaContentForSourceReturnValue = expectedImage.pngData()
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"), let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
size: nil) size: nil)
switch result { switch result {
case .success(let image): case .success(let image):
XCTAssertEqual(image.pngData(), expectedImage.pngData()) XCTAssertEqual(image.pngData(), expectedImage.pngData())
@ -164,8 +164,8 @@ final class MediaProviderTests: XCTestCase {
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndLoadImageThumbnailFails_errorIsThrown() async throws { func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndLoadImageThumbnailFails_errorIsThrown() async throws {
mediaLoader.loadMediaThumbnailForSourceWidthHeightThrowableError = MediaProviderTestsError.error mediaLoader.loadMediaThumbnailForSourceWidthHeightThrowableError = MediaProviderTestsError.error
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"), let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
size: Avatars.Size.room(on: .timeline).scaledSize) size: Avatars.Size.room(on: .timeline).scaledSize)
switch result { switch result {
case .success: case .success:
XCTFail("Should fail") XCTFail("Should fail")
@ -177,8 +177,8 @@ final class MediaProviderTests: XCTestCase {
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndNoAvatarSizeAndLoadImageContentFails_errorIsThrown() async throws { func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndNoAvatarSizeAndLoadImageContentFails_errorIsThrown() async throws {
mediaLoader.loadMediaContentForSourceThrowableError = MediaProviderTestsError.error mediaLoader.loadMediaContentForSourceThrowableError = MediaProviderTestsError.error
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"), let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
size: nil) size: nil)
switch result { switch result {
case .success: case .success:
XCTFail("Should fail") XCTFail("Should fail")
@ -190,8 +190,8 @@ final class MediaProviderTests: XCTestCase {
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndImageThumbnailIsLoadedWithCorruptedData_errorIsThrown() async throws { func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndImageThumbnailIsLoadedWithCorruptedData_errorIsThrown() async throws {
mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = Data() mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = Data()
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"), let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
size: Avatars.Size.room(on: .timeline).scaledSize) size: Avatars.Size.room(on: .timeline).scaledSize)
switch result { switch result {
case .success: case .success:
XCTFail("Should fail") XCTFail("Should fail")

View File

@ -26,7 +26,7 @@ class VoiceMessageCacheTests: XCTestCase {
voiceMessageCache.clearCache() voiceMessageCache.clearCache()
fileManager = FileManager.default fileManager = FileManager.default
mediaSource = MediaSourceProxy(url: someURL, mimeType: "audio/ogg") mediaSource = try MediaSourceProxy(url: someURL, mimeType: "audio/ogg")
// Create the temporary directory we will use // Create the temporary directory we will use
try fileManager.createDirectory(at: testTemporaryDirectory, withIntermediateDirectories: true) try fileManager.createDirectory(at: testTemporaryDirectory, withIntermediateDirectories: true)

View File

@ -28,7 +28,7 @@ class VoiceMessageMediaManagerTests: XCTestCase {
func testLoadVoiceMessageFromSourceUnsupportedMedia() async throws { func testLoadVoiceMessageFromSourceUnsupportedMedia() async throws {
// Only "audio/ogg" file are supported // Only "audio/ogg" file are supported
let unsupportedMediaSource = MediaSourceProxy(url: someURL, mimeType: "audio/wav") let unsupportedMediaSource = try MediaSourceProxy(url: someURL, mimeType: "audio/wav")
do { do {
_ = try await voiceMessageMediaManager.loadVoiceMessageFromSource(unsupportedMediaSource, body: nil) _ = try await voiceMessageMediaManager.loadVoiceMessageFromSource(unsupportedMediaSource, body: nil)
XCTFail("A `VoiceMessageMediaManagerError.unsupportedMimeTye` error is expected") XCTFail("A `VoiceMessageMediaManagerError.unsupportedMimeTye` error is expected")
@ -49,7 +49,7 @@ class VoiceMessageMediaManagerTests: XCTestCase {
let cachedConvertedFileURL = URL("/some/url/cached_converted_file.m4a") let cachedConvertedFileURL = URL("/some/url/cached_converted_file.m4a")
voiceMessageCache.fileURLForReturnValue = nil voiceMessageCache.fileURLForReturnValue = nil
let mediaSource = MediaSourceProxy(url: someURL, mimeType: "audio/ogg; codecs=opus") let mediaSource = try MediaSourceProxy(url: someURL, mimeType: "audio/ogg; codecs=opus")
mediaProvider.loadFileFromSourceFilenameReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile)) mediaProvider.loadFileFromSourceFilenameReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile))
voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL) voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL)
@ -67,7 +67,7 @@ class VoiceMessageMediaManagerTests: XCTestCase {
func testLoadVoiceMessageFromSourceAlreadyCached() async throws { func testLoadVoiceMessageFromSourceAlreadyCached() async throws {
// Check if the file is already present in cache // Check if the file is already present in cache
voiceMessageCache.fileURLForReturnValue = URL("/converted_file/url") voiceMessageCache.fileURLForReturnValue = URL("/converted_file/url")
let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType) let mediaSource = try MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType)
let url = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil) let url = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil)
XCTAssertEqual(url, URL("/converted_file/url")) XCTAssertEqual(url, URL("/converted_file/url"))
// The file must have be search in the cache // The file must have be search in the cache
@ -81,7 +81,7 @@ class VoiceMessageMediaManagerTests: XCTestCase {
// An error must be reported if the file cannot be retrieved // An error must be reported if the file cannot be retrieved
do { do {
voiceMessageCache.fileURLForReturnValue = nil voiceMessageCache.fileURLForReturnValue = nil
let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType) let mediaSource = try MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType)
_ = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil) _ = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil)
XCTFail("A `MediaProviderError.failedRetrievingFile` error is expected") XCTFail("A `MediaProviderError.failedRetrievingFile` error is expected")
} catch { } catch {
@ -102,7 +102,7 @@ class VoiceMessageMediaManagerTests: XCTestCase {
// Check if the file is not already present in cache // Check if the file is not already present in cache
voiceMessageCache.fileURLForReturnValue = nil voiceMessageCache.fileURLForReturnValue = nil
let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType) let mediaSource = try MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType)
mediaProvider.loadFileFromSourceFilenameReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile)) mediaProvider.loadFileFromSourceFilenameReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile))
let audioConverter = AudioConverterMock() let audioConverter = AudioConverterMock()
voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL) voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL)
@ -145,7 +145,7 @@ class VoiceMessageMediaManagerTests: XCTestCase {
voiceMessageCache: voiceMessageCache, voiceMessageCache: voiceMessageCache,
audioConverter: audioConverter) audioConverter: audioConverter)
let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType) let mediaSource = try MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType)
for _ in 0..<10 { for _ in 0..<10 {
let url = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil) let url = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil)
XCTAssertEqual(url, cachedConvertedFileURL) XCTAssertEqual(url, cachedConvertedFileURL)