mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
215 lines
9.7 KiB
Swift
215 lines
9.7 KiB
Swift
//
|
|
// Copyright 2022-2024 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
// Please see LICENSE files in the repository root for full details.
|
|
//
|
|
|
|
@testable import ElementX
|
|
|
|
import Combine
|
|
import Kingfisher
|
|
import XCTest
|
|
|
|
@MainActor
|
|
final class MediaProviderTests: XCTestCase {
|
|
private var mediaLoader: MediaLoaderMock!
|
|
private var imageCache: MockImageCache!
|
|
private var networkMonitor: NetworkMonitorMock!
|
|
|
|
var mediaProvider: MediaProvider!
|
|
|
|
override func setUp() {
|
|
mediaLoader = MediaLoaderMock()
|
|
imageCache = MockImageCache(name: "Test")
|
|
networkMonitor = NetworkMonitorMock()
|
|
|
|
mediaProvider = MediaProvider(mediaLoader: mediaLoader,
|
|
imageCache: imageCache,
|
|
networkMonitor: networkMonitor)
|
|
}
|
|
|
|
func testLoadingRetriedOnReconnection() async throws {
|
|
let testImage = try loadTestImage()
|
|
guard let pngData = testImage.pngData() else {
|
|
XCTFail("Test image should contain valid .png data")
|
|
return
|
|
}
|
|
|
|
let loadTask = try mediaProvider.loadImageRetryingOnReconnection(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"))
|
|
|
|
let connectivitySubject = CurrentValueSubject<NetworkMonitorReachability, Never>(.unreachable)
|
|
|
|
mediaLoader.loadMediaContentForSourceClosure = { _ in
|
|
switch connectivitySubject.value {
|
|
case .unreachable:
|
|
connectivitySubject.send(.reachable)
|
|
throw MediaProviderTestsError.error
|
|
case .reachable:
|
|
return pngData
|
|
}
|
|
}
|
|
|
|
networkMonitor.underlyingReachabilityPublisher = connectivitySubject.asCurrentValuePublisher()
|
|
|
|
let result = try? await loadTask.value
|
|
|
|
XCTAssertNotNil(result)
|
|
XCTAssertEqual(mediaLoader.loadMediaContentForSourceCallsCount, 2)
|
|
}
|
|
|
|
func testLoadingRetriedOnReconnectionCancelsAfterSecondFailure() async throws {
|
|
let loadTask = try mediaProvider.loadImageRetryingOnReconnection(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"))
|
|
|
|
let connectivitySubject = CurrentValueSubject<NetworkMonitorReachability, Never>(.reachable)
|
|
|
|
mediaLoader.loadMediaContentForSourceThrowableError = MediaProviderTestsError.error
|
|
|
|
networkMonitor.underlyingReachabilityPublisher = connectivitySubject.asCurrentValuePublisher()
|
|
|
|
let result = try? await loadTask.value
|
|
|
|
XCTAssertNil(result)
|
|
}
|
|
|
|
func test_whenImageFromSourceWithSourceNil_nilReturned() throws {
|
|
let image = mediaProvider.imageFromSource(nil, size: Avatars.Size.room(on: .timeline).scaledSize)
|
|
XCTAssertNil(image)
|
|
}
|
|
|
|
func test_whenImageFromSourceWithSourceNotNilAndImageCacheContainsImage_ImageIsReturned() throws {
|
|
let avatarSize = Avatars.Size.room(on: .timeline)
|
|
let url = URL.mockMXCImage
|
|
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
|
let imageForKey = UIImage()
|
|
imageCache.retrievedImagesInMemory[key] = imageForKey
|
|
let image = try mediaProvider.imageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"),
|
|
size: avatarSize.scaledSize)
|
|
XCTAssertEqual(image, imageForKey)
|
|
}
|
|
|
|
func test_whenImageFromSourceWithSourceNotNilAndImageNotCached_nilReturned() throws {
|
|
let image = try mediaProvider.imageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
|
|
size: Avatars.Size.room(on: .timeline).scaledSize)
|
|
XCTAssertNil(image)
|
|
}
|
|
|
|
func test_whenLoadImageFromSourceAndImageCacheContainsImage_successIsReturned() async throws {
|
|
let avatarSize = Avatars.Size.room(on: .timeline)
|
|
let url = URL.mockMXCImage
|
|
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
|
let imageForKey = UIImage()
|
|
imageCache.retrievedImagesInMemory[key] = imageForKey
|
|
let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"),
|
|
size: avatarSize.scaledSize)
|
|
XCTAssertEqual(Result.success(imageForKey), result)
|
|
}
|
|
|
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageSucceeds_successIsReturned() async throws {
|
|
let avatarSize = Avatars.Size.room(on: .timeline)
|
|
let url = URL.mockMXCImage
|
|
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
|
let imageForKey = UIImage()
|
|
imageCache.retrievedImages[key] = imageForKey
|
|
let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"),
|
|
size: avatarSize.scaledSize)
|
|
XCTAssertEqual(Result.success(imageForKey), result)
|
|
}
|
|
|
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFails_imageThumbnailIsLoaded() async throws {
|
|
let avatarSize = Avatars.Size.room(on: .timeline)
|
|
let expectedImage = try loadTestImage()
|
|
|
|
mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = expectedImage.pngData()
|
|
|
|
let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
|
|
size: avatarSize.scaledSize)
|
|
switch result {
|
|
case .success(let image):
|
|
XCTAssertEqual(image.pngData(), expectedImage.pngData())
|
|
case .failure:
|
|
XCTFail("Should be success")
|
|
}
|
|
}
|
|
|
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFails_imageIsStored() async throws {
|
|
let avatarSize = Avatars.Size.room(on: .timeline)
|
|
let url = URL.mockMXCImage
|
|
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
|
let expectedImage = try loadTestImage()
|
|
|
|
mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = expectedImage.pngData()
|
|
|
|
_ = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"),
|
|
size: avatarSize.scaledSize)
|
|
let storedImage = try XCTUnwrap(imageCache.storedImages[key])
|
|
XCTAssertEqual(expectedImage.pngData(), storedImage.pngData())
|
|
}
|
|
|
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndNoAvatarSize_imageContentIsLoaded() async throws {
|
|
let expectedImage = try loadTestImage()
|
|
|
|
mediaLoader.loadMediaContentForSourceReturnValue = expectedImage.pngData()
|
|
|
|
let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
|
|
size: nil)
|
|
switch result {
|
|
case .success(let image):
|
|
XCTAssertEqual(image.pngData(), expectedImage.pngData())
|
|
case .failure:
|
|
XCTFail("Should be success")
|
|
}
|
|
}
|
|
|
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndLoadImageThumbnailFails_errorIsThrown() async throws {
|
|
mediaLoader.loadMediaThumbnailForSourceWidthHeightThrowableError = MediaProviderTestsError.error
|
|
|
|
let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
|
|
size: Avatars.Size.room(on: .timeline).scaledSize)
|
|
switch result {
|
|
case .success:
|
|
XCTFail("Should fail")
|
|
case .failure(let error):
|
|
XCTAssertEqual(error, MediaProviderError.failedRetrievingImage)
|
|
}
|
|
}
|
|
|
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndNoAvatarSizeAndLoadImageContentFails_errorIsThrown() async throws {
|
|
mediaLoader.loadMediaContentForSourceThrowableError = MediaProviderTestsError.error
|
|
|
|
let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
|
|
size: nil)
|
|
switch result {
|
|
case .success:
|
|
XCTFail("Should fail")
|
|
case .failure(let error):
|
|
XCTAssertEqual(error, MediaProviderError.failedRetrievingImage)
|
|
}
|
|
}
|
|
|
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndImageThumbnailIsLoadedWithCorruptedData_errorIsThrown() async throws {
|
|
mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = Data()
|
|
|
|
let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
|
|
size: Avatars.Size.room(on: .timeline).scaledSize)
|
|
switch result {
|
|
case .success:
|
|
XCTFail("Should fail")
|
|
case .failure(let error):
|
|
XCTAssertEqual(error, MediaProviderError.invalidImageData)
|
|
}
|
|
}
|
|
|
|
private func loadTestImage() throws -> UIImage {
|
|
guard let path = Bundle(for: Self.self).path(forResource: "test_image", ofType: "png"),
|
|
let image = UIImage(contentsOfFile: path) else {
|
|
throw MediaProviderTestsError.error
|
|
}
|
|
return image
|
|
}
|
|
}
|
|
|
|
private enum MediaProviderTestsError: Error {
|
|
case error
|
|
}
|