1*d9f75844SAndroid Build Coastguard Worker/* 2*d9f75844SAndroid Build Coastguard Worker * Copyright 2016 The WebRTC project authors. All Rights Reserved. 3*d9f75844SAndroid Build Coastguard Worker * 4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license 5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source 6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found 7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may 8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree. 9*d9f75844SAndroid Build Coastguard Worker */ 10*d9f75844SAndroid Build Coastguard Worker 11*d9f75844SAndroid Build Coastguard Worker#import <Foundation/Foundation.h> 12*d9f75844SAndroid Build Coastguard Worker#import <OCMock/OCMock.h> 13*d9f75844SAndroid Build Coastguard Worker 14*d9f75844SAndroid Build Coastguard Worker#include "rtc_base/gunit.h" 15*d9f75844SAndroid Build Coastguard Worker 16*d9f75844SAndroid Build Coastguard Worker// Width and height don't play any role so lets use predefined values throughout 17*d9f75844SAndroid Build Coastguard Worker// the tests. 18*d9f75844SAndroid Build Coastguard Workerstatic const int kFormatWidth = 789; 19*d9f75844SAndroid Build Coastguard Workerstatic const int kFormatHeight = 987; 20*d9f75844SAndroid Build Coastguard Worker 21*d9f75844SAndroid Build Coastguard Worker// Hardcoded framrate to be used throughout the tests. 22*d9f75844SAndroid Build Coastguard Workerstatic const int kFramerate = 30; 23*d9f75844SAndroid Build Coastguard Worker 24*d9f75844SAndroid Build Coastguard Worker// Same width and height is used so it's ok to expect same cricket::VideoFormat 25*d9f75844SAndroid Build Coastguard Workerstatic cricket::VideoFormat expectedFormat = 26*d9f75844SAndroid Build Coastguard Worker cricket::VideoFormat(kFormatWidth, 27*d9f75844SAndroid Build Coastguard Worker kFormatHeight, 28*d9f75844SAndroid Build Coastguard Worker cricket::VideoFormat::FpsToInterval(kFramerate), 29*d9f75844SAndroid Build Coastguard Worker cricket::FOURCC_NV12); 30*d9f75844SAndroid Build Coastguard Worker 31*d9f75844SAndroid Build Coastguard Worker// Mock class for AVCaptureDeviceFormat. 32*d9f75844SAndroid Build Coastguard Worker// Custom implementation needed because OCMock cannot handle the 33*d9f75844SAndroid Build Coastguard Worker// CMVideoDescriptionRef mocking. 34*d9f75844SAndroid Build Coastguard Worker@interface AVCaptureDeviceFormatMock : NSObject 35*d9f75844SAndroid Build Coastguard Worker 36*d9f75844SAndroid Build Coastguard Worker@property (nonatomic, assign) CMVideoFormatDescriptionRef format; 37*d9f75844SAndroid Build Coastguard Worker@property (nonatomic, strong) OCMockObject *rangeMock; 38*d9f75844SAndroid Build Coastguard Worker 39*d9f75844SAndroid Build Coastguard Worker- (instancetype)initWithMediaSubtype:(FourCharCode)subtype 40*d9f75844SAndroid Build Coastguard Worker minFps:(float)minFps 41*d9f75844SAndroid Build Coastguard Worker maxFps:(float)maxFps; 42*d9f75844SAndroid Build Coastguard Worker+ (instancetype)validFormat; 43*d9f75844SAndroid Build Coastguard Worker+ (instancetype)invalidFpsFormat; 44*d9f75844SAndroid Build Coastguard Worker+ (instancetype)invalidMediaSubtypeFormat; 45*d9f75844SAndroid Build Coastguard Worker 46*d9f75844SAndroid Build Coastguard Worker@end 47*d9f75844SAndroid Build Coastguard Worker 48*d9f75844SAndroid Build Coastguard Worker@implementation AVCaptureDeviceFormatMock 49*d9f75844SAndroid Build Coastguard Worker 50*d9f75844SAndroid Build Coastguard Worker@synthesize format = _format; 51*d9f75844SAndroid Build Coastguard Worker@synthesize rangeMock = _rangeMock; 52*d9f75844SAndroid Build Coastguard Worker 53*d9f75844SAndroid Build Coastguard Worker- (instancetype)initWithMediaSubtype:(FourCharCode)subtype 54*d9f75844SAndroid Build Coastguard Worker minFps:(float)minFps 55*d9f75844SAndroid Build Coastguard Worker maxFps:(float)maxFps { 56*d9f75844SAndroid Build Coastguard Worker if (self = [super init]) { 57*d9f75844SAndroid Build Coastguard Worker CMVideoFormatDescriptionCreate(nil, subtype, kFormatWidth, kFormatHeight, 58*d9f75844SAndroid Build Coastguard Worker nil, &_format); 59*d9f75844SAndroid Build Coastguard Worker // We can use OCMock for the range. 60*d9f75844SAndroid Build Coastguard Worker _rangeMock = [OCMockObject mockForClass:[AVFrameRateRange class]]; 61*d9f75844SAndroid Build Coastguard Worker [[[_rangeMock stub] andReturnValue:@(minFps)] minFrameRate]; 62*d9f75844SAndroid Build Coastguard Worker [[[_rangeMock stub] andReturnValue:@(maxFps)] maxFrameRate]; 63*d9f75844SAndroid Build Coastguard Worker } 64*d9f75844SAndroid Build Coastguard Worker 65*d9f75844SAndroid Build Coastguard Worker return self; 66*d9f75844SAndroid Build Coastguard Worker} 67*d9f75844SAndroid Build Coastguard Worker 68*d9f75844SAndroid Build Coastguard Worker+ (instancetype)validFormat { 69*d9f75844SAndroid Build Coastguard Worker AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc] 70*d9f75844SAndroid Build Coastguard Worker initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 71*d9f75844SAndroid Build Coastguard Worker minFps:0.0 72*d9f75844SAndroid Build Coastguard Worker maxFps:30.0]; 73*d9f75844SAndroid Build Coastguard Worker return instance; 74*d9f75844SAndroid Build Coastguard Worker} 75*d9f75844SAndroid Build Coastguard Worker 76*d9f75844SAndroid Build Coastguard Worker+ (instancetype)invalidFpsFormat { 77*d9f75844SAndroid Build Coastguard Worker AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc] 78*d9f75844SAndroid Build Coastguard Worker initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 79*d9f75844SAndroid Build Coastguard Worker minFps:0.0 80*d9f75844SAndroid Build Coastguard Worker maxFps:22.0]; 81*d9f75844SAndroid Build Coastguard Worker return instance; 82*d9f75844SAndroid Build Coastguard Worker} 83*d9f75844SAndroid Build Coastguard Worker 84*d9f75844SAndroid Build Coastguard Worker+ (instancetype)invalidMediaSubtypeFormat { 85*d9f75844SAndroid Build Coastguard Worker AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc] 86*d9f75844SAndroid Build Coastguard Worker initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8Planar 87*d9f75844SAndroid Build Coastguard Worker minFps:0.0 88*d9f75844SAndroid Build Coastguard Worker maxFps:60.0]; 89*d9f75844SAndroid Build Coastguard Worker return instance; 90*d9f75844SAndroid Build Coastguard Worker} 91*d9f75844SAndroid Build Coastguard Worker 92*d9f75844SAndroid Build Coastguard Worker- (void)dealloc { 93*d9f75844SAndroid Build Coastguard Worker if (_format != nil) { 94*d9f75844SAndroid Build Coastguard Worker CFRelease(_format); 95*d9f75844SAndroid Build Coastguard Worker _format = nil; 96*d9f75844SAndroid Build Coastguard Worker } 97*d9f75844SAndroid Build Coastguard Worker} 98*d9f75844SAndroid Build Coastguard Worker 99*d9f75844SAndroid Build Coastguard Worker// Redefinition of AVCaptureDevice methods we want to mock. 100*d9f75844SAndroid Build Coastguard Worker- (CMVideoFormatDescriptionRef)formatDescription { 101*d9f75844SAndroid Build Coastguard Worker return self.format; 102*d9f75844SAndroid Build Coastguard Worker} 103*d9f75844SAndroid Build Coastguard Worker 104*d9f75844SAndroid Build Coastguard Worker- (NSArray *)videoSupportedFrameRateRanges { 105*d9f75844SAndroid Build Coastguard Worker return @[ self.rangeMock ]; 106*d9f75844SAndroid Build Coastguard Worker} 107*d9f75844SAndroid Build Coastguard Worker 108*d9f75844SAndroid Build Coastguard Worker@end 109*d9f75844SAndroid Build Coastguard Worker 110*d9f75844SAndroid Build Coastguard WorkerTEST(AVFormatMapperTest, SuportedCricketFormatsWithInvalidFramerateFormats) { 111*d9f75844SAndroid Build Coastguard Worker // given 112*d9f75844SAndroid Build Coastguard Worker id mockDevice = OCMClassMock([AVCaptureDevice class]); 113*d9f75844SAndroid Build Coastguard Worker 114*d9f75844SAndroid Build Coastguard Worker // Valid media subtype, invalid framerate 115*d9f75844SAndroid Build Coastguard Worker AVCaptureDeviceFormatMock* mock = 116*d9f75844SAndroid Build Coastguard Worker [AVCaptureDeviceFormatMock invalidFpsFormat]; 117*d9f75844SAndroid Build Coastguard Worker OCMStub([mockDevice formats]).andReturn(@[ mock ]); 118*d9f75844SAndroid Build Coastguard Worker 119*d9f75844SAndroid Build Coastguard Worker // when 120*d9f75844SAndroid Build Coastguard Worker std::set<cricket::VideoFormat> result = 121*d9f75844SAndroid Build Coastguard Worker webrtc::GetSupportedVideoFormatsForDevice(mockDevice); 122*d9f75844SAndroid Build Coastguard Worker 123*d9f75844SAndroid Build Coastguard Worker // then 124*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE(result.empty()); 125*d9f75844SAndroid Build Coastguard Worker} 126*d9f75844SAndroid Build Coastguard Worker 127*d9f75844SAndroid Build Coastguard WorkerTEST(AVFormatMapperTest, SuportedCricketFormatsWithInvalidFormats) { 128*d9f75844SAndroid Build Coastguard Worker // given 129*d9f75844SAndroid Build Coastguard Worker id mockDevice = OCMClassMock([AVCaptureDevice class]); 130*d9f75844SAndroid Build Coastguard Worker 131*d9f75844SAndroid Build Coastguard Worker // Invalid media subtype, valid framerate 132*d9f75844SAndroid Build Coastguard Worker AVCaptureDeviceFormatMock* mock = 133*d9f75844SAndroid Build Coastguard Worker [AVCaptureDeviceFormatMock invalidMediaSubtypeFormat]; 134*d9f75844SAndroid Build Coastguard Worker OCMStub([mockDevice formats]).andReturn(@[ mock ]); 135*d9f75844SAndroid Build Coastguard Worker 136*d9f75844SAndroid Build Coastguard Worker // when 137*d9f75844SAndroid Build Coastguard Worker std::set<cricket::VideoFormat> result = 138*d9f75844SAndroid Build Coastguard Worker webrtc::GetSupportedVideoFormatsForDevice(mockDevice); 139*d9f75844SAndroid Build Coastguard Worker 140*d9f75844SAndroid Build Coastguard Worker // then 141*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE(result.empty()); 142*d9f75844SAndroid Build Coastguard Worker} 143*d9f75844SAndroid Build Coastguard Worker 144*d9f75844SAndroid Build Coastguard WorkerTEST(AVFormatMapperTest, SuportedCricketFormats) { 145*d9f75844SAndroid Build Coastguard Worker // given 146*d9f75844SAndroid Build Coastguard Worker id mockDevice = OCMClassMock([AVCaptureDevice class]); 147*d9f75844SAndroid Build Coastguard Worker 148*d9f75844SAndroid Build Coastguard Worker // valid media subtype, valid framerate 149*d9f75844SAndroid Build Coastguard Worker AVCaptureDeviceFormatMock* mock = [AVCaptureDeviceFormatMock validFormat]; 150*d9f75844SAndroid Build Coastguard Worker OCMStub([mockDevice formats]).andReturn(@[ mock ]); 151*d9f75844SAndroid Build Coastguard Worker 152*d9f75844SAndroid Build Coastguard Worker // when 153*d9f75844SAndroid Build Coastguard Worker std::set<cricket::VideoFormat> result = 154*d9f75844SAndroid Build Coastguard Worker webrtc::GetSupportedVideoFormatsForDevice(mockDevice); 155*d9f75844SAndroid Build Coastguard Worker 156*d9f75844SAndroid Build Coastguard Worker // then 157*d9f75844SAndroid Build Coastguard Worker EXPECT_EQ(1u, result.size()); 158*d9f75844SAndroid Build Coastguard Worker // make sure the set has the expected format 159*d9f75844SAndroid Build Coastguard Worker EXPECT_EQ(expectedFormat, *result.begin()); 160*d9f75844SAndroid Build Coastguard Worker} 161*d9f75844SAndroid Build Coastguard Worker 162*d9f75844SAndroid Build Coastguard WorkerTEST(AVFormatMapperTest, MediaSubtypePreference) { 163*d9f75844SAndroid Build Coastguard Worker // given 164*d9f75844SAndroid Build Coastguard Worker id mockDevice = OCMClassMock([AVCaptureDevice class]); 165*d9f75844SAndroid Build Coastguard Worker 166*d9f75844SAndroid Build Coastguard Worker // valid media subtype, valid framerate 167*d9f75844SAndroid Build Coastguard Worker AVCaptureDeviceFormatMock* mockOne = [[AVCaptureDeviceFormatMock alloc] 168*d9f75844SAndroid Build Coastguard Worker initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange 169*d9f75844SAndroid Build Coastguard Worker minFps:0.0 170*d9f75844SAndroid Build Coastguard Worker maxFps:30.0]; 171*d9f75844SAndroid Build Coastguard Worker // valid media subtype, valid framerate. 172*d9f75844SAndroid Build Coastguard Worker // This media subtype should be the preffered one. 173*d9f75844SAndroid Build Coastguard Worker AVCaptureDeviceFormatMock* mockTwo = [[AVCaptureDeviceFormatMock alloc] 174*d9f75844SAndroid Build Coastguard Worker initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 175*d9f75844SAndroid Build Coastguard Worker minFps:0.0 176*d9f75844SAndroid Build Coastguard Worker maxFps:30.0]; 177*d9f75844SAndroid Build Coastguard Worker OCMStub([mockDevice lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES); 178*d9f75844SAndroid Build Coastguard Worker OCMStub([mockDevice unlockForConfiguration]); 179*d9f75844SAndroid Build Coastguard Worker NSArray* array = @[ mockOne, mockTwo ]; 180*d9f75844SAndroid Build Coastguard Worker OCMStub([mockDevice formats]).andReturn(array); 181*d9f75844SAndroid Build Coastguard Worker 182*d9f75844SAndroid Build Coastguard Worker // to verify 183*d9f75844SAndroid Build Coastguard Worker OCMExpect([mockDevice setActiveFormat:(AVCaptureDeviceFormat*)mockTwo]); 184*d9f75844SAndroid Build Coastguard Worker OCMExpect( 185*d9f75844SAndroid Build Coastguard Worker [mockDevice setActiveVideoMinFrameDuration:CMTimeMake(1, kFramerate)]); 186*d9f75844SAndroid Build Coastguard Worker 187*d9f75844SAndroid Build Coastguard Worker // when 188*d9f75844SAndroid Build Coastguard Worker bool resultFormat = 189*d9f75844SAndroid Build Coastguard Worker webrtc::SetFormatForCaptureDevice(mockDevice, nil, expectedFormat); 190*d9f75844SAndroid Build Coastguard Worker 191*d9f75844SAndroid Build Coastguard Worker // then 192*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE(resultFormat); 193*d9f75844SAndroid Build Coastguard Worker [mockDevice verify]; 194*d9f75844SAndroid Build Coastguard Worker} 195*d9f75844SAndroid Build Coastguard Worker 196*d9f75844SAndroid Build Coastguard WorkerTEST(AVFormatMapperTest, SetFormatWhenDeviceCannotLock) { 197*d9f75844SAndroid Build Coastguard Worker // given 198*d9f75844SAndroid Build Coastguard Worker id mockDevice = OCMClassMock([AVCaptureDevice class]); 199*d9f75844SAndroid Build Coastguard Worker [[[mockDevice stub] andReturnValue:@(NO)] 200*d9f75844SAndroid Build Coastguard Worker lockForConfiguration:[OCMArg setTo:nil]]; 201*d9f75844SAndroid Build Coastguard Worker [[[mockDevice stub] andReturn:@[]] formats]; 202*d9f75844SAndroid Build Coastguard Worker 203*d9f75844SAndroid Build Coastguard Worker // when 204*d9f75844SAndroid Build Coastguard Worker bool resultFormat = webrtc::SetFormatForCaptureDevice(mockDevice, nil, 205*d9f75844SAndroid Build Coastguard Worker cricket::VideoFormat()); 206*d9f75844SAndroid Build Coastguard Worker 207*d9f75844SAndroid Build Coastguard Worker // then 208*d9f75844SAndroid Build Coastguard Worker EXPECT_FALSE(resultFormat); 209*d9f75844SAndroid Build Coastguard Worker} 210*d9f75844SAndroid Build Coastguard Worker 211*d9f75844SAndroid Build Coastguard WorkerTEST(AVFormatMapperTest, SetFormatWhenFormatIsIncompatible) { 212*d9f75844SAndroid Build Coastguard Worker // given 213*d9f75844SAndroid Build Coastguard Worker id mockDevice = OCMClassMock([AVCaptureDevice class]); 214*d9f75844SAndroid Build Coastguard Worker OCMStub([mockDevice formats]).andReturn(@[]); 215*d9f75844SAndroid Build Coastguard Worker OCMStub([mockDevice lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES); 216*d9f75844SAndroid Build Coastguard Worker NSException* testException = 217*d9f75844SAndroid Build Coastguard Worker [NSException exceptionWithName:@"Test exception" 218*d9f75844SAndroid Build Coastguard Worker reason:@"Raised from unit tests" 219*d9f75844SAndroid Build Coastguard Worker userInfo:nil]; 220*d9f75844SAndroid Build Coastguard Worker OCMStub([mockDevice setActiveFormat:[OCMArg any]]).andThrow(testException); 221*d9f75844SAndroid Build Coastguard Worker OCMExpect([mockDevice unlockForConfiguration]); 222*d9f75844SAndroid Build Coastguard Worker 223*d9f75844SAndroid Build Coastguard Worker // when 224*d9f75844SAndroid Build Coastguard Worker bool resultFormat = webrtc::SetFormatForCaptureDevice(mockDevice, nil, 225*d9f75844SAndroid Build Coastguard Worker cricket::VideoFormat()); 226*d9f75844SAndroid Build Coastguard Worker 227*d9f75844SAndroid Build Coastguard Worker // then 228*d9f75844SAndroid Build Coastguard Worker EXPECT_FALSE(resultFormat); 229*d9f75844SAndroid Build Coastguard Worker 230*d9f75844SAndroid Build Coastguard Worker // TODO(denicija): Remove try-catch when Chromium rolls this change: 231*d9f75844SAndroid Build Coastguard Worker // https://github.com/erikdoe/ocmock/commit/de1419415581dc307045e54bfe9c98c86efea96b 232*d9f75844SAndroid Build Coastguard Worker // Without it, stubbed exceptions are being re-raised on [mock verify]. 233*d9f75844SAndroid Build Coastguard Worker // More information here: 234*d9f75844SAndroid Build Coastguard Worker //https://github.com/erikdoe/ocmock/issues/241 235*d9f75844SAndroid Build Coastguard Worker @try { 236*d9f75844SAndroid Build Coastguard Worker [mockDevice verify]; 237*d9f75844SAndroid Build Coastguard Worker } @catch (NSException* exception) { 238*d9f75844SAndroid Build Coastguard Worker if ([exception.reason isEqual:testException.reason]) { 239*d9f75844SAndroid Build Coastguard Worker // Nothing dangerous here 240*d9f75844SAndroid Build Coastguard Worker EXPECT_TRUE([exception.reason isEqualToString:exception.reason]); 241*d9f75844SAndroid Build Coastguard Worker } 242*d9f75844SAndroid Build Coastguard Worker } 243*d9f75844SAndroid Build Coastguard Worker} 244