twonly-app-dependencies/camera_avfoundation/darwin/Tests/PhotoCaptureTests.swift
2026-10-07 22:18:23 +02:00

248 lines
8.9 KiB
Swift

// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import AVFoundation
import XCTest
@testable import camera_avfoundation
/// Includes test cases related to photo capture operations for Camera class.
final class PhotoCaptureTests: XCTestCase {
private func createCam(with captureSessionQueue: DispatchQueue) -> DefaultCamera {
let configuration = CameraTestUtils.createTestCameraConfiguration()
configuration.captureSessionQueue = captureSessionQueue
return CameraTestUtils.createTestCamera(configuration)
}
func testCaptureToFile_mustReportErrorToResultIfSavePhotoDelegateCompletionsWithError() {
let errorExpectation = expectation(
description: "Must send error to result if save photo delegate completes with error.")
let captureSessionQueue = DispatchQueue(label: "capture_session_queue")
captureSessionQueue.setSpecific(
key: captureSessionQueueSpecificKey, value: captureSessionQueueSpecificValue)
let cam = createCam(with: captureSessionQueue)
let error = NSError(domain: "test", code: 0, userInfo: nil)
let mockOutput = MockCapturePhotoOutput()
mockOutput.capturePhotoWithSettingsStub = { settings, photoDelegate in
let delegate =
cam.inProgressSavePhotoDelegates[settings.uniqueID]
// Completion runs on IO queue.
let ioQueue = DispatchQueue(label: "io_queue")
ioQueue.async {
delegate?.completionHandler(nil, error)
}
}
cam.capturePhotoOutput = mockOutput
// `Camera.captureToFile` runs on capture session queue.
captureSessionQueue.async {
cam.captureToFile { result in
switch result {
case .success(_):
XCTFail("Expected failure")
case .failure(_):
break
}
errorExpectation.fulfill()
}
}
waitForExpectations(timeout: 30, handler: nil)
}
func testCaptureToFile_mustReportPathToResultIfSavePhotoDelegateCompletionsWithPath() {
let pathExpectation = expectation(
description: "Must send file path to result if save photo delegate completes with file path.")
let captureSessionQueue = DispatchQueue(label: "capture_session_queue")
captureSessionQueue.setSpecific(
key: captureSessionQueueSpecificKey, value: captureSessionQueueSpecificValue)
let cam = createCam(with: captureSessionQueue)
let filePath = "test"
let mockOutput = MockCapturePhotoOutput()
mockOutput.capturePhotoWithSettingsStub = { settings, photoDelegate in
let delegate =
cam.inProgressSavePhotoDelegates[settings.uniqueID]
// Completion runs on IO queue.
let ioQueue = DispatchQueue(label: "io_queue")
ioQueue.async {
delegate?.completionHandler(filePath, nil)
}
}
cam.capturePhotoOutput = mockOutput
// `Camera.captureToFile` runs on capture session queue.
captureSessionQueue.async {
cam.captureToFile { result in
switch result {
case .success(let result):
XCTAssertEqual(result, filePath)
case .failure(_):
XCTFail("Unexpected failure")
}
pathExpectation.fulfill()
}
}
waitForExpectations(timeout: 30, handler: nil)
}
func testCaptureToFile_mustReportFileExtensionWithHeifWhenHEVCIsAvailableAndFileFormatIsHEIF() {
let expectation = self.expectation(
description: "Test must set extension to heif if availablePhotoCodecTypes contains HEVC.")
let captureSessionQueue = DispatchQueue(label: "capture_session_queue")
captureSessionQueue.setSpecific(
key: captureSessionQueueSpecificKey, value: captureSessionQueueSpecificValue)
let cam = createCam(with: captureSessionQueue)
cam.setImageFileFormat(PlatformImageFileFormat.heif)
let mockOutput = MockCapturePhotoOutput()
mockOutput.availablePhotoCodecTypes = [AVVideoCodecType.hevc]
mockOutput.capturePhotoWithSettingsStub = { settings, photoDelegate in
let delegate =
cam.inProgressSavePhotoDelegates[settings.uniqueID]
// Completion runs on IO queue.
let ioQueue = DispatchQueue(label: "io_queue")
ioQueue.async {
delegate?.completionHandler(delegate?.filePath, nil)
}
}
cam.capturePhotoOutput = mockOutput
// `Camera.captureToFile` runs on capture session queue.
captureSessionQueue.async {
cam.captureToFile { result in
if let filePath = self.assertSuccess(result) {
XCTAssertEqual((filePath as NSString).pathExtension, "heif")
}
expectation.fulfill()
}
}
waitForExpectations(timeout: 30, handler: nil)
}
func testCaptureToFile_mustReportFileExtensionWithJpgWhenHEVCNotAvailableAndFileFormatIsHEIF() {
let expectation = self.expectation(
description:
"Test must set extension to jpg if availablePhotoCodecTypes does not contain HEVC.")
let captureSessionQueue = DispatchQueue(label: "capture_session_queue")
captureSessionQueue.setSpecific(
key: captureSessionQueueSpecificKey, value: captureSessionQueueSpecificValue)
let cam = createCam(with: captureSessionQueue)
cam.setImageFileFormat(PlatformImageFileFormat.heif)
let mockOutput = MockCapturePhotoOutput()
mockOutput.capturePhotoWithSettingsStub = { settings, photoDelegate in
let delegate =
cam.inProgressSavePhotoDelegates[settings.uniqueID]
// Completion runs on IO queue.
let ioQueue = DispatchQueue(label: "io_queue")
ioQueue.async {
delegate?.completionHandler(delegate?.filePath, nil)
}
}
cam.capturePhotoOutput = mockOutput
// `Camera.captureToFile` runs on capture session queue.
captureSessionQueue.async {
cam.captureToFile { result in
if let filePath = self.assertSuccess(result) {
XCTAssertEqual((filePath as NSString).pathExtension, "jpg")
}
expectation.fulfill()
}
}
waitForExpectations(timeout: 30, handler: nil)
}
func testCaptureToFile_handlesTorchMode() {
let pathExpectation = expectation(
description: "Must send file path to result if save photo delegate completes with file path.")
let setTorchExpectation = expectation(
description: "Should set torch mode to AVCaptureTorchModeOn.")
let captureDeviceMock = MockCaptureDevice()
captureDeviceMock.hasTorch = true
captureDeviceMock.isTorchAvailable = true
captureDeviceMock.getTorchModeStub = { .auto }
captureDeviceMock.setTorchModeStub = { mode in
if mode == .on {
setTorchExpectation.fulfill()
}
}
let captureSessionQueue = DispatchQueue(label: "capture_session_queue")
captureSessionQueue.setSpecific(
key: captureSessionQueueSpecificKey, value: captureSessionQueueSpecificValue)
let configuration = CameraTestUtils.createTestCameraConfiguration()
configuration.captureSessionQueue = captureSessionQueue
configuration.videoCaptureDeviceFactory = { _ in captureDeviceMock }
let cam = CameraTestUtils.createTestCamera(configuration)
let filePath = "test"
let mockOutput = MockCapturePhotoOutput()
mockOutput.capturePhotoWithSettingsStub = { settings, photoDelegate in
let delegate =
cam.inProgressSavePhotoDelegates[settings.uniqueID]
// Completion runs on IO queue.
let ioQueue = DispatchQueue(label: "io_queue")
ioQueue.async {
delegate?.completionHandler(filePath, nil)
}
}
cam.capturePhotoOutput = mockOutput
// `Camera.captureToFile` runs on capture session queue.
captureSessionQueue.async {
cam.setFlashMode(.torch) { _ in }
cam.captureToFile { result in
if let result = self.assertSuccess(result) {
XCTAssertEqual(result, filePath)
}
pathExpectation.fulfill()
}
}
waitForExpectations(timeout: 30, handler: nil)
}
func testCaptureToFile_mustSavePhotoToCameraPicturesDirectory() {
let expectedPath = "/tmp/camera/pictures/"
let expectation = self.expectation(
description: "Photo must be saved to \(expectedPath) directory.")
let captureSessionQueue = DispatchQueue(label: "capture_session_queue")
captureSessionQueue.setSpecific(
key: captureSessionQueueSpecificKey, value: captureSessionQueueSpecificValue)
let cam = createCam(with: captureSessionQueue)
let mockOutput = MockCapturePhotoOutput()
mockOutput.capturePhotoWithSettingsStub = { settings, photoDelegate in
let delegate = cam.inProgressSavePhotoDelegates[settings.uniqueID]
let ioQueue = DispatchQueue(label: "io_queue")
ioQueue.async {
delegate?.completionHandler(delegate?.filePath, nil)
}
}
cam.capturePhotoOutput = mockOutput
captureSessionQueue.async {
cam.captureToFile { result in
if let filePath = self.assertSuccess(result) {
XCTAssertTrue(
filePath.contains(expectedPath)
)
}
expectation.fulfill()
}
}
waitForExpectations(timeout: 30, handler: nil)
}
}