Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
FlutterViewTest.mm
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import <OCMock/OCMock.h>
6#import <XCTest/XCTest.h>
7
14
16
17@interface FlutterView (Testing)
19@end
20
22@property(nonatomic) BOOL callbackCalled;
23@end
24
25@implementation FakeDelegate
26
27@synthesize platformViewsController = _platformViewsController;
28
29- (instancetype)init {
30 _callbackCalled = NO;
31 return self;
32}
33
35 asBase64Encoded:(BOOL)base64Encode {
36 return {};
37}
38
39- (void)flutterViewAccessibilityDidCall {
40 _callbackCalled = YES;
41}
42
43@end
44
45@interface FlutterViewTest : XCTestCase
46@end
47
48@implementation FlutterViewTest
49
50- (void)testFlutterViewEnableSemanticsWhenIsAccessibilityElementIsCalled {
51 FakeDelegate* delegate = [[FakeDelegate alloc] init];
52 FlutterView* view = [[FlutterView alloc] initWithDelegate:delegate opaque:NO enableWideGamut:NO];
53 delegate.callbackCalled = NO;
54 XCTAssertFalse(view.isAccessibilityElement);
55 XCTAssertTrue(delegate.callbackCalled);
56}
57
58- (void)testFlutterViewBackgroundColorIsNil {
59 FakeDelegate* delegate = [[FakeDelegate alloc] init];
60 FlutterView* view = [[FlutterView alloc] initWithDelegate:delegate opaque:NO enableWideGamut:NO];
61 XCTAssertNil(view.backgroundColor);
62}
63
64- (void)testLayerScalesMatchScreenAfterLayoutSubviews {
65 FakeDelegate* delegate = [[FakeDelegate alloc] init];
66 FlutterView* view = [[FlutterView alloc] initWithDelegate:delegate opaque:NO enableWideGamut:NO];
67 view.layer.contentsScale = CGFloat(-99.0);
68 view.layer.rasterizationScale = CGFloat(-99.0);
69 UIScreen* screen = [view screen];
70 XCTAssertNotEqual(view.layer.contentsScale, screen.scale);
71 XCTAssertNotEqual(view.layer.rasterizationScale, screen.scale);
72 [view layoutSubviews];
73 XCTAssertEqual(view.layer.contentsScale, screen.scale);
74 XCTAssertEqual(view.layer.rasterizationScale, screen.scale);
75}
76
77- (void)testViewWillMoveToWindow {
78 NSDictionary* mocks = [self createWindowMocks];
79 FlutterView* view = (FlutterView*)mocks[@"view"];
80 id mockLifecycleDelegate = mocks[@"mockLifecycleDelegate"];
81 FlutterPluginSceneLifeCycleDelegate* lifecycleDelegate =
82 (FlutterPluginSceneLifeCycleDelegate*)mocks[@"lifecycleDelegate"];
83 id mockEngine = mocks[@"mockEngine"];
84 id mockWindow = mocks[@"mockWindow"];
85
86 [view willMoveToWindow:mockWindow];
87 OCMVerify(times(1), [mockLifecycleDelegate addFlutterManagedEngine:mockEngine]);
88 XCTAssertEqual(lifecycleDelegate.flutterManagedEngines.count, 1.0);
89}
90
91- (void)testViewWillMoveToSameWindow {
92 NSDictionary* mocks = [self createWindowMocks];
93 FlutterView* view = (FlutterView*)mocks[@"view"];
94 id mockLifecycleDelegate = mocks[@"mockLifecycleDelegate"];
95 FlutterPluginSceneLifeCycleDelegate* lifecycleDelegate =
96 (FlutterPluginSceneLifeCycleDelegate*)mocks[@"lifecycleDelegate"];
97 id mockEngine = mocks[@"mockEngine"];
98 id mockWindow = mocks[@"mockWindow"];
99
100 [view willMoveToWindow:mockWindow];
101 [view willMoveToWindow:mockWindow];
102
103 OCMVerify(times(2), [mockLifecycleDelegate addFlutterManagedEngine:mockEngine]);
104 XCTAssertEqual(lifecycleDelegate.flutterManagedEngines.count, 1.0);
105}
106
107- (void)testMultipleViewsWillMoveToSameWindow {
108 NSDictionary* mocks = [self createWindowMocks];
109 FlutterView* view1 = (FlutterView*)mocks[@"view"];
110 id mockLifecycleDelegate = mocks[@"mockLifecycleDelegate"];
111 FlutterPluginSceneLifeCycleDelegate* lifecycleDelegate =
112 (FlutterPluginSceneLifeCycleDelegate*)mocks[@"lifecycleDelegate"];
113 id mockEngine1 = mocks[@"mockEngine"];
114 id mockWindow1 = mocks[@"mockWindow"];
115
116 id mockEngine2 = OCMClassMock([FlutterEngine class]);
117 FlutterView* view2 = [[FlutterView alloc] initWithDelegate:mockEngine2
118 opaque:NO
119 enableWideGamut:NO];
120
121 [view1 willMoveToWindow:mockWindow1];
122 [view2 willMoveToWindow:mockWindow1];
123 [view1 willMoveToWindow:mockWindow1];
124 OCMVerify(times(2), [mockLifecycleDelegate addFlutterManagedEngine:mockEngine1]);
125 OCMVerify(times(1), [mockLifecycleDelegate addFlutterManagedEngine:mockEngine2]);
126 XCTAssertEqual(lifecycleDelegate.flutterManagedEngines.count, 2.0);
127}
128
129- (void)testMultipleViewsWillMoveToDifferentWindow {
130 NSDictionary* mocks = [self createWindowMocks];
131 FlutterView* view1 = (FlutterView*)mocks[@"view"];
132 id mockLifecycleDelegate1 = mocks[@"mockLifecycleDelegate"];
133 FlutterPluginSceneLifeCycleDelegate* lifecycleDelegate1 =
134 (FlutterPluginSceneLifeCycleDelegate*)mocks[@"lifecycleDelegate"];
135 id mockEngine1 = mocks[@"mockEngine"];
136 id mockWindow1 = mocks[@"mockWindow"];
137
138 NSDictionary* mocks2 = [self createWindowMocks];
139 FlutterView* view2 = (FlutterView*)mocks2[@"view"];
140 id mockLifecycleDelegate2 = mocks2[@"mockLifecycleDelegate"];
141 FlutterPluginSceneLifeCycleDelegate* lifecycleDelegate2 =
142 (FlutterPluginSceneLifeCycleDelegate*)mocks2[@"lifecycleDelegate"];
143 id mockEngine2 = mocks2[@"mockEngine"];
144 id mockWindow2 = mocks2[@"mockWindow"];
145
146 [view1 willMoveToWindow:mockWindow1];
147 [view2 willMoveToWindow:mockWindow2];
148 [view1 willMoveToWindow:mockWindow1];
149 OCMVerify(times(2), [mockLifecycleDelegate1 addFlutterManagedEngine:mockEngine1]);
150 OCMVerify(times(1), [mockLifecycleDelegate2 addFlutterManagedEngine:mockEngine2]);
151 XCTAssertEqual(lifecycleDelegate1.flutterManagedEngines.count, 1.0);
152 XCTAssertEqual(lifecycleDelegate2.flutterManagedEngines.count, 1.0);
153}
154
155- (void)testViewRemovedFromWindowAndAddedToNewScene {
156 NSDictionary* mocks = [self createWindowMocks];
157 FlutterView* view = (FlutterView*)mocks[@"view"];
158 id mockLifecycleDelegate = mocks[@"mockLifecycleDelegate"];
159 FlutterPluginSceneLifeCycleDelegate* lifecycleDelegate =
160 (FlutterPluginSceneLifeCycleDelegate*)mocks[@"lifecycleDelegate"];
161 id mockEngine = mocks[@"mockEngine"];
162 id mockWindow = mocks[@"mockWindow"];
163
164 NSDictionary* mocks2 = [self createWindowMocks];
165 id mockWindow2 = mocks2[@"mockWindow"];
166 id mockLifecycleDelegate2 = mocks2[@"mockLifecycleDelegate"];
167 FlutterPluginSceneLifeCycleDelegate* lifecycleDelegate2 =
168 (FlutterPluginSceneLifeCycleDelegate*)mocks2[@"lifecycleDelegate"];
169
170 id mockView = OCMPartialMock(view);
171
172 [mockView willMoveToWindow:mockWindow];
173 OCMVerify(times(1), [mockLifecycleDelegate addFlutterManagedEngine:mockEngine]);
174 XCTAssertEqual(lifecycleDelegate.flutterManagedEngines.count, 1.0);
175
176 OCMStub([mockView window]).andReturn(mockWindow);
177 [mockView willMoveToWindow:nil];
178 XCTAssertEqual(lifecycleDelegate.flutterManagedEngines.count, 1.0);
179
180 OCMStub([mockView window]).andReturn(nil);
181 [mockView willMoveToWindow:mockWindow2];
182 OCMVerify(times(1), [mockLifecycleDelegate removeFlutterManagedEngine:mockEngine]);
183 XCTAssertEqual(lifecycleDelegate.flutterManagedEngines.count, 0.0);
184 OCMVerify(times(1), [mockLifecycleDelegate2 addFlutterManagedEngine:mockEngine]);
185 XCTAssertEqual(lifecycleDelegate2.flutterManagedEngines.count, 1.0);
186}
187
188- (NSDictionary*)createWindowMocks {
189 return [self createWindowMocksWithWideGamut:NO];
190}
191
192- (NSDictionary*)createWindowMocksWithWideGamut:(BOOL)enableWideGamut {
193 id mockEngine = OCMClassMock([FlutterEngine class]);
194 FlutterView* view = [[FlutterView alloc] initWithDelegate:mockEngine
195 opaque:NO
196 enableWideGamut:enableWideGamut];
197 id mockWindow = OCMClassMock([UIWindow class]);
198 id mockWindowScene = OCMClassMock([UIWindowScene class]);
199
200 FlutterSceneDelegate* sceneDelegate = [[FlutterSceneDelegate alloc] init];
201 id mockSceneDelegate = OCMPartialMock(sceneDelegate);
202
203 FlutterPluginSceneLifeCycleDelegate* lifecycleDelegate =
205 id mockLifecycleDelegate = OCMPartialMock(lifecycleDelegate);
206
207 OCMStub([mockWindow windowScene]).andReturn(mockWindowScene);
208 OCMStub([mockWindowScene delegate]).andReturn(mockSceneDelegate);
209 OCMStub([mockSceneDelegate sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
210
211 return @{
212 @"view" : view,
213 @"mockLifecycleDelegate" : mockLifecycleDelegate,
214 @"lifecycleDelegate" : lifecycleDelegate,
215 @"mockEngine" : mockEngine,
216 @"mockWindow" : mockWindow,
217 };
218}
219
220#pragma mark - Wide Gamut Tests
221
222// Helper: add FlutterView to a real UIWindow so that layoutSubviews can access screen.
223- (FlutterView*)createViewInWindowWithWideGamut:(BOOL)enableWideGamut {
224 FakeDelegate* delegate = [[FakeDelegate alloc] init];
225 FlutterView* view = [[FlutterView alloc] initWithDelegate:delegate
226 opaque:NO
227 enableWideGamut:enableWideGamut];
228 // Add to a real window so layoutSubviews has access to screen.
229 UIWindow* window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
230 [window addSubview:view];
231 view.frame = window.bounds;
232 [view layoutSubviews];
233 return view;
234}
235
236- (void)testWideGamutViewSetsBGRA10XRPixelFormat {
237 FlutterView* view = [self createViewInWindowWithWideGamut:YES];
238 // On a wide gamut capable device, the pixel format should be BGRA10_XR.
239 // On non-wide-gamut devices, it falls back to BGRA8Unorm.
240 if ([view isWideGamutSupported]) {
241 XCTAssertEqual(view.pixelFormat, MTLPixelFormatBGRA10_XR);
242 } else {
243 XCTAssertEqual(view.pixelFormat, MTLPixelFormatBGRA8Unorm);
244 }
245}
246
247- (void)testStandardGamutViewKeepsBGRA8Unorm {
248 FlutterView* view = [self createViewInWindowWithWideGamut:NO];
249 XCTAssertEqual(view.pixelFormat, MTLPixelFormatBGRA8Unorm);
250}
251
252- (void)testWideGamutViewSetsExtendedSRGBColorSpace {
253 FlutterView* view = [self createViewInWindowWithWideGamut:YES];
254 if ([view isWideGamutSupported]) {
255 CAMetalLayer* layer = (CAMetalLayer*)view.layer;
256 CGColorSpaceRef colorSpace = layer.colorspace;
257 XCTAssertNotNil((__bridge id)colorSpace);
258 CGColorSpaceRef extendedSRGB = CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB);
259 XCTAssertTrue(CFEqual(colorSpace, extendedSRGB));
260 CGColorSpaceRelease(extendedSRGB);
261 }
262}
263
264- (void)testStandardGamutViewDoesNotSetExtendedColorSpace {
265 FlutterView* view = [self createViewInWindowWithWideGamut:NO];
266 CAMetalLayer* layer = (CAMetalLayer*)view.layer;
267 // Default CAMetalLayer colorspace is nil (device default sRGB).
268 XCTAssertNil((__bridge id)layer.colorspace);
269}
270
271#pragma mark - FlutterOverlayView Wide Gamut Tests
272
273- (void)testOverlayViewWideGamutSetsBGRA10XR {
274 FlutterOverlayView* overlay =
275 [[FlutterOverlayView alloc] initWithContentsScale:2.0 pixelFormat:MTLPixelFormatBGRA10_XR];
276 CAMetalLayer* layer = (CAMetalLayer*)overlay.layer;
277 XCTAssertEqual(layer.pixelFormat, MTLPixelFormatBGRA10_XR);
278}
279
280- (void)testOverlayViewWideGamutSetsExtendedSRGBColorSpace {
281 FlutterOverlayView* overlay =
282 [[FlutterOverlayView alloc] initWithContentsScale:2.0 pixelFormat:MTLPixelFormatBGRA10_XR];
283 CAMetalLayer* layer = (CAMetalLayer*)overlay.layer;
284 CGColorSpaceRef colorSpace = layer.colorspace;
285 XCTAssertNotNil((__bridge id)colorSpace);
286 CGColorSpaceRef extendedSRGB = CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB);
287 XCTAssertTrue(CFEqual(colorSpace, extendedSRGB));
288 CGColorSpaceRelease(extendedSRGB);
289}
290
291- (void)testOverlayViewStandardGamutKeepsBGRA8Unorm {
292 FlutterOverlayView* overlay =
293 [[FlutterOverlayView alloc] initWithContentsScale:2.0 pixelFormat:MTLPixelFormatBGRA8Unorm];
294 CAMetalLayer* layer = (CAMetalLayer*)overlay.layer;
295 XCTAssertEqual(layer.pixelFormat, MTLPixelFormatBGRA8Unorm);
296}
297
298- (void)testOverlayViewStandardGamutDoesNotSetExtendedColorSpace {
299 FlutterOverlayView* overlay =
300 [[FlutterOverlayView alloc] initWithContentsScale:2.0 pixelFormat:MTLPixelFormatBGRA8Unorm];
301 CAMetalLayer* layer = (CAMetalLayer*)overlay.layer;
302 XCTAssertNil((__bridge id)layer.colorspace);
303}
304
305- (void)testOverlayViewContentsScaleIsSet {
306 FlutterOverlayView* overlay =
307 [[FlutterOverlayView alloc] initWithContentsScale:3.0 pixelFormat:MTLPixelFormatBGRA10_XR];
308 XCTAssertEqual(overlay.layer.contentsScale, 3.0);
309 XCTAssertEqual(overlay.layer.rasterizationScale, 3.0);
310}
311
312@end
GLenum type
ScreenshotType
The type of the screenshot to obtain of the previously rendered layer tree.
Definition rasterizer.h:348
GLFWwindow * window
Definition main.cc:60
FlView * view
UIScreen * screen()
A POD type used to return the screenshot data along with the size of the frame.
Definition rasterizer.h:399
int BOOL