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