Add tests for starting and stopping RTCCameraVideoCapturer.
Bug: webrtc:8755
Change-Id: I07d9a203276359069af7ba384c58612df7f2b467
Reviewed-on: https://webrtc-review.googlesource.com/40240
Commit-Queue: Anders Carlsson <andersc@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Reviewed-by: Patrik Höglund <phoglund@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21692}
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index b587a55..e370a05 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -625,10 +625,7 @@
"objc/Framework/UnitTests/objc_video_encoder_factory_tests.mm",
"objc/Framework/UnitTests/scoped_cftyperef_tests.mm",
]
- if (is_ios &&
- !(use_ios_simulator &&
- # The tests crash on these simulator versions:
- (ios_sdk_version == "10.0" || ios_sdk_version == "10.1"))) {
+ if (is_ios) {
sources +=
[ "objc/Framework/UnitTests/RTCCameraVideoCapturerTests.mm" ]
}
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCCameraVideoCapturer.m b/sdk/objc/Framework/Classes/PeerConnection/RTCCameraVideoCapturer.m
index 89060ef..a7d1ec1 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCCameraVideoCapturer.m
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCCameraVideoCapturer.m
@@ -47,12 +47,18 @@
@synthesize captureSession = _captureSession;
- (instancetype)initWithDelegate:(__weak id<RTCVideoCapturerDelegate>)delegate {
+ return [self initWithDelegate:delegate captureSession:[[AVCaptureSession alloc] init]];
+}
+
+// This initializer is used for testing.
+- (instancetype)initWithDelegate:(__weak id<RTCVideoCapturerDelegate>)delegate
+ captureSession:(AVCaptureSession *)captureSession {
if (self = [super initWithDelegate:delegate]) {
// Create the capture session and all relevant inputs and outputs. We need
// to do this in init because the application may want the capture session
// before we start the capturer for e.g. AVCapturePreviewLayer. All objects
// created here are retained until dealloc and never recreated.
- if (![self setupCaptureSession]) {
+ if (![self setupCaptureSession:captureSession]) {
return nil;
}
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
@@ -146,6 +152,7 @@
if (completionHandler) {
completionHandler(error);
}
+ _willBeRunning = NO;
return;
}
[self reconfigureCaptureSessionInput];
@@ -379,9 +386,9 @@
return _frameQueue;
}
-- (BOOL)setupCaptureSession {
+- (BOOL)setupCaptureSession:(AVCaptureSession *)captureSession {
NSAssert(_captureSession == nil, @"Setup capture session called twice.");
- _captureSession = [[AVCaptureSession alloc] init];
+ _captureSession = captureSession;
#if defined(WEBRTC_IOS)
_captureSession.sessionPreset = AVCaptureSessionPresetInputPriority;
_captureSession.usesApplicationAudioSession = NO;
diff --git a/sdk/objc/Framework/UnitTests/RTCCameraVideoCapturerTests.mm b/sdk/objc/Framework/UnitTests/RTCCameraVideoCapturerTests.mm
index f543069..905b974 100644
--- a/sdk/objc/Framework/UnitTests/RTCCameraVideoCapturerTests.mm
+++ b/sdk/objc/Framework/UnitTests/RTCCameraVideoCapturerTests.mm
@@ -60,20 +60,24 @@
}
#endif
@interface RTCCameraVideoCapturer (Tests)<AVCaptureVideoDataOutputSampleBufferDelegate>
+- (instancetype)initWithDelegate:(__weak id<RTCVideoCapturerDelegate>)delegate
+ captureSession:(AVCaptureSession *)captureSession;
@end
@interface RTCCameraVideoCapturerTests : NSObject
@property(nonatomic, strong) id delegateMock;
@property(nonatomic, strong) id deviceMock;
@property(nonatomic, strong) id captureConnectionMock;
+@property(nonatomic, strong) id captureSessionMock;
@property(nonatomic, strong) RTCCameraVideoCapturer *capturer;
@end
@implementation RTCCameraVideoCapturerTests
@synthesize delegateMock = _delegateMock;
-@synthesize captureConnectionMock = _captureConnectionMock;
-@synthesize capturer = _capturer;
@synthesize deviceMock = _deviceMock;
+@synthesize captureConnectionMock = _captureConnectionMock;
+@synthesize captureSessionMock = _captureSessionMock;
+@synthesize capturer = _capturer;
- (void)setup {
self.delegateMock = OCMProtocolMock(@protocol(RTCVideoCapturerDelegate));
@@ -82,6 +86,21 @@
self.deviceMock = [self createDeviceMock];
}
+- (void)setupWithMockedCaptureSession {
+ self.captureSessionMock = OCMStrictClassMock([AVCaptureSession class]);
+ OCMStub([self.captureSessionMock setSessionPreset:[OCMArg any]]);
+ OCMStub([self.captureSessionMock setUsesApplicationAudioSession:NO]);
+ OCMStub([self.captureSessionMock canAddOutput:[OCMArg any]]).andReturn(YES);
+ OCMStub([self.captureSessionMock addOutput:[OCMArg any]]);
+ OCMStub([self.captureSessionMock beginConfiguration]);
+ OCMStub([self.captureSessionMock commitConfiguration]);
+ self.delegateMock = OCMProtocolMock(@protocol(RTCVideoCapturerDelegate));
+ self.captureConnectionMock = OCMClassMock([AVCaptureConnection class]);
+ self.capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:self.delegateMock
+ captureSession:self.captureSessionMock];
+ self.deviceMock = [self createDeviceMock];
+}
+
- (void)tearDown {
[self.delegateMock stopMocking];
[self.deviceMock stopMocking];
@@ -144,9 +163,11 @@
NSArray *supportedFormats = [RTCCameraVideoCapturer supportedFormatsForDevice:self.deviceMock];
// then
- EXPECT_EQ(supportedFormats.count, 2u);
+ EXPECT_EQ(supportedFormats.count, 3u);
EXPECT_TRUE([supportedFormats containsObject:validFormat1]);
EXPECT_TRUE([supportedFormats containsObject:validFormat2]);
+ EXPECT_TRUE([supportedFormats containsObject:invalidFormat]);
+
// cleanup
[validFormat1 stopMocking];
[validFormat2 stopMocking];
@@ -349,59 +370,162 @@
#endif
}
+- (void)testStartingAndStoppingCapture {
+ id expectedDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]);
+ id captureDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]);
+ OCMStub([captureDeviceInputMock deviceInputWithDevice:self.deviceMock error:[OCMArg setTo:nil]])
+ .andReturn(expectedDeviceInputMock);
+
+ OCMStub([self.deviceMock lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES);
+ OCMStub([self.deviceMock unlockForConfiguration]);
+ OCMStub([_captureSessionMock canAddInput:expectedDeviceInputMock]).andReturn(YES);
+ OCMStub([_captureSessionMock inputs]).andReturn(@[ expectedDeviceInputMock ]);
+ OCMStub([_captureSessionMock removeInput:expectedDeviceInputMock]);
+
+ // Set expectation that the capture session should be started with correct device.
+ OCMExpect([_captureSessionMock addInput:expectedDeviceInputMock]);
+ OCMExpect([_captureSessionMock startRunning]);
+ OCMExpect([_captureSessionMock stopRunning]);
+
+ id format = OCMClassMock([AVCaptureDeviceFormat class]);
+ [self.capturer startCaptureWithDevice:self.deviceMock format:format fps:30];
+ [self.capturer stopCapture];
+
+ // Start capture code is dispatched async.
+ OCMVerifyAllWithDelay(_captureSessionMock, 15);
+}
+
+- (void)testStartCaptureFailingToLockForConfiguration {
+ // The captureSessionMock is a strict mock, so this test will crash if the startCapture
+ // method does not return when failing to lock for configuration.
+ OCMExpect([self.deviceMock lockForConfiguration:[OCMArg setTo:nil]]).andReturn(NO);
+
+ id format = OCMClassMock([AVCaptureDeviceFormat class]);
+ [self.capturer startCaptureWithDevice:self.deviceMock format:format fps:30];
+
+ // Start capture code is dispatched async.
+ OCMVerifyAllWithDelay(self.deviceMock, 15);
+}
+
+- (void)testStartingAndStoppingCaptureWithCallbacks {
+ id expectedDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]);
+ id captureDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]);
+ OCMStub([captureDeviceInputMock deviceInputWithDevice:self.deviceMock error:[OCMArg setTo:nil]])
+ .andReturn(expectedDeviceInputMock);
+
+ OCMStub([self.deviceMock lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES);
+ OCMStub([self.deviceMock unlockForConfiguration]);
+ OCMStub([_captureSessionMock canAddInput:expectedDeviceInputMock]).andReturn(YES);
+ OCMStub([_captureSessionMock inputs]).andReturn(@[ expectedDeviceInputMock ]);
+ OCMStub([_captureSessionMock removeInput:expectedDeviceInputMock]);
+
+ // Set expectation that the capture session should be started with correct device.
+ OCMExpect([_captureSessionMock addInput:expectedDeviceInputMock]);
+ OCMExpect([_captureSessionMock startRunning]);
+ OCMExpect([_captureSessionMock stopRunning]);
+
+ dispatch_semaphore_t completedStopSemaphore = dispatch_semaphore_create(0);
+
+ __block BOOL completedStart = NO;
+ id format = OCMClassMock([AVCaptureDeviceFormat class]);
+ [self.capturer startCaptureWithDevice:self.deviceMock
+ format:format
+ fps:30
+ completionHandler:^(NSError *error) {
+ EXPECT_EQ(error, nil);
+ completedStart = YES;
+ }];
+
+ __block BOOL completedStop = NO;
+ [self.capturer stopCaptureWithCompletionHandler:^{
+ completedStop = YES;
+ dispatch_semaphore_signal(completedStopSemaphore);
+ }];
+
+ dispatch_semaphore_wait(completedStopSemaphore,
+ dispatch_time(DISPATCH_TIME_NOW, 15.0 * NSEC_PER_SEC));
+ OCMVerifyAllWithDelay(_captureSessionMock, 15);
+ EXPECT_TRUE(completedStart);
+ EXPECT_TRUE(completedStop);
+}
+
+- (void)testStartCaptureFailingToLockForConfigurationWithCallback {
+ id expectedDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]);
+ id captureDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]);
+ OCMStub([captureDeviceInputMock deviceInputWithDevice:self.deviceMock error:[OCMArg setTo:nil]])
+ .andReturn(expectedDeviceInputMock);
+
+ id errorMock = OCMClassMock([NSError class]);
+
+ OCMStub([self.deviceMock lockForConfiguration:[OCMArg setTo:errorMock]]).andReturn(NO);
+ OCMStub([_captureSessionMock canAddInput:expectedDeviceInputMock]).andReturn(YES);
+ OCMStub([self.deviceMock unlockForConfiguration]);
+
+ OCMExpect([_captureSessionMock addInput:expectedDeviceInputMock]);
+
+ dispatch_semaphore_t completedStartSemaphore = dispatch_semaphore_create(0);
+ __block NSError *callbackError = nil;
+
+ id format = OCMClassMock([AVCaptureDeviceFormat class]);
+ [self.capturer startCaptureWithDevice:self.deviceMock
+ format:format
+ fps:30
+ completionHandler:^(NSError *error) {
+ callbackError = error;
+ dispatch_semaphore_signal(completedStartSemaphore);
+ }];
+
+ long ret = dispatch_semaphore_wait(completedStartSemaphore,
+ dispatch_time(DISPATCH_TIME_NOW, 15.0 * NSEC_PER_SEC));
+ EXPECT_EQ(ret, 0);
+ EXPECT_EQ(callbackError, errorMock);
+}
+
@end
-// TODO(kthelgason): Reenable these tests on simulator.
-// See bugs.webrtc.org/7813
-#if TARGET_IPHONE_SIMULATOR
-#define MAYBE_TEST(f, name) TEST(f, DISABLED_##name)
-#else
-#define MAYBE_TEST TEST
-#endif
-
-MAYBE_TEST(RTCCameraVideoCapturerTests, SetupSession) {
+TEST(RTCCameraVideoCapturerTests, SetupSession) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testSetupSession];
[test tearDown];
}
-MAYBE_TEST(RTCCameraVideoCapturerTests, SetupSessionOutput) {
+TEST(RTCCameraVideoCapturerTests, SetupSessionOutput) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testSetupSessionOutput];
[test tearDown];
}
-MAYBE_TEST(RTCCameraVideoCapturerTests, SupportedFormatsForDevice) {
+TEST(RTCCameraVideoCapturerTests, SupportedFormatsForDevice) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testSupportedFormatsForDevice];
[test tearDown];
}
-MAYBE_TEST(RTCCameraVideoCapturerTests, CaptureDevices) {
+TEST(RTCCameraVideoCapturerTests, CaptureDevices) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testCaptureDevices];
[test tearDown];
}
-MAYBE_TEST(RTCCameraVideoCapturerTests, DelegateCallbackNotCalledWhenInvalidBuffer) {
+TEST(RTCCameraVideoCapturerTests, DelegateCallbackNotCalledWhenInvalidBuffer) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testDelegateCallbackNotCalledWhenInvalidBuffer];
[test tearDown];
}
-MAYBE_TEST(RTCCameraVideoCapturerTests, DelegateCallbackWithValidBufferAndOrientationUpdate) {
+TEST(RTCCameraVideoCapturerTests, DelegateCallbackWithValidBufferAndOrientationUpdate) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testDelegateCallbackWithValidBufferAndOrientationUpdate];
[test tearDown];
}
-MAYBE_TEST(RTCCameraVideoCapturerTests, RotationCameraBackLandscapeLeft) {
+TEST(RTCCameraVideoCapturerTests, RotationCameraBackLandscapeLeft) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testRotationCamera:AVCaptureDevicePositionBack
@@ -409,7 +533,7 @@
[test tearDown];
}
-MAYBE_TEST(RTCCameraVideoCapturerTests, RotationCameraFrontLandscapeLeft) {
+TEST(RTCCameraVideoCapturerTests, RotationCameraFrontLandscapeLeft) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testRotationCamera:AVCaptureDevicePositionFront
@@ -417,7 +541,7 @@
[test tearDown];
}
-MAYBE_TEST(RTCCameraVideoCapturerTests, RotationCameraBackLandscapeRight) {
+TEST(RTCCameraVideoCapturerTests, RotationCameraBackLandscapeRight) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testRotationCamera:AVCaptureDevicePositionBack
@@ -425,7 +549,7 @@
[test tearDown];
}
-MAYBE_TEST(RTCCameraVideoCapturerTests, RotationCameraFrontLandscapeRight) {
+TEST(RTCCameraVideoCapturerTests, RotationCameraFrontLandscapeRight) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testRotationCamera:AVCaptureDevicePositionFront
@@ -433,16 +557,44 @@
[test tearDown];
}
-MAYBE_TEST(RTCCameraVideoCapturerTests, RotationCameraFrame) {
+TEST(RTCCameraVideoCapturerTests, RotationCameraFrame) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testRotationFrame];
[test tearDown];
}
-MAYBE_TEST(RTCCameraVideoCapturerTests, ImageExif) {
+TEST(RTCCameraVideoCapturerTests, ImageExif) {
RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
[test setup];
[test testImageExif];
[test tearDown];
}
+
+TEST(RTCCameraVideoCapturerTests, StartAndStopCapture) {
+ RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
+ [test setupWithMockedCaptureSession];
+ [test testStartingAndStoppingCapture];
+ [test tearDown];
+}
+
+TEST(RTCCameraVideoCapturerTests, StartCaptureFailingToLockForConfiguration) {
+ RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
+ [test setupWithMockedCaptureSession];
+ [test testStartCaptureFailingToLockForConfiguration];
+ [test tearDown];
+}
+
+TEST(RTCCameraVideoCapturerTests, StartAndStopCaptureWithCallbacks) {
+ RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
+ [test setupWithMockedCaptureSession];
+ [test testStartingAndStoppingCaptureWithCallbacks];
+ [test tearDown];
+}
+
+TEST(RTCCameraVideoCapturerTests, StartCaptureFailingToLockForConfigurationWithCallback) {
+ RTCCameraVideoCapturerTests *test = [[RTCCameraVideoCapturerTests alloc] init];
+ [test setupWithMockedCaptureSession];
+ [test testStartCaptureFailingToLockForConfigurationWithCallback];
+ [test tearDown];
+}