| /* | 
 |  *  Copyright 2016 The WebRTC project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #import <Foundation/Foundation.h> | 
 | #import <OCMock/OCMock.h> | 
 |  | 
 | #include "rtc_base/gunit.h" | 
 |  | 
 | // Width and height don't play any role so lets use predefined values throughout | 
 | // the tests. | 
 | static const int kFormatWidth = 789; | 
 | static const int kFormatHeight = 987; | 
 |  | 
 | // Hardcoded framrate to be used throughout the tests. | 
 | static const int kFramerate = 30; | 
 |  | 
 | // Same width and height is used so it's ok to expect same cricket::VideoFormat | 
 | static cricket::VideoFormat expectedFormat = | 
 |     cricket::VideoFormat(kFormatWidth, | 
 |                          kFormatHeight, | 
 |                          cricket::VideoFormat::FpsToInterval(kFramerate), | 
 |                          cricket::FOURCC_NV12); | 
 |  | 
 | // Mock class for AVCaptureDeviceFormat. | 
 | // Custom implementation needed because OCMock cannot handle the | 
 | // CMVideoDescriptionRef mocking. | 
 | @interface AVCaptureDeviceFormatMock : NSObject | 
 |  | 
 | @property (nonatomic, assign) CMVideoFormatDescriptionRef format; | 
 | @property (nonatomic, strong) OCMockObject *rangeMock; | 
 |  | 
 | - (instancetype)initWithMediaSubtype:(FourCharCode)subtype | 
 |                               minFps:(float)minFps | 
 |                               maxFps:(float)maxFps; | 
 | + (instancetype)validFormat; | 
 | + (instancetype)invalidFpsFormat; | 
 | + (instancetype)invalidMediaSubtypeFormat; | 
 |  | 
 | @end | 
 |  | 
 | @implementation AVCaptureDeviceFormatMock | 
 |  | 
 | @synthesize format = _format; | 
 | @synthesize rangeMock = _rangeMock; | 
 |  | 
 | - (instancetype)initWithMediaSubtype:(FourCharCode)subtype | 
 |                               minFps:(float)minFps | 
 |                               maxFps:(float)maxFps { | 
 |   if (self = [super init]) { | 
 |     CMVideoFormatDescriptionCreate(nil, subtype, kFormatWidth, kFormatHeight, | 
 |                                    nil, &_format); | 
 |     // We can use OCMock for the range. | 
 |     _rangeMock = [OCMockObject mockForClass:[AVFrameRateRange class]]; | 
 |     [[[_rangeMock stub] andReturnValue:@(minFps)] minFrameRate]; | 
 |     [[[_rangeMock stub] andReturnValue:@(maxFps)] maxFrameRate]; | 
 |   } | 
 |  | 
 |   return self; | 
 | } | 
 |  | 
 | + (instancetype)validFormat { | 
 |   AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc] | 
 |       initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange | 
 |                     minFps:0.0 | 
 |                     maxFps:30.0]; | 
 |   return instance; | 
 | } | 
 |  | 
 | + (instancetype)invalidFpsFormat { | 
 |   AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc] | 
 |       initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange | 
 |                     minFps:0.0 | 
 |                     maxFps:22.0]; | 
 |   return instance; | 
 | } | 
 |  | 
 | + (instancetype)invalidMediaSubtypeFormat { | 
 |   AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc] | 
 |       initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8Planar | 
 |                     minFps:0.0 | 
 |                     maxFps:60.0]; | 
 |   return instance; | 
 | } | 
 |  | 
 | - (void)dealloc { | 
 |   if (_format != nil) { | 
 |     CFRelease(_format); | 
 |     _format = nil; | 
 |   } | 
 | } | 
 |  | 
 | // Redefinition of AVCaptureDevice methods we want to mock. | 
 | - (CMVideoFormatDescriptionRef)formatDescription { | 
 |   return self.format; | 
 | } | 
 |  | 
 | - (NSArray *)videoSupportedFrameRateRanges { | 
 |   return @[ self.rangeMock ]; | 
 | } | 
 |  | 
 | @end | 
 |  | 
 | TEST(AVFormatMapperTest, SuportedCricketFormatsWithInvalidFramerateFormats) { | 
 |   // given | 
 |   id mockDevice = OCMClassMock([AVCaptureDevice class]); | 
 |  | 
 |   // Valid media subtype, invalid framerate | 
 |   AVCaptureDeviceFormatMock* mock = | 
 |       [AVCaptureDeviceFormatMock invalidFpsFormat]; | 
 |   OCMStub([mockDevice formats]).andReturn(@[ mock ]); | 
 |  | 
 |   // when | 
 |   std::set<cricket::VideoFormat> result = | 
 |       webrtc::GetSupportedVideoFormatsForDevice(mockDevice); | 
 |  | 
 |   // then | 
 |   EXPECT_TRUE(result.empty()); | 
 | } | 
 |  | 
 | TEST(AVFormatMapperTest, SuportedCricketFormatsWithInvalidFormats) { | 
 |   // given | 
 |   id mockDevice = OCMClassMock([AVCaptureDevice class]); | 
 |  | 
 |   // Invalid media subtype, valid framerate | 
 |   AVCaptureDeviceFormatMock* mock = | 
 |       [AVCaptureDeviceFormatMock invalidMediaSubtypeFormat]; | 
 |   OCMStub([mockDevice formats]).andReturn(@[ mock ]); | 
 |  | 
 |   // when | 
 |   std::set<cricket::VideoFormat> result = | 
 |       webrtc::GetSupportedVideoFormatsForDevice(mockDevice); | 
 |  | 
 |   // then | 
 |   EXPECT_TRUE(result.empty()); | 
 | } | 
 |  | 
 | TEST(AVFormatMapperTest, SuportedCricketFormats) { | 
 |   // given | 
 |   id mockDevice = OCMClassMock([AVCaptureDevice class]); | 
 |  | 
 |   // valid media subtype, valid framerate | 
 |   AVCaptureDeviceFormatMock* mock = [AVCaptureDeviceFormatMock validFormat]; | 
 |   OCMStub([mockDevice formats]).andReturn(@[ mock ]); | 
 |  | 
 |   // when | 
 |   std::set<cricket::VideoFormat> result = | 
 |       webrtc::GetSupportedVideoFormatsForDevice(mockDevice); | 
 |  | 
 |   // then | 
 |   EXPECT_EQ(1u, result.size()); | 
 |   // make sure the set has the expected format | 
 |   EXPECT_EQ(expectedFormat, *result.begin()); | 
 | } | 
 |  | 
 | TEST(AVFormatMapperTest, MediaSubtypePreference) { | 
 |   // given | 
 |   id mockDevice = OCMClassMock([AVCaptureDevice class]); | 
 |  | 
 |   // valid media subtype, valid framerate | 
 |   AVCaptureDeviceFormatMock* mockOne = [[AVCaptureDeviceFormatMock alloc] | 
 |       initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange | 
 |                     minFps:0.0 | 
 |                     maxFps:30.0]; | 
 |   // valid media subtype, valid framerate. | 
 |   // This media subtype should be the preffered one. | 
 |   AVCaptureDeviceFormatMock* mockTwo = [[AVCaptureDeviceFormatMock alloc] | 
 |       initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange | 
 |                     minFps:0.0 | 
 |                     maxFps:30.0]; | 
 |   OCMStub([mockDevice lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES); | 
 |   OCMStub([mockDevice unlockForConfiguration]); | 
 |   NSArray* array = @[ mockOne, mockTwo ]; | 
 |   OCMStub([mockDevice formats]).andReturn(array); | 
 |  | 
 |   // to verify | 
 |   OCMExpect([mockDevice setActiveFormat:(AVCaptureDeviceFormat*)mockTwo]); | 
 |   OCMExpect( | 
 |       [mockDevice setActiveVideoMinFrameDuration:CMTimeMake(1, kFramerate)]); | 
 |  | 
 |   // when | 
 |   bool resultFormat = | 
 |       webrtc::SetFormatForCaptureDevice(mockDevice, nil, expectedFormat); | 
 |  | 
 |   // then | 
 |   EXPECT_TRUE(resultFormat); | 
 |   [mockDevice verify]; | 
 | } | 
 |  | 
 | TEST(AVFormatMapperTest, SetFormatWhenDeviceCannotLock) { | 
 |   // given | 
 |   id mockDevice = OCMClassMock([AVCaptureDevice class]); | 
 |   [[[mockDevice stub] andReturnValue:@(NO)] | 
 |       lockForConfiguration:[OCMArg setTo:nil]]; | 
 |   [[[mockDevice stub] andReturn:@[]] formats]; | 
 |  | 
 |   // when | 
 |   bool resultFormat = webrtc::SetFormatForCaptureDevice(mockDevice, nil, | 
 |                                                         cricket::VideoFormat()); | 
 |  | 
 |   // then | 
 |   EXPECT_FALSE(resultFormat); | 
 | } | 
 |  | 
 | TEST(AVFormatMapperTest, SetFormatWhenFormatIsIncompatible) { | 
 |   // given | 
 |   id mockDevice = OCMClassMock([AVCaptureDevice class]); | 
 |   OCMStub([mockDevice formats]).andReturn(@[]); | 
 |   OCMStub([mockDevice lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES); | 
 |   NSException* testException = | 
 |       [NSException exceptionWithName:@"Test exception" | 
 |                               reason:@"Raised from unit tests" | 
 |                             userInfo:nil]; | 
 |   OCMStub([mockDevice setActiveFormat:[OCMArg any]]).andThrow(testException); | 
 |   OCMExpect([mockDevice unlockForConfiguration]); | 
 |  | 
 |   // when | 
 |   bool resultFormat = webrtc::SetFormatForCaptureDevice(mockDevice, nil, | 
 |                                                         cricket::VideoFormat()); | 
 |  | 
 |   // then | 
 |   EXPECT_FALSE(resultFormat); | 
 |  | 
 |   // TODO(denicija): Remove try-catch when Chromium rolls this change: | 
 |   // https://github.com/erikdoe/ocmock/commit/de1419415581dc307045e54bfe9c98c86efea96b | 
 |   // Without it, stubbed exceptions are being re-raised on [mock verify]. | 
 |   // More information here: | 
 |   //https://github.com/erikdoe/ocmock/issues/241 | 
 |   @try { | 
 |     [mockDevice verify]; | 
 |   } @catch (NSException* exception) { | 
 |     if ([exception.reason isEqual:testException.reason]) { | 
 |       // Nothing dangerous here | 
 |       EXPECT_TRUE([exception.reason isEqualToString:exception.reason]); | 
 |     } | 
 |   } | 
 | } |