Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,161 +8,165 @@ import XCTest

@testable import camera_avfoundation

private final class MockUIDevice: UIDevice {
var mockOrientation: UIDeviceOrientation = .unknown
#if os(iOS)
private final class MockUIDevice: UIDevice {
var mockOrientation: UIDeviceOrientation = .unknown

override var orientation: UIDeviceOrientation {
return mockOrientation
}
}

final class CameraOrientationTests: XCTestCase {
private func createCameraPlugin() -> (
cameraPlugin: CameraPlugin,
mockCamera: MockCamera,
mockEventAPI: MockGlobalEventApi,
mockDevice: MockCaptureDevice,
mockDeviceDiscoverer: MockCameraDeviceDiscoverer,
captureSessionQueue: DispatchQueue
) {
let mockDevice = MockCaptureDevice()
let mockCamera = MockCamera()
let mockEventAPI = MockGlobalEventApi()
let mockDeviceDiscoverer = MockCameraDeviceDiscoverer()
let captureSessionQueue = DispatchQueue(label: "io.flutter.camera.captureSessionQueue")

let cameraPlugin = CameraPlugin(
registry: MockFlutterTextureRegistry(),
messenger: MockFlutterBinaryMessenger(),
globalAPI: mockEventAPI,
deviceDiscoverer: mockDeviceDiscoverer,
permissionManager: MockCameraPermissionManager(),
deviceFactory: { _ in mockDevice },
captureSessionFactory: { MockCaptureSession() },
captureDeviceInputFactory: MockCaptureDeviceInputFactory(),
captureSessionQueue: captureSessionQueue
)
cameraPlugin.camera = mockCamera

return (
cameraPlugin,
mockCamera,
mockEventAPI,
mockDevice,
mockDeviceDiscoverer,
captureSessionQueue
)
}

private func sendOrientation(
_ orientation: UIDeviceOrientation,
to cameraPlugin: CameraPlugin,
captureSessionQueue: DispatchQueue
) {
cameraPlugin.orientationChanged(createMockNotification(for: orientation))
waitForQueueRoundTrip(with: captureSessionQueue)
}

private func createMockNotification(for deviceOrientation: UIDeviceOrientation) -> Notification {
let mockDevice = MockUIDevice()
mockDevice.mockOrientation = deviceOrientation
return Notification(name: Notification.Name("orientation_test"), object: mockDevice)
}

func testOrientationNotifications() {
let (cameraPlugin, _, mockEventAPI, _, _, captureSessionQueue) = createCameraPlugin()

sendOrientation(.portraitUpsideDown, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertEqual(mockEventAPI.lastOrientation, .portraitDown)
sendOrientation(.portrait, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertEqual(mockEventAPI.lastOrientation, .portraitUp)
sendOrientation(.landscapeLeft, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertEqual(mockEventAPI.lastOrientation, .landscapeLeft)
sendOrientation(.landscapeRight, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertEqual(mockEventAPI.lastOrientation, .landscapeRight)
}

func testOrientationNotificationsNotCalledForFaceUp() {
let (cameraPlugin, _, mockEventAPI, _, _, captureSessionQueue) = createCameraPlugin()
sendOrientation(.faceUp, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertFalse(mockEventAPI.deviceOrientationChangedCalled)
}

func testOrientationNotificationsNotCalledForFaceDown() {
let (cameraPlugin, _, mockEventAPI, _, _, captureSessionQueue) = createCameraPlugin()
sendOrientation(.faceDown, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertFalse(mockEventAPI.deviceOrientationChangedCalled)
}

func testOrientationUpdateMustBeOnCaptureSessionQueue() {
let queueExpectation = expectation(
description: "Orientation update must happen on the capture session queue")
let (cameraPlugin, mockCamera, _, _, _, captureSessionQueue) = createCameraPlugin()
let captureSessionQueueSpecific = DispatchSpecificKey<Void>()
captureSessionQueue.setSpecific(
key: captureSessionQueueSpecific,
value: ())

mockCamera.setDeviceOrientationStub = { orientation in
if DispatchQueue.getSpecific(key: captureSessionQueueSpecific) != nil {
queueExpectation.fulfill()
}
override var orientation: UIDeviceOrientation {
return mockOrientation
}

cameraPlugin.orientationChanged(createMockNotification(for: .landscapeLeft))
waitForExpectations(timeout: 30, handler: nil)
}

func testOrientationChangedNoRetainCycle() {
let (_, mockCamera, mockEventAPI, mockDevice, mockDeviceDiscoverer, _) = createCameraPlugin()
let captureSessionQueue = DispatchQueue(label: "capture_session_queue")
weak var weakPlugin: CameraPlugin?
weak var weakDevice = mockDevice
final class CameraOrientationTests: XCTestCase {
private func createCameraPlugin() -> (
cameraPlugin: CameraPlugin,
mockCamera: MockCamera,
mockEventAPI: MockGlobalEventApi,
mockDevice: MockCaptureDevice,
mockDeviceDiscoverer: MockCameraDeviceDiscoverer,
captureSessionQueue: DispatchQueue
) {
let mockDevice = MockCaptureDevice()
let mockCamera = MockCamera()
let mockEventAPI = MockGlobalEventApi()
let mockDeviceDiscoverer = MockCameraDeviceDiscoverer()
let captureSessionQueue = DispatchQueue(label: "io.flutter.camera.captureSessionQueue")

autoreleasepool {
let cameraPlugin = CameraPlugin(
registry: MockFlutterTextureRegistry(),
messenger: MockFlutterBinaryMessenger(),
globalAPI: mockEventAPI,
deviceDiscoverer: mockDeviceDiscoverer,
permissionManager: MockCameraPermissionManager(),
deviceFactory: { _ in weakDevice! },
deviceFactory: { _ in mockDevice },
captureSessionFactory: { MockCaptureSession() },
captureDeviceInputFactory: MockCaptureDeviceInputFactory(),
captureSessionQueue: captureSessionQueue
)
weakPlugin = cameraPlugin
cameraPlugin.camera = mockCamera

cameraPlugin.orientationChanged(createMockNotification(for: .landscapeLeft))
return (
cameraPlugin,
mockCamera,
mockEventAPI,
mockDevice,
mockDeviceDiscoverer,
captureSessionQueue
)
}

// Sanity check.
let cameraDeallocatedExpectation = self.expectation(
description: "Camera must have been deallocated.")
captureSessionQueue.async {
XCTAssertNil(weakPlugin)
cameraDeallocatedExpectation.fulfill()
private func sendOrientation(
_ orientation: UIDeviceOrientation,
to cameraPlugin: CameraPlugin,
captureSessionQueue: DispatchQueue
) {
cameraPlugin.orientationChanged(createMockNotification(for: orientation))
waitForQueueRoundTrip(with: captureSessionQueue)
}
// Awaiting expectation is needed. The test is flaky when checking for nil right away.
waitForExpectations(timeout: 1, handler: nil)

var setDeviceOrientationCalled = false
mockCamera.setDeviceOrientationStub = { orientation in
if orientation == .landscapeLeft {
setDeviceOrientationCalled = true
}
private func createMockNotification(for deviceOrientation: UIDeviceOrientation) -> Notification
{
let mockDevice = MockUIDevice()
mockDevice.mockOrientation = deviceOrientation
return Notification(name: Notification.Name("orientation_test"), object: mockDevice)
}

weak var weakEventAPI = mockEventAPI
// Must check in captureSessionQueue since orientationChanged dispatches to this queue.
let expectation = self.expectation(description: "Dispatched to capture session queue")
captureSessionQueue.async {
XCTAssertFalse(setDeviceOrientationCalled)
XCTAssertFalse(weakEventAPI?.deviceOrientationChangedCalled ?? false)
expectation.fulfill()
func testOrientationNotifications() {
let (cameraPlugin, _, mockEventAPI, _, _, captureSessionQueue) = createCameraPlugin()

sendOrientation(
.portraitUpsideDown, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertEqual(mockEventAPI.lastOrientation, .portraitDown)
sendOrientation(.portrait, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertEqual(mockEventAPI.lastOrientation, .portraitUp)
sendOrientation(.landscapeLeft, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertEqual(mockEventAPI.lastOrientation, .landscapeLeft)
sendOrientation(.landscapeRight, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertEqual(mockEventAPI.lastOrientation, .landscapeRight)
}

waitForExpectations(timeout: 30, handler: nil)
func testOrientationNotificationsNotCalledForFaceUp() {
let (cameraPlugin, _, mockEventAPI, _, _, captureSessionQueue) = createCameraPlugin()
sendOrientation(.faceUp, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertFalse(mockEventAPI.deviceOrientationChangedCalled)
}

func testOrientationNotificationsNotCalledForFaceDown() {
let (cameraPlugin, _, mockEventAPI, _, _, captureSessionQueue) = createCameraPlugin()
sendOrientation(.faceDown, to: cameraPlugin, captureSessionQueue: captureSessionQueue)
XCTAssertFalse(mockEventAPI.deviceOrientationChangedCalled)
}

func testOrientationUpdateMustBeOnCaptureSessionQueue() {
let queueExpectation = expectation(
description: "Orientation update must happen on the capture session queue")
let (cameraPlugin, mockCamera, _, _, _, captureSessionQueue) = createCameraPlugin()
let captureSessionQueueSpecific = DispatchSpecificKey<Void>()
captureSessionQueue.setSpecific(
key: captureSessionQueueSpecific,
value: ())

mockCamera.setDeviceOrientationStub = { orientation in
if DispatchQueue.getSpecific(key: captureSessionQueueSpecific) != nil {
queueExpectation.fulfill()
}
}

cameraPlugin.orientationChanged(createMockNotification(for: .landscapeLeft))
waitForExpectations(timeout: 30, handler: nil)
}

func testOrientationChangedNoRetainCycle() {
let (_, mockCamera, mockEventAPI, mockDevice, mockDeviceDiscoverer, _) = createCameraPlugin()
let captureSessionQueue = DispatchQueue(label: "capture_session_queue")
weak var weakPlugin: CameraPlugin?
weak var weakDevice = mockDevice

autoreleasepool {
let cameraPlugin = CameraPlugin(
registry: MockFlutterTextureRegistry(),
messenger: MockFlutterBinaryMessenger(),
globalAPI: mockEventAPI,
deviceDiscoverer: mockDeviceDiscoverer,
permissionManager: MockCameraPermissionManager(),
deviceFactory: { _ in weakDevice! },
captureSessionFactory: { MockCaptureSession() },
captureDeviceInputFactory: MockCaptureDeviceInputFactory(),
captureSessionQueue: captureSessionQueue
)
weakPlugin = cameraPlugin
cameraPlugin.camera = mockCamera

cameraPlugin.orientationChanged(createMockNotification(for: .landscapeLeft))
}

// Sanity check.
let cameraDeallocatedExpectation = self.expectation(
description: "Camera must have been deallocated.")
captureSessionQueue.async {
XCTAssertNil(weakPlugin)
cameraDeallocatedExpectation.fulfill()
}
// Awaiting expectation is needed. The test is flaky when checking for nil right away.
waitForExpectations(timeout: 1, handler: nil)

var setDeviceOrientationCalled = false
mockCamera.setDeviceOrientationStub = { orientation in
if orientation == .landscapeLeft {
setDeviceOrientationCalled = true
}
}

weak var weakEventAPI = mockEventAPI
// Must check in captureSessionQueue since orientationChanged dispatches to this queue.
let expectation = self.expectation(description: "Dispatched to capture session queue")
captureSessionQueue.async {
XCTAssertFalse(setDeviceOrientationCalled)
XCTAssertFalse(weakEventAPI?.deviceOrientationChangedCalled ?? false)
expectation.fulfill()
}

waitForExpectations(timeout: 30, handler: nil)
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -36,38 +36,40 @@ final class CameraPropertiesTests: XCTestCase {

// MARK: - Device Orientation Tests

func testGetUIDeviceOrientationForPigeonDeviceOrientation() {
XCTAssertEqual(
UIDeviceOrientation.portraitUpsideDown,
getUIDeviceOrientation(for: .portraitDown)
)
XCTAssertEqual(
UIDeviceOrientation.landscapeLeft,
getUIDeviceOrientation(for: .landscapeLeft))
XCTAssertEqual(
UIDeviceOrientation.landscapeRight,
getUIDeviceOrientation(for: .landscapeRight))
XCTAssertEqual(
UIDeviceOrientation.portrait,
getUIDeviceOrientation(for: .portraitUp))
}
#if os(iOS)
func testGetUIDeviceOrientationForPigeonDeviceOrientation() {
XCTAssertEqual(
UIDeviceOrientation.portraitUpsideDown,
getUIDeviceOrientation(for: .portraitDown)
)
XCTAssertEqual(
UIDeviceOrientation.landscapeLeft,
getUIDeviceOrientation(for: .landscapeLeft))
XCTAssertEqual(
UIDeviceOrientation.landscapeRight,
getUIDeviceOrientation(for: .landscapeRight))
XCTAssertEqual(
UIDeviceOrientation.portrait,
getUIDeviceOrientation(for: .portraitUp))
}

func testGetPigeonDeviceOrientationForUIDeviceOrientation() {
XCTAssertEqual(
PlatformDeviceOrientation.portraitDown,
getPigeonDeviceOrientation(for: .portraitUpsideDown))
XCTAssertEqual(
PlatformDeviceOrientation.landscapeLeft,
getPigeonDeviceOrientation(for: .landscapeLeft))
XCTAssertEqual(
PlatformDeviceOrientation.landscapeRight,
getPigeonDeviceOrientation(for: .landscapeRight))
XCTAssertEqual(
PlatformDeviceOrientation.portraitUp,
getPigeonDeviceOrientation(for: .portrait))
// Test default case.
XCTAssertEqual(
PlatformDeviceOrientation.portraitUp,
getPigeonDeviceOrientation(for: .unknown))
}
func testGetPigeonDeviceOrientationForUIDeviceOrientation() {
XCTAssertEqual(
PlatformDeviceOrientation.portraitDown,
getPigeonDeviceOrientation(for: .portraitUpsideDown))
XCTAssertEqual(
PlatformDeviceOrientation.landscapeLeft,
getPigeonDeviceOrientation(for: .landscapeLeft))
XCTAssertEqual(
PlatformDeviceOrientation.landscapeRight,
getPigeonDeviceOrientation(for: .landscapeRight))
XCTAssertEqual(
PlatformDeviceOrientation.portraitUp,
getPigeonDeviceOrientation(for: .portrait))
// Test default case.
XCTAssertEqual(
PlatformDeviceOrientation.portraitUp,
getPigeonDeviceOrientation(for: .unknown))
}
#endif
}
Loading