| Flutter iOS Embedder |

FlutterSceneLifeCycleTest.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 <Foundation/Foundation.h>
6 #import <OCMock/OCMock.h>
7 #import <UIKit/UIKit.h>
8 #import <XCTest/XCTest.h>
9 #include <objc/NSObject.h>
10
11 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
12 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h"
13 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate_internal.h"
14 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterRestorationPlugin.h"
15 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterSceneLifeCycle_Internal.h"
16 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterSceneLifeCycle_Test.h"
17 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterSharedApplication.h"
18
19 FLUTTER_ASSERT_ARC
20
21 // This class is meant to mimic the SceneDelegate class generated by SwiftUI that does not conform
22 // FlutterSceneLifeCycleProvider but actually does.
23 @interface FlutterSwiftUIAppSceneDelegate : NSObject
24 @property(nonatomic, readonly) FlutterPluginSceneLifeCycleDelegate* sceneLifeCycleDelegate;
25 @end
26
27 @implementation FlutterSwiftUIAppSceneDelegate
28
29 @synthesize sceneLifeCycleDelegate = _sceneLifeCycleDelegate;
30
31 - (instancetype)init {
32 if (self = [super init]) {
33 _sceneLifeCycleDelegate = [[FlutterPluginSceneLifeCycleDelegate alloc] init];
34 }
35 return self;
36 }
37 @end
38
39 // This class is not referenced in test because it's a test itself. The implementation
40 // verifies that sceneLifeCycleDelegate is a readonly property.
41 @interface TestSceneLifecycleProvider : NSObject <FlutterSceneLifeCycleProvider>
42 @end
43 @implementation TestSceneLifecycleProvider
44 - (FlutterPluginSceneLifeCycleDelegate*)sceneLifeCycleDelegate {
45 return [[FlutterPluginSceneLifeCycleDelegate alloc] init];
46 }
47 @end
48
49 @interface FlutterSceneLifecycleTest : XCTestCase
50 @end
51
52 @implementation FlutterSceneLifecycleTest
53 - (void)setUp {
54 }
55
56 - (void)tearDown {
57 }
58
59 #pragma mark - FlutterPluginSceneLifeCycleDelegate
60
61 - (void)testAddFlutterManagedEngine {
62 FlutterPluginSceneLifeCycleDelegate* delegate =
63 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
64
65 id mockEngine = OCMClassMock([FlutterEngine class]);
66 id mockLifecycleDelegate = OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
67 OCMStub([mockEngine sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
68
69 [delegate addFlutterManagedEngine:mockEngine];
70 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
71 }
72
73 - (void)testAddDuplicateFlutterEngine {
74 FlutterPluginSceneLifeCycleDelegate* delegate =
75 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
76
77 id mockEngine = OCMClassMock([FlutterEngine class]);
78 id mockLifecycleDelegate = OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
79 OCMStub([mockEngine sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
80
81 [delegate addFlutterManagedEngine:mockEngine];
82 [delegate addFlutterManagedEngine:mockEngine];
83 [delegate addFlutterManagedEngine:mockEngine];
84 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
85 }
86
87 - (void)testAddMultipleFlutterEngine {
88 FlutterPluginSceneLifeCycleDelegate* delegate =
89 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
90
91 id mockEngine = OCMClassMock([FlutterEngine class]);
92 id mockLifecycleDelegate = OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
93 OCMStub([mockEngine sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
94 [delegate addFlutterManagedEngine:mockEngine];
95
96 id mockEngine2 = OCMClassMock([FlutterEngine class]);
97 id mockLifecycleDelegate2 = OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
98 OCMStub([mockEngine2 sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate2);
99 [delegate addFlutterManagedEngine:mockEngine2];
100
101 XCTAssertEqual(delegate.flutterManagedEngines.count, 2.0);
102 }
103
104 - (void)testRemoveFlutterManagedEngine {
105 FlutterPluginSceneLifeCycleDelegate* delegate =
106 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
107
108 id mockEngine = OCMClassMock([FlutterEngine class]);
109 [delegate addFlutterManagedEngine:mockEngine];
110 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
111
112 [delegate removeFlutterManagedEngine:mockEngine];
113 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
114 }
115
116 - (void)testRemoveNotFoundFlutterEngine {
117 FlutterPluginSceneLifeCycleDelegate* delegate =
118 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
119
120 id mockEngine = OCMClassMock([FlutterEngine class]);
121 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
122
123 [delegate removeFlutterManagedEngine:mockEngine];
124 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
125 }
126
127 - (void)testupdateFlutterManagedEnginesInSceneRemovesDeallocatedEngine {
128 FlutterPluginSceneLifeCycleDelegate* delegate =
129 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
130
131 id mockWindowScene = OCMClassMock([UIWindowScene class]);
132
133 @autoreleasepool {
134 id mockEngine = OCMClassMock([FlutterEngine class]);
135 [delegate addFlutterManagedEngine:mockEngine];
136 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
137 }
138
139 [delegate updateFlutterManagedEnginesInScene:mockWindowScene];
140 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
141 }
142
143 - (void)testupdateFlutterManagedEnginesInSceneRemovesEngineNotInScene {
144 FlutterPluginSceneLifeCycleDelegate* delegate =
145 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
146
147 id mockEngine = OCMClassMock([FlutterEngine class]);
148 id mockViewController = OCMClassMock([UIViewController class]);
149 id mockView = OCMClassMock([UIView class]);
150 id mockWindow = OCMClassMock([UIWindow class]);
151 id mockWindowScene = OCMClassMock([UIWindowScene class]);
152 id mockLifecycleProvider = OCMProtocolMock(@protocol(FlutterSceneLifeCycleProvider));
153 id mockLifecycleDelegate = OCMClassMock([FlutterPluginSceneLifeCycleDelegate class]);
154 OCMStub([mockEngine viewController]).andReturn(mockViewController);
155 OCMStub([mockViewController view]).andReturn(mockView);
156 OCMStub([mockView window]).andReturn(mockWindow);
157 OCMStub([mockWindow windowScene]).andReturn(mockWindowScene);
158 OCMStub([mockWindow windowScene]).andReturn(mockWindowScene);
159 OCMStub([mockWindowScene delegate]).andReturn(mockLifecycleProvider);
160 OCMStub([mockLifecycleProvider sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
161
162 [delegate addFlutterManagedEngine:mockEngine];
163 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
164
165 id mockWindowScene2 = OCMClassMock([UIWindowScene class]);
166
167 [delegate updateFlutterManagedEnginesInScene:mockWindowScene2];
168 OCMVerify(times(1), [mockLifecycleDelegate addFlutterManagedEngine:mockEngine]);
169 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
170 }
171
172 - (void)testupdateFlutterManagedEnginesInSceneDoesNotRemoveEngineWithNilScene {
173 FlutterPluginSceneLifeCycleDelegate* delegate =
174 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
175 id mockEngine = OCMClassMock([FlutterEngine class]);
176 id mockWindowScene = OCMClassMock([UIWindowScene class]);
177 [delegate addFlutterManagedEngine:mockEngine];
178 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
179
180 [delegate updateFlutterManagedEnginesInScene:mockWindowScene];
181 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
182 }
183
184 - (void)testManuallyRegisterSceneLifeCycleWithFlutterEngine {
185 FlutterPluginSceneLifeCycleDelegate* delegate =
186 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
187
188 id mockEngine = OCMClassMock([FlutterEngine class]);
189 id mockLifecycleDelegate = OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
190 OCMStub([mockEngine sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
191
192 [delegate registerSceneLifeCycleWithFlutterEngine:mockEngine];
193 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
194 XCTAssertEqual(delegate.developerManagedEngines.count, 1.0);
195 }
196
197 - (void)testManuallyUnregisterSceneLifeCycleWithFlutterEngine {
198 FlutterPluginSceneLifeCycleDelegate* delegate =
199 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
200
201 id mockEngine = OCMClassMock([FlutterEngine class]);
202 id mockLifecycleDelegate = OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
203 OCMStub([mockEngine sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
204
205 [delegate registerSceneLifeCycleWithFlutterEngine:mockEngine];
206 [delegate unregisterSceneLifeCycleWithFlutterEngine:mockEngine];
207 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
208 XCTAssertEqual(delegate.developerManagedEngines.count, 0.0);
209 }
210
211 - (void)testManuallyRegisterSceneLifeCycleWithFlutterEngineCannotBeAutoAdded {
212 FlutterPluginSceneLifeCycleDelegate* delegate =
213 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
214
215 FlutterEngine* engine = [[FlutterEngine alloc] init];
216 id mockEngine = OCMPartialMock(engine);
217 id mockLifecycleDelegate = OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
218 OCMStub([mockEngine sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
219
220 [delegate registerSceneLifeCycleWithFlutterEngine:mockEngine];
221 [delegate addFlutterManagedEngine:mockEngine];
222 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
223 XCTAssertEqual(delegate.developerManagedEngines.count, 1.0);
224
225 [delegate unregisterSceneLifeCycleWithFlutterEngine:mockEngine];
226 [delegate addFlutterManagedEngine:mockEngine];
227 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
228 XCTAssertEqual(delegate.developerManagedEngines.count, 0.0);
229
230 [delegate registerSceneLifeCycleWithFlutterEngine:mockEngine];
231 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
232 XCTAssertEqual(delegate.developerManagedEngines.count, 1.0);
233 }
234
235 - (void)testManuallyRegisterSceneLifeCycleWithFlutterEngineCannotBeAutoRemoved {
236 FlutterPluginSceneLifeCycleDelegate* delegate =
237 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
238
239 id mockEngine = OCMClassMock([FlutterEngine class]);
240 id mockLifecycleDelegate = OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
241 OCMStub([mockEngine sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
242
243 [delegate registerSceneLifeCycleWithFlutterEngine:mockEngine];
244 [delegate removeFlutterManagedEngine:mockEngine];
245 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
246 XCTAssertEqual(delegate.developerManagedEngines.count, 1.0);
247 }
248
249 - (void)testManuallyRegisterSceneLifeCycleWithFlutterEngineRemovesAutomatic {
250 FlutterPluginSceneLifeCycleDelegate* delegate =
251 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
252
253 id mockEngine = OCMClassMock([FlutterEngine class]);
254 id mockLifecycleDelegate = OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
255 OCMStub([mockEngine sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
256
257 [delegate addFlutterManagedEngine:mockEngine];
258 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
259 [delegate registerSceneLifeCycleWithFlutterEngine:mockEngine];
260 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
261 XCTAssertEqual(delegate.developerManagedEngines.count, 1.0);
262 }
263
264 - (void)testAllEnginesContainsManualAndAutomatic {
265 FlutterPluginSceneLifeCycleDelegate* delegate =
266 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
267
268 id mockEngine = OCMClassMock([FlutterEngine class]);
269 id mockLifecycleDelegate = OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
270 OCMStub([mockEngine sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
271
272 id mockEngine2 = OCMClassMock([FlutterEngine class]);
273
274 [delegate addFlutterManagedEngine:mockEngine];
275 [delegate registerSceneLifeCycleWithFlutterEngine:mockEngine2];
276
277 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
278 XCTAssertEqual(delegate.developerManagedEngines.count, 1.0);
279 XCTAssertEqual(delegate.allEngines.count, 2.0);
280 XCTAssertEqual([delegate.allEngines objectAtIndex:0], mockEngine);
281 XCTAssertEqual([delegate.allEngines objectAtIndex:1], mockEngine2);
282 }
283
284 - (void)testEngineReceivedConnectNotificationForSceneBeforeActualEvent {
285 FlutterPluginSceneLifeCycleDelegate* delegate =
286 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
287 FlutterPluginSceneLifeCycleDelegate* mockDelegate = OCMPartialMock(delegate);
288 id mocks = [self mocksForEvents];
289 id mockEngine = mocks[@"mockEngine"];
290 id mockScene = mocks[@"mockScene"];
291 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
292 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
293 OCMStub([mockLifecycleDelegate scene:[OCMArg any]
294 willConnectToSession:[OCMArg any]
295 options:[OCMArg any]])
296 .andReturn(YES);
297
298 id mocks2 = [self mocksForEvents];
299 id mockEngine2 = mocks2[@"mockEngine"];
300 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate2 =
301 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks2[@"mockLifecycleDelegate"];
302 OCMStub([mockLifecycleDelegate2 scene:[OCMArg any]
303 willConnectToSession:[OCMArg any]
304 options:[OCMArg any]])
305 .andReturn(YES);
306
307 // received notification
308 [mockDelegate engine:mockEngine receivedConnectNotificationFor:mockScene];
309 [mockDelegate engine:mockEngine2 receivedConnectNotificationFor:mockScene];
310 OCMVerify(times(1), [mockDelegate addFlutterManagedEngine:mockEngine]);
311 OCMVerify(times(1), [mockDelegate addFlutterManagedEngine:mockEngine2]);
312 XCTAssertEqual(delegate.flutterManagedEngines.count, 2.0);
313 OCMVerify(times(0), [mockDelegate scene:[OCMArg any]
314 willConnectToSession:[OCMArg any]
315 options:[OCMArg any]]);
316
317 // actual event
318 id session = OCMClassMock([UISceneSession class]);
319 id options = OCMClassMock([UISceneConnectionOptions class]);
320 [mockDelegate scene:mockScene willConnectToSession:session options:options];
321 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene
322 willConnectToSession:session
323 options:options]);
324 OCMVerify(times(1), [mockLifecycleDelegate2 scene:mockScene
325 willConnectToSession:session
326 options:nil]);
327 XCTAssertEqual(delegate.flutterManagedEngines.count, 2.0);
328 }
329
330 - (void)testEngineReceivedConnectNotificationForSceneAfterActualEvent {
331 FlutterPluginSceneLifeCycleDelegate* delegate =
332 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
333 FlutterPluginSceneLifeCycleDelegate* mockDelegate = OCMPartialMock(delegate);
334 id mocks = [self mocksForEvents];
335 id mockEngine = mocks[@"mockEngine"];
336 id mockScene = mocks[@"mockScene"];
337 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
338 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
339 OCMStub([mockLifecycleDelegate scene:[OCMArg any]
340 willConnectToSession:[OCMArg any]
341 options:[OCMArg any]])
342 .andReturn(YES);
343 id mocks2 = [self mocksForEvents];
344 id mockEngine2 = mocks2[@"mockEngine"];
345 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate2 =
346 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks2[@"mockLifecycleDelegate"];
347 OCMStub([mockLifecycleDelegate2 scene:[OCMArg any]
348 willConnectToSession:[OCMArg any]
349 options:[OCMArg any]])
350 .andReturn(YES);
351
352 // actual event
353 id session = OCMClassMock([UISceneSession class]);
354 id options = OCMClassMock([UISceneConnectionOptions class]);
355 OCMStub([mockScene session]).andReturn(session);
356 [mockDelegate scene:mockScene willConnectToSession:session options:options];
357 XCTAssertEqual(delegate.flutterManagedEngines.count, 0.0);
358 OCMVerify(times(0), [mockLifecycleDelegate scene:mockScene
359 willConnectToSession:session
360 options:options]);
361 OCMVerify(times(0), [mockLifecycleDelegate2 scene:mockScene
362 willConnectToSession:session
363 options:options]);
364 OCMStub([mockDelegate connectionOptions]).andReturn(options);
365
366 // received notification
367 [mockDelegate engine:mockEngine receivedConnectNotificationFor:mockScene];
368 [mockDelegate engine:mockEngine2 receivedConnectNotificationFor:mockScene];
369
370 OCMVerify(times(1), [mockDelegate addFlutterManagedEngine:mockEngine]);
371 OCMVerify(times(1), [mockDelegate addFlutterManagedEngine:mockEngine2]);
372 XCTAssertEqual(delegate.flutterManagedEngines.count, 2.0);
373 OCMVerify(times(1), [mockDelegate scene:mockScene willConnectToSession:session options:options]);
374 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene
375 willConnectToSession:session
376 options:options]);
377 OCMVerify(times(1), [mockLifecycleDelegate2 scene:mockScene
378 willConnectToSession:session
379 options:nil]);
380 }
381
382 - (void)testSceneWillConnectToSessionOptionsHandledByScenePlugin {
383 FlutterPluginSceneLifeCycleDelegate* delegate =
384 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
385
386 id mocks = [self mocksForEvents];
387 id mockEngine = mocks[@"mockEngine"];
388 id mockScene = mocks[@"mockScene"];
389 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
390 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
391 OCMStub([mockLifecycleDelegate scene:[OCMArg any]
392 willConnectToSession:[OCMArg any]
393 options:[OCMArg any]])
394 .andReturn(YES);
395 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
396 OCMStub([mockAppLifecycleDelegate sceneWillConnectFallback:[OCMArg any]]).andReturn(YES);
397
398 id session = OCMClassMock([UISceneSession class]);
399 id options = OCMClassMock([UISceneConnectionOptions class]);
400
401 [delegate addFlutterManagedEngine:mockEngine];
402 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
403
404 [delegate scene:mockScene willConnectToSession:session options:options];
405 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene
406 willConnectToSession:session
407 options:options]);
408 OCMVerify(times(0), [mockAppLifecycleDelegate sceneWillConnectFallback:options]);
409 OCMVerify(times(0), [mockEngine sendDeepLinkToFramework:[OCMArg any]
410 completionHandler:[OCMArg any]]);
411 }
412
413 - (void)testSceneWillConnectToSessionOptionsHandledByApplicationPlugin {
414 FlutterPluginSceneLifeCycleDelegate* delegate =
415 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
416
417 id mocks = [self mocksForEvents];
418 id mockEngine = mocks[@"mockEngine"];
419 id mockScene = mocks[@"mockScene"];
420 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
421 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
422 OCMStub([mockLifecycleDelegate scene:[OCMArg any]
423 willConnectToSession:[OCMArg any]
424 options:[OCMArg any]])
425 .andReturn(NO);
426 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
427 OCMStub([mockAppLifecycleDelegate sceneWillConnectFallback:[OCMArg any]]).andReturn(YES);
428
429 id session = OCMClassMock([UISceneSession class]);
430 id options = OCMClassMock([UISceneConnectionOptions class]);
431
432 [delegate addFlutterManagedEngine:mockEngine];
433 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
434
435 [delegate scene:mockScene willConnectToSession:session options:options];
436 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene
437 willConnectToSession:session
438 options:options]);
439 OCMVerify(times(1), [mockAppLifecycleDelegate sceneWillConnectFallback:options]);
440 OCMVerify(times(0), [mockEngine sendDeepLinkToFramework:[OCMArg any]
441 completionHandler:[OCMArg any]]);
442 }
443
444 - (void)testSceneWillConnectToSessionOptionsHandledByApplicationPluginMultipleEngines {
445 FlutterPluginSceneLifeCycleDelegate* delegate =
446 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
447
448 id mocks = [self mocksForEvents];
449 id mockEngine = mocks[@"mockEngine"];
450 id mockScene = mocks[@"mockScene"];
451 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
452 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
453 OCMStub([mockLifecycleDelegate scene:[OCMArg any]
454 willConnectToSession:[OCMArg any]
455 options:[OCMArg any]])
456 .andReturn(NO);
457
458 id mocks2 = [self mocksForEvents];
459 id mockEngine2 = mocks2[@"mockEngine"];
460 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate2 =
461 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks2[@"mockLifecycleDelegate"];
462 OCMStub([mockLifecycleDelegate2 scene:[OCMArg any]
463 willConnectToSession:[OCMArg any]
464 options:[OCMArg any]])
465 .andReturn(NO);
466
467 id mockAppLifecycleDelegate = mocks2[@"mockAppLifecycleDelegate"];
468 OCMStub([mockAppLifecycleDelegate sceneWillConnectFallback:[OCMArg any]]).andReturn(YES);
469 id session = OCMClassMock([UISceneSession class]);
470 id options = OCMClassMock([UISceneConnectionOptions class]);
471
472 [delegate addFlutterManagedEngine:mockEngine];
473 [delegate addFlutterManagedEngine:mockEngine2];
474 XCTAssertEqual(delegate.flutterManagedEngines.count, 2.0);
475
476 [delegate scene:mockScene willConnectToSession:session options:options];
477 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene
478 willConnectToSession:session
479 options:options]);
480 OCMVerify(times(1), [mockLifecycleDelegate2 scene:mockScene
481 willConnectToSession:session
482 options:nil]);
483 OCMVerify(times(1), [mockAppLifecycleDelegate sceneWillConnectFallback:options]);
484 OCMVerify(times(0), [mockEngine sendDeepLinkToFramework:[OCMArg any]
485 completionHandler:[OCMArg any]]);
486 }
487
488 - (void)testSceneWillConnectToSessionOptionsHandledByUniversalLinks {
489 FlutterPluginSceneLifeCycleDelegate* delegate =
490 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
491
492 id mocks = [self mocksForEvents];
493 id mockEngine = mocks[@"mockEngine"];
494 id mockScene = mocks[@"mockScene"];
495 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
496 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
497 OCMStub([mockLifecycleDelegate scene:[OCMArg any]
498 willConnectToSession:[OCMArg any]
499 options:[OCMArg any]])
500 .andReturn(NO);
501
502 id session = OCMClassMock([UISceneSession class]);
503 id options = OCMClassMock([UISceneConnectionOptions class]);
504 id userActivity = OCMClassMock([NSUserActivity class]);
505 id flutterApp = OCMClassMock([FlutterSharedApplication class]);
506 NSURL* url = [NSURL URLWithString:@"example.com"];
507 OCMStub([userActivity webpageURL]).andReturn(url);
508 NSSet<NSUserActivity*>* userActivities = [NSSet setWithObjects:userActivity, nil];
509 OCMStub([options userActivities]).andReturn(userActivities);
510 OCMStub([flutterApp isFlutterDeepLinkingEnabled]).andReturn(YES);
511
512 [delegate addFlutterManagedEngine:mockEngine];
513 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
514
515 [delegate scene:mockScene willConnectToSession:session options:options];
516 OCMVerify(times(1), [mockEngine sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
517 }
518
519 - (void)testSceneWillConnectToSessionOptionsHandledByDeepLinks {
520 FlutterPluginSceneLifeCycleDelegate* delegate =
521 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
522
523 id mocks = [self mocksForEvents];
524 id mockEngine = mocks[@"mockEngine"];
525 id mockScene = mocks[@"mockScene"];
526 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
527 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
528 OCMStub([mockLifecycleDelegate scene:[OCMArg any]
529 willConnectToSession:[OCMArg any]
530 options:[OCMArg any]])
531 .andReturn(NO);
532
533 id session = OCMClassMock([UISceneSession class]);
534 id options = OCMClassMock([UISceneConnectionOptions class]);
535 id flutterApp = OCMClassMock([FlutterSharedApplication class]);
536 NSURL* url = [NSURL URLWithString:@"example.com"];
537 OCMStub([flutterApp isFlutterDeepLinkingEnabled]).andReturn(YES);
538 id urlContext = OCMClassMock([UIOpenURLContext class]);
539 OCMStub([urlContext URL]).andReturn(url);
540 NSSet<UIOpenURLContext*>* urlContexts = [NSSet setWithObjects:urlContext, nil];
541 OCMStub([options URLContexts]).andReturn(urlContexts);
542
543 [delegate addFlutterManagedEngine:mockEngine];
544 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
545
546 [delegate scene:mockScene willConnectToSession:session options:options];
547 OCMVerify(times(1), [mockEngine sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
548 }
549
550 - (void)testSceneWillConnectToSessionOptionsHandledByNoPlugin {
551 FlutterPluginSceneLifeCycleDelegate* delegate =
552 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
553
554 id mocks = [self mocksForEvents];
555 id mockEngine = mocks[@"mockEngine"];
556 id mockScene = mocks[@"mockScene"];
557 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
558 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
559 OCMStub([mockLifecycleDelegate scene:[OCMArg any]
560 willConnectToSession:[OCMArg any]
561 options:[OCMArg any]])
562 .andReturn(NO);
563
564 id session = OCMClassMock([UISceneSession class]);
565 id options = OCMClassMock([UISceneConnectionOptions class]);
566 id userActivity = OCMClassMock([NSUserActivity class]);
567 id flutterApp = OCMClassMock([FlutterSharedApplication class]);
568 NSURL* url = [NSURL URLWithString:@"example.com"];
569 OCMStub([userActivity webpageURL]).andReturn(url);
570 NSSet<NSUserActivity*>* userActivities = [NSSet setWithObjects:userActivity, nil];
571 OCMStub([options userActivities]).andReturn(userActivities);
572 OCMStub([flutterApp isFlutterDeepLinkingEnabled]).andReturn(NO);
573
574 [delegate addFlutterManagedEngine:mockEngine];
575 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
576
577 [delegate scene:mockScene willConnectToSession:session options:options];
578 OCMVerify(times(0), [mockEngine sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
579 }
580
581 - (void)testSceneWillConnectToSessionAddsEngineFromRootViewController {
582 FlutterPluginSceneLifeCycleDelegate* delegate =
583 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
584 FlutterPluginSceneLifeCycleDelegate* mockDelegate = OCMPartialMock(delegate);
585
586 id mockScene = OCMClassMock([UIWindowScene class]);
587 id mockWindow = OCMClassMock([UIWindow class]);
588 id mockViewController = OCMClassMock([FlutterViewController class]);
589 id mockEngine = OCMClassMock([FlutterEngine class]);
590 id mockSceneDelegate = OCMProtocolMock(@protocol(UIWindowSceneDelegate));
591
592 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
593 OCMStub([mockSceneDelegate window]).andReturn(mockWindow);
594 OCMStub([mockWindow rootViewController]).andReturn(mockViewController);
595 OCMStub([mockViewController engine]).andReturn(mockEngine);
596
597 id session = OCMClassMock([UISceneSession class]);
598 id options = OCMClassMock([UISceneConnectionOptions class]);
599
600 [mockDelegate scene:mockScene willConnectToSession:session options:options];
601
602 OCMVerify(times(1), [mockDelegate addFlutterManagedEngine:mockEngine]);
603 OCMVerify(times(1), [mockDelegate scene:mockScene
604 willConnectToSession:session
605 flutterEngine:mockEngine
606 options:options]);
607 }
608
609 - (void)testSceneWillConnectToSessionAddsEngineFromRootViewControllerAndNotNotification {
610 FlutterPluginSceneLifeCycleDelegate* delegate =
611 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
612 FlutterPluginSceneLifeCycleDelegate* mockDelegate = OCMPartialMock(delegate);
613
614 id mockScene = OCMClassMock([UIWindowScene class]);
615 id mockWindow = OCMClassMock([UIWindow class]);
616 id mockViewController = OCMClassMock([FlutterViewController class]);
617 id mockEngine = OCMClassMock([FlutterEngine class]);
618 id mockSceneDelegate = OCMProtocolMock(@protocol(UIWindowSceneDelegate));
619
620 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
621 OCMStub([mockSceneDelegate window]).andReturn(mockWindow);
622 OCMStub([mockWindow rootViewController]).andReturn(mockViewController);
623 OCMStub([mockViewController engine]).andReturn(mockEngine);
624
625 id session = OCMClassMock([UISceneSession class]);
626 id options = OCMClassMock([UISceneConnectionOptions class]);
627
628 [mockDelegate scene:mockScene willConnectToSession:session options:options];
629 [mockDelegate engine:mockEngine receivedConnectNotificationFor:mockScene];
630
631 OCMVerify(times(2), [mockDelegate addFlutterManagedEngine:mockEngine]);
632 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
633 OCMVerify(times(1), [mockDelegate scene:mockScene
634 willConnectToSession:session
635 flutterEngine:mockEngine
636 options:options]);
637 }
638
639 - (void)testSceneDidDisconnect {
640 FlutterPluginSceneLifeCycleDelegate* delegate =
641 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
642
643 id mocks = [self mocksForEvents];
644 id mockEngine = mocks[@"mockEngine"];
645 id mockScene = mocks[@"mockScene"];
646 id mockLifecycleDelegate = mocks[@"mockLifecycleDelegate"];
647
648 [delegate addFlutterManagedEngine:mockEngine];
649 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
650
651 [delegate sceneDidDisconnect:mockScene];
652 OCMVerify(times(1), [mockLifecycleDelegate sceneDidDisconnect:mockScene]);
653 }
654
655 - (void)testSceneWillEnterForeground {
656 FlutterPluginSceneLifeCycleDelegate* delegate =
657 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
658
659 id mocks = [self mocksForEvents];
660 id mockEngine = mocks[@"mockEngine"];
661 id mockScene = mocks[@"mockScene"];
662 id mockLifecycleDelegate = mocks[@"mockLifecycleDelegate"];
663 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
664
665 [delegate addFlutterManagedEngine:mockEngine];
666 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
667
668 [delegate sceneWillEnterForeground:mockScene];
669 OCMVerify(times(1), [mockLifecycleDelegate sceneWillEnterForeground:mockScene]);
670 OCMVerify(times(1), [mockAppLifecycleDelegate sceneWillEnterForegroundFallback]);
671 }
672
673 - (void)testSceneDidBecomeActive {
674 FlutterPluginSceneLifeCycleDelegate* delegate =
675 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
676
677 id mocks = [self mocksForEvents];
678 id mockEngine = mocks[@"mockEngine"];
679 id mockScene = mocks[@"mockScene"];
680 id mockLifecycleDelegate = mocks[@"mockLifecycleDelegate"];
681 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
682
683 [delegate addFlutterManagedEngine:mockEngine];
684 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
685
686 [delegate sceneDidBecomeActive:mockScene];
687 OCMVerify(times(1), [mockLifecycleDelegate sceneDidBecomeActive:mockScene]);
688 OCMVerify(times(1), [mockAppLifecycleDelegate sceneDidBecomeActiveFallback]);
689 }
690
691 - (void)testSceneWillResignActive {
692 FlutterPluginSceneLifeCycleDelegate* delegate =
693 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
694
695 id mocks = [self mocksForEvents];
696 id mockEngine = mocks[@"mockEngine"];
697 id mockScene = mocks[@"mockScene"];
698 id mockLifecycleDelegate = mocks[@"mockLifecycleDelegate"];
699 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
700
701 [delegate addFlutterManagedEngine:mockEngine];
702 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
703
704 [delegate sceneWillResignActive:mockScene];
705 OCMVerify(times(1), [mockLifecycleDelegate sceneWillResignActive:mockScene]);
706 OCMVerify(times(1), [mockAppLifecycleDelegate sceneWillResignActiveFallback]);
707 }
708
709 - (void)testSceneDidEnterBackground {
710 FlutterPluginSceneLifeCycleDelegate* delegate =
711 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
712
713 id mocks = [self mocksForEvents];
714 id mockEngine = mocks[@"mockEngine"];
715 id mockScene = mocks[@"mockScene"];
716 id mockLifecycleDelegate = mocks[@"mockLifecycleDelegate"];
717 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
718
719 [delegate addFlutterManagedEngine:mockEngine];
720 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
721
722 [delegate sceneDidEnterBackground:mockScene];
723 OCMVerify(times(1), [mockLifecycleDelegate sceneDidEnterBackground:mockScene]);
724 OCMVerify(times(1), [mockAppLifecycleDelegate sceneDidEnterBackgroundFallback]);
725 }
726
727 - (void)testSceneOpenURLContextsHandledByScenePlugin {
728 FlutterPluginSceneLifeCycleDelegate* delegate =
729 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
730
731 id mocks = [self mocksForEvents];
732 id mockEngine = mocks[@"mockEngine"];
733 id mockScene = mocks[@"mockScene"];
734 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
735 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
736 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
737 OCMStub([mockLifecycleDelegate scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(YES);
738 OCMStub([mockAppLifecycleDelegate sceneFallbackOpenURLContexts:[OCMArg any]]).andReturn(YES);
739
740 id urlContext = OCMClassMock([UIOpenURLContext class]);
741 NSSet<UIOpenURLContext*>* urlContexts = [NSSet setWithObjects:urlContext, nil];
742
743 [delegate addFlutterManagedEngine:mockEngine];
744 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
745
746 [delegate scene:mockScene openURLContexts:urlContexts];
747 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene openURLContexts:urlContexts]);
748 OCMVerify(times(0), [mockAppLifecycleDelegate sceneFallbackOpenURLContexts:urlContexts]);
749 }
750
751 - (void)testSceneOpenURLContextsHandledByApplicationPlugin {
752 FlutterPluginSceneLifeCycleDelegate* delegate =
753 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
754
755 id mocks = [self mocksForEvents];
756 id mockEngine = mocks[@"mockEngine"];
757 id mockScene = mocks[@"mockScene"];
758 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
759 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
760 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
761 OCMStub([mockLifecycleDelegate scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(NO);
762 OCMStub([mockAppLifecycleDelegate sceneFallbackOpenURLContexts:[OCMArg any]]).andReturn(YES);
763
764 id urlContext = OCMClassMock([UIOpenURLContext class]);
765 NSSet<UIOpenURLContext*>* urlContexts = [NSSet setWithObjects:urlContext, nil];
766
767 [delegate addFlutterManagedEngine:mockEngine];
768 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
769
770 [delegate scene:mockScene openURLContexts:urlContexts];
771 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene openURLContexts:urlContexts]);
772 OCMVerify(times(1), [mockAppLifecycleDelegate sceneFallbackOpenURLContexts:urlContexts]);
773 }
774
775 - (void)testSceneOpenURLContextsHandledByDeeplink {
776 FlutterPluginSceneLifeCycleDelegate* delegate =
777 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
778
779 id mocks = [self mocksForEvents];
780 id mockEngine = mocks[@"mockEngine"];
781 id mockScene = mocks[@"mockScene"];
782 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
783 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
784 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
785 OCMStub([mockLifecycleDelegate scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(NO);
786 OCMStub([mockAppLifecycleDelegate sceneFallbackOpenURLContexts:[OCMArg any]]).andReturn(NO);
787 id flutterApp = OCMClassMock([FlutterSharedApplication class]);
788 OCMStub([flutterApp isFlutterDeepLinkingEnabled]).andReturn(YES);
789
790 NSURL* url = [NSURL URLWithString:@"example.com"];
791 id urlContext = OCMClassMock([UIOpenURLContext class]);
792 OCMStub([urlContext URL]).andReturn(url);
793 NSSet<UIOpenURLContext*>* urlContexts = [NSSet setWithObjects:urlContext, nil];
794
795 [delegate addFlutterManagedEngine:mockEngine];
796 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
797
798 [delegate scene:mockScene openURLContexts:urlContexts];
799 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene openURLContexts:urlContexts]);
800 OCMVerify(times(1), [mockAppLifecycleDelegate sceneFallbackOpenURLContexts:urlContexts]);
801 OCMVerify(times(1), [mockEngine sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
802 }
803
804 - (void)testSceneOpenURLContextsHandledByNoPlugin {
805 FlutterPluginSceneLifeCycleDelegate* delegate =
806 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
807
808 id mocks = [self mocksForEvents];
809 id mockEngine = mocks[@"mockEngine"];
810 id mockScene = mocks[@"mockScene"];
811 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
812 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
813 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
814 OCMStub([mockLifecycleDelegate scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(NO);
815 OCMStub([mockAppLifecycleDelegate sceneFallbackOpenURLContexts:[OCMArg any]]).andReturn(NO);
816 id flutterApp = OCMClassMock([FlutterSharedApplication class]);
817 OCMStub([flutterApp isFlutterDeepLinkingEnabled]).andReturn(NO);
818
819 id urlContext = OCMClassMock([UIOpenURLContext class]);
820 NSSet<UIOpenURLContext*>* urlContexts = [NSSet setWithObjects:urlContext, nil];
821
822 [delegate addFlutterManagedEngine:mockEngine];
823 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
824
825 [delegate scene:mockScene openURLContexts:urlContexts];
826 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene openURLContexts:urlContexts]);
827 OCMVerify(times(1), [mockAppLifecycleDelegate sceneFallbackOpenURLContexts:urlContexts]);
828 }
829
830 - (void)testSceneOpenURLContextsWithMultipleEnginesSomeHandledByPlugin {
831 FlutterPluginSceneLifeCycleDelegate* delegate =
832 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
833
834 id mocks = [self mocksForEvents];
835 id mockScene = mocks[@"mockScene"];
836 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
837
838 id mockEngine1 = OCMClassMock([FlutterEngine class]);
839 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate1 =
840 OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
841 OCMStub([mockEngine1 sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate1);
842 OCMStub([mockLifecycleDelegate1 scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(YES);
843
844 id mockEngine2 = OCMClassMock([FlutterEngine class]);
845 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate2 =
846 OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
847 OCMStub([mockEngine2 sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate2);
848 OCMStub([mockLifecycleDelegate2 scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(NO);
849
850 id flutterApp = OCMClassMock([FlutterSharedApplication class]);
851 OCMStub([flutterApp isFlutterDeepLinkingEnabled]).andReturn(YES);
852
853 NSURL* url = [NSURL URLWithString:@"example.com"];
854 id urlContext = OCMClassMock([UIOpenURLContext class]);
855 OCMStub([urlContext URL]).andReturn(url);
856 NSSet<UIOpenURLContext*>* urlContexts = [NSSet setWithObjects:urlContext, nil];
857
858 [delegate addFlutterManagedEngine:mockEngine1];
859 [delegate addFlutterManagedEngine:mockEngine2];
860
861 [delegate scene:mockScene openURLContexts:urlContexts];
862
863 OCMVerify(times(1), [mockLifecycleDelegate1 scene:mockScene openURLContexts:urlContexts]);
864 OCMVerify(times(1), [mockLifecycleDelegate2 scene:mockScene openURLContexts:urlContexts]);
865 OCMVerify(times(0), [mockAppLifecycleDelegate sceneFallbackOpenURLContexts:urlContexts]);
866 OCMVerify(times(0), [mockEngine1 sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
867 OCMVerify(times(1), [mockEngine2 sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
868 }
869
870 - (void)testSceneOpenURLContextsWithMultipleEnginesHandledByApplication {
871 FlutterPluginSceneLifeCycleDelegate* delegate =
872 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
873
874 id mocks = [self mocksForEvents];
875 id mockScene = mocks[@"mockScene"];
876 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
877 OCMStub([mockAppLifecycleDelegate sceneFallbackOpenURLContexts:[OCMArg any]]).andReturn(YES);
878
879 id mockEngine1 = OCMClassMock([FlutterEngine class]);
880 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate1 =
881 OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
882 OCMStub([mockEngine1 sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate1);
883 OCMStub([mockLifecycleDelegate1 scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(NO);
884
885 id mockEngine2 = OCMClassMock([FlutterEngine class]);
886 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate2 =
887 OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
888 OCMStub([mockEngine2 sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate2);
889 OCMStub([mockLifecycleDelegate2 scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(NO);
890
891 id flutterApp = OCMClassMock([FlutterSharedApplication class]);
892 OCMStub([flutterApp isFlutterDeepLinkingEnabled]).andReturn(YES);
893
894 NSURL* url = [NSURL URLWithString:@"example.com"];
895 id urlContext = OCMClassMock([UIOpenURLContext class]);
896 OCMStub([urlContext URL]).andReturn(url);
897 NSSet<UIOpenURLContext*>* urlContexts = [NSSet setWithObjects:urlContext, nil];
898
899 [delegate addFlutterManagedEngine:mockEngine1];
900 [delegate addFlutterManagedEngine:mockEngine2];
901
902 [delegate scene:mockScene openURLContexts:urlContexts];
903
904 OCMVerify(times(1), [mockLifecycleDelegate1 scene:mockScene openURLContexts:urlContexts]);
905 OCMVerify(times(1), [mockLifecycleDelegate2 scene:mockScene openURLContexts:urlContexts]);
906 OCMVerify(times(1), [mockAppLifecycleDelegate sceneFallbackOpenURLContexts:urlContexts]);
907 OCMVerify(times(0), [mockEngine1 sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
908 OCMVerify(times(0), [mockEngine2 sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
909 }
910
911 - (void)testSceneOpenURLContextsWithMultipleEnginesHandledByDeeplink {
912 FlutterPluginSceneLifeCycleDelegate* delegate =
913 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
914
915 id mocks = [self mocksForEvents];
916 id mockScene = mocks[@"mockScene"];
917 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
918 OCMStub([mockAppLifecycleDelegate sceneFallbackOpenURLContexts:[OCMArg any]]).andReturn(NO);
919
920 id mockEngine1 = OCMClassMock([FlutterEngine class]);
921 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate1 =
922 OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
923 OCMStub([mockEngine1 sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate1);
924 OCMStub([mockLifecycleDelegate1 scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(NO);
925
926 id mockEngine2 = OCMClassMock([FlutterEngine class]);
927 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate2 =
928 OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
929 OCMStub([mockEngine2 sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate2);
930 OCMStub([mockLifecycleDelegate2 scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(NO);
931
932 id flutterApp = OCMClassMock([FlutterSharedApplication class]);
933 OCMStub([flutterApp isFlutterDeepLinkingEnabled]).andReturn(YES);
934
935 NSURL* url = [NSURL URLWithString:@"example.com"];
936 id urlContext = OCMClassMock([UIOpenURLContext class]);
937 OCMStub([urlContext URL]).andReturn(url);
938 NSSet<UIOpenURLContext*>* urlContexts = [NSSet setWithObjects:urlContext, nil];
939
940 [delegate addFlutterManagedEngine:mockEngine1];
941 [delegate addFlutterManagedEngine:mockEngine2];
942
943 [delegate scene:mockScene openURLContexts:urlContexts];
944
945 OCMVerify(times(1), [mockLifecycleDelegate1 scene:mockScene openURLContexts:urlContexts]);
946 OCMVerify(times(1), [mockLifecycleDelegate2 scene:mockScene openURLContexts:urlContexts]);
947 OCMVerify(times(1), [mockAppLifecycleDelegate sceneFallbackOpenURLContexts:urlContexts]);
948 OCMVerify(times(1), [mockEngine1 sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
949 OCMVerify(times(1), [mockEngine2 sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
950 }
951
952 - (void)testSceneContinueUserActivityHandledByScenePlugin {
953 FlutterPluginSceneLifeCycleDelegate* delegate =
954 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
955
956 id mocks = [self mocksForEvents];
957 id mockEngine = mocks[@"mockEngine"];
958 id mockScene = mocks[@"mockScene"];
959 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
960 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
961 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
962 OCMStub([mockLifecycleDelegate scene:[OCMArg any] continueUserActivity:[OCMArg any]])
963 .andReturn(YES);
964 OCMStub([mockAppLifecycleDelegate sceneFallbackContinueUserActivity:[OCMArg any]]).andReturn(YES);
965
966 id userActivity = OCMClassMock([NSUserActivity class]);
967
968 [delegate addFlutterManagedEngine:mockEngine];
969 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
970
971 [delegate scene:mockScene continueUserActivity:userActivity];
972 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene continueUserActivity:userActivity]);
973 OCMVerify(times(0), [mockAppLifecycleDelegate sceneFallbackContinueUserActivity:userActivity]);
974 }
975
976 - (void)testSceneContinueUserActivityHandledByApplicationPlugin {
977 FlutterPluginSceneLifeCycleDelegate* delegate =
978 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
979
980 id mocks = [self mocksForEvents];
981 id mockEngine = mocks[@"mockEngine"];
982 id mockScene = mocks[@"mockScene"];
983 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
984 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
985 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
986 OCMStub([mockLifecycleDelegate scene:[OCMArg any] continueUserActivity:[OCMArg any]])
987 .andReturn(NO);
988 OCMStub([mockAppLifecycleDelegate sceneFallbackContinueUserActivity:[OCMArg any]]).andReturn(YES);
989
990 id userActivity = OCMClassMock([NSUserActivity class]);
991
992 [delegate addFlutterManagedEngine:mockEngine];
993 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
994
995 [delegate scene:mockScene continueUserActivity:userActivity];
996 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene continueUserActivity:userActivity]);
997 OCMVerify(times(1), [mockAppLifecycleDelegate sceneFallbackContinueUserActivity:userActivity]);
998 }
999
1000 - (void)testSceneContinueUserActivityHandledByUniversalLinks {
1001 FlutterPluginSceneLifeCycleDelegate* delegate =
1002 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
1003
1004 id mocks = [self mocksForEvents];
1005 id mockEngine = mocks[@"mockEngine"];
1006 id mockScene = mocks[@"mockScene"];
1007 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
1008 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
1009 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
1010 OCMStub([mockLifecycleDelegate scene:[OCMArg any] continueUserActivity:[OCMArg any]])
1011 .andReturn(NO);
1012 OCMStub([mockAppLifecycleDelegate sceneFallbackContinueUserActivity:[OCMArg any]]).andReturn(NO);
1013 id flutterApp = OCMClassMock([FlutterSharedApplication class]);
1014 OCMStub([flutterApp isFlutterDeepLinkingEnabled]).andReturn(YES);
1015
1016 NSURL* url = [NSURL URLWithString:@"example.com"];
1017 id userActivity = OCMClassMock([NSUserActivity class]);
1018 OCMStub([userActivity webpageURL]).andReturn(url);
1019
1020 [delegate addFlutterManagedEngine:mockEngine];
1021 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
1022
1023 [delegate scene:mockScene continueUserActivity:userActivity];
1024 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene continueUserActivity:userActivity]);
1025 OCMVerify(times(1), [mockAppLifecycleDelegate sceneFallbackContinueUserActivity:userActivity]);
1026 OCMVerify(times(1), [mockEngine sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
1027 }
1028
1029 - (void)testStateRestorationActivityForScene {
1030 FlutterPluginSceneLifeCycleDelegate* delegate =
1031 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
1032
1033 id mocks = [self mocksForEvents];
1034 id mockEngine = mocks[@"mockEngine"];
1035 id mockViewController = OCMClassMock([FlutterViewController class]);
1036 id mockRestorationPlugin = OCMClassMock([FlutterRestorationPlugin class]);
1037 OCMStub([mockEngine viewController]).andReturn(mockViewController);
1038 OCMStub([mockEngine restorationPlugin]).andReturn(mockRestorationPlugin);
1039
1040 NSString* restorationId = @"restorationId";
1041 NSString* mockDataString = @"mockData";
1042 NSString* configName = @"ConfigurationName";
1043 NSData* mockData = [mockDataString dataUsingEncoding:NSUTF8StringEncoding];
1044 OCMStub([mockViewController restorationIdentifier]).andReturn(restorationId);
1045 OCMStub([mockRestorationPlugin restorationData]).andReturn(mockData);
1046
1047 id mockScene = mocks[@"mockScene"];
1048 id mockSession = OCMClassMock([UISceneSession class]);
1049 id mockConfiguration = OCMClassMock([UISceneConfiguration class]);
1050 OCMStub([mockScene session]).andReturn(mockSession);
1051 OCMStub([mockSession configuration]).andReturn(mockConfiguration);
1052 OCMStub([mockConfiguration name]).andReturn(configName);
1053
1054 [delegate addFlutterManagedEngine:mockEngine];
1055 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
1056 NSUserActivity* state = [delegate stateRestorationActivityForScene:mockScene];
1057 XCTAssertEqual(state.userInfo[restorationId], mockData);
1058 XCTAssertEqual(state.activityType, configName);
1059 }
1060
1061 - (void)testSceneRestoreInteractionStateWithUserActivity {
1062 FlutterPluginSceneLifeCycleDelegate* delegate =
1063 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
1064 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
1065 id mockURL = OCMClassMock([NSURL class]);
1066 id mockFileDate = OCMClassMock([NSDate class]);
1067 int64_t mockBundleDateNum = 1;
1068
1069 OCMStub([mockBundle executableURL]).andReturn(mockURL);
1070 OCMStub([mockURL getResourceValue:[OCMArg setTo:mockFileDate]
1071 forKey:NSURLContentModificationDateKey
1072 error:[OCMArg setTo:nil]]);
1073 OCMStub([mockFileDate timeIntervalSince1970]).andReturn(mockBundleDateNum);
1074
1075 id mocks = [self mocksForEvents];
1076 id mockEngine = mocks[@"mockEngine"];
1077 id mockViewController = OCMClassMock([FlutterViewController class]);
1078 id mockRestorationPlugin = OCMClassMock([FlutterRestorationPlugin class]);
1079 OCMStub([mockEngine viewController]).andReturn(mockViewController);
1080 OCMStub([mockEngine restorationPlugin]).andReturn(mockRestorationPlugin);
1081
1082 NSString* restorationId = @"restorationId";
1083 NSString* mockDataString = @"teststring";
1084 NSNumber* mockBundleStateDateNum = @1.0;
1085 NSData* mockData = [mockDataString dataUsingEncoding:NSUTF8StringEncoding];
1086 OCMStub([mockViewController restorationIdentifier]).andReturn(restorationId);
1087 OCMStub([mockRestorationPlugin restorationData]).andReturn(mockData);
1088
1089 id mockScene = mocks[@"mockScene"];
1090
1091 id userActivity = OCMClassMock([NSUserActivity class]);
1092 NSDictionary* mockUserInfo = @{
1093 @"mod-date" : mockBundleStateDateNum,
1094 restorationId : mockData,
1095 };
1096 OCMStub([userActivity userInfo]).andReturn(mockUserInfo);
1097
1098 [delegate addFlutterManagedEngine:mockEngine];
1099 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
1100 [delegate scene:mockScene restoreInteractionStateWithUserActivity:userActivity];
1101 OCMVerify(times(1), [mockRestorationPlugin setRestorationData:mockData]);
1102 [mockBundle stopMocking];
1103 }
1104
1105 - (void)testSceneDoesNotRestoreInteractionStateWithUserActivity {
1106 FlutterPluginSceneLifeCycleDelegate* delegate =
1107 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
1108 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
1109 id mockURL = OCMClassMock([NSURL class]);
1110 id mockFileDate = OCMClassMock([NSDate class]);
1111 int64_t mockBundleDateNum = 1;
1112
1113 OCMStub([mockBundle executableURL]).andReturn(mockURL);
1114 OCMStub([mockURL getResourceValue:[OCMArg setTo:mockFileDate]
1115 forKey:NSURLContentModificationDateKey
1116 error:[OCMArg setTo:nil]]);
1117 OCMStub([mockFileDate timeIntervalSince1970]).andReturn(mockBundleDateNum);
1118
1119 id mocks = [self mocksForEvents];
1120 id mockEngine = mocks[@"mockEngine"];
1121 id mockViewController = OCMClassMock([FlutterViewController class]);
1122 id mockRestorationPlugin = OCMClassMock([FlutterRestorationPlugin class]);
1123 OCMStub([mockEngine viewController]).andReturn(mockViewController);
1124 OCMStub([mockEngine restorationPlugin]).andReturn(mockRestorationPlugin);
1125
1126 NSString* restorationId = @"restorationId";
1127 NSString* mockDataString = @"teststring";
1128 NSNumber* mockBundleStateDateNum = @2.0;
1129 NSData* mockData = [mockDataString dataUsingEncoding:NSUTF8StringEncoding];
1130 OCMStub([mockViewController restorationIdentifier]).andReturn(restorationId);
1131 OCMStub([mockRestorationPlugin restorationData]).andReturn(mockData);
1132
1133 id mockScene = mocks[@"mockScene"];
1134
1135 id userActivity = OCMClassMock([NSUserActivity class]);
1136 NSDictionary* mockUserInfo = @{
1137 @"mod-date" : mockBundleStateDateNum,
1138 restorationId : mockData,
1139 };
1140 OCMStub([userActivity userInfo]).andReturn(mockUserInfo);
1141
1142 [delegate addFlutterManagedEngine:mockEngine];
1143 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
1144 [delegate scene:mockScene restoreInteractionStateWithUserActivity:userActivity];
1145 OCMVerify(times(0), [mockRestorationPlugin setRestorationData:mockData]);
1146 [mockBundle stopMocking];
1147 }
1148
1149 - (void)testSceneContinueUserActivityHandledByNoPlugin {
1150 FlutterPluginSceneLifeCycleDelegate* delegate =
1151 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
1152
1153 id mocks = [self mocksForEvents];
1154 id mockEngine = mocks[@"mockEngine"];
1155 id mockScene = mocks[@"mockScene"];
1156 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
1157 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
1158 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
1159 OCMStub([mockLifecycleDelegate scene:[OCMArg any] continueUserActivity:[OCMArg any]])
1160 .andReturn(NO);
1161 OCMStub([mockAppLifecycleDelegate sceneFallbackContinueUserActivity:[OCMArg any]]).andReturn(NO);
1162 id flutterApp = OCMClassMock([FlutterSharedApplication class]);
1163 OCMStub([flutterApp isFlutterDeepLinkingEnabled]).andReturn(NO);
1164
1165 NSURL* url = [NSURL URLWithString:@"example.com"];
1166 id userActivity = OCMClassMock([NSUserActivity class]);
1167 OCMStub([userActivity webpageURL]).andReturn(url);
1168
1169 [delegate addFlutterManagedEngine:mockEngine];
1170 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
1171
1172 [delegate scene:mockScene continueUserActivity:userActivity];
1173 OCMVerify(times(1), [mockLifecycleDelegate scene:mockScene continueUserActivity:userActivity]);
1174 OCMVerify(times(1), [mockAppLifecycleDelegate sceneFallbackContinueUserActivity:userActivity]);
1175 OCMVerify(times(0), [mockEngine sendDeepLinkToFramework:url completionHandler:[OCMArg any]]);
1176 }
1177
1178 - (void)testWindowScenePerformActionForShortcutItemHandledByScenePlugin {
1179 FlutterPluginSceneLifeCycleDelegate* delegate =
1180 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
1181
1182 id mocks = [self mocksForEvents];
1183 id mockEngine = mocks[@"mockEngine"];
1184 id mockScene = mocks[@"mockScene"];
1185 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
1186 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
1187 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
1188 OCMStub([mockLifecycleDelegate windowScene:[OCMArg any]
1189 performActionForShortcutItem:[OCMArg any]
1190 completionHandler:[OCMArg any]])
1191 .andReturn(YES);
1192 OCMStub([mockAppLifecycleDelegate sceneFallbackPerformActionForShortcutItem:[OCMArg any]
1193 completionHandler:[OCMArg any]])
1194 .andReturn(YES);
1195
1196 id shortcutItem = OCMClassMock([UIApplicationShortcutItem class]);
1197 id handler = ^(BOOL succeeded) {
1198 };
1199
1200 [delegate addFlutterManagedEngine:mockEngine];
1201 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
1202
1203 [delegate windowScene:mockScene
1204 performActionForShortcutItem:shortcutItem
1205 completionHandler:handler];
1206 OCMVerify(times(1), [mockLifecycleDelegate windowScene:mockScene
1207 performActionForShortcutItem:shortcutItem
1208 completionHandler:handler]);
1209 OCMVerify(times(0),
1210 [mockAppLifecycleDelegate sceneFallbackPerformActionForShortcutItem:shortcutItem
1211 completionHandler:handler]);
1212 }
1213
1214 - (void)testWindowScenePerformActionForShortcutItemHandledByApplicationPlugin {
1215 FlutterPluginSceneLifeCycleDelegate* delegate =
1216 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
1217
1218 id mocks = [self mocksForEvents];
1219 id mockEngine = mocks[@"mockEngine"];
1220 id mockScene = mocks[@"mockScene"];
1221 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
1222 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
1223 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
1224 OCMStub([mockLifecycleDelegate windowScene:[OCMArg any]
1225 performActionForShortcutItem:[OCMArg any]
1226 completionHandler:[OCMArg any]])
1227 .andReturn(NO);
1228 OCMStub([mockAppLifecycleDelegate sceneFallbackPerformActionForShortcutItem:[OCMArg any]
1229 completionHandler:[OCMArg any]])
1230 .andReturn(YES);
1231
1232 id shortcutItem = OCMClassMock([UIApplicationShortcutItem class]);
1233 id handler = ^(BOOL succeeded) {
1234 };
1235
1236 [delegate addFlutterManagedEngine:mockEngine];
1237 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
1238
1239 [delegate windowScene:mockScene
1240 performActionForShortcutItem:shortcutItem
1241 completionHandler:handler];
1242 OCMVerify(times(1), [mockLifecycleDelegate windowScene:mockScene
1243 performActionForShortcutItem:shortcutItem
1244 completionHandler:handler]);
1245 OCMVerify(times(1),
1246 [mockAppLifecycleDelegate sceneFallbackPerformActionForShortcutItem:shortcutItem
1247 completionHandler:handler]);
1248 }
1249
1250 - (void)testWindowScenePerformActionForShortcutItemHandledByNoPlugin {
1251 FlutterPluginSceneLifeCycleDelegate* delegate =
1252 [[FlutterPluginSceneLifeCycleDelegate alloc] init];
1253
1254 id mocks = [self mocksForEvents];
1255 id mockEngine = mocks[@"mockEngine"];
1256 id mockScene = mocks[@"mockScene"];
1257 FlutterEnginePluginSceneLifeCycleDelegate* mockLifecycleDelegate =
1258 (FlutterEnginePluginSceneLifeCycleDelegate*)mocks[@"mockLifecycleDelegate"];
1259 id mockAppLifecycleDelegate = mocks[@"mockAppLifecycleDelegate"];
1260 OCMStub([mockLifecycleDelegate windowScene:[OCMArg any]
1261 performActionForShortcutItem:[OCMArg any]
1262 completionHandler:[OCMArg any]])
1263 .andReturn(NO);
1264 OCMStub([mockAppLifecycleDelegate sceneFallbackPerformActionForShortcutItem:[OCMArg any]
1265 completionHandler:[OCMArg any]])
1266 .andReturn(NO);
1267
1268 id shortcutItem = OCMClassMock([UIApplicationShortcutItem class]);
1269 id handler = ^(BOOL succeeded) {
1270 };
1271
1272 [delegate addFlutterManagedEngine:mockEngine];
1273 XCTAssertEqual(delegate.flutterManagedEngines.count, 1.0);
1274
1275 [delegate windowScene:mockScene
1276 performActionForShortcutItem:shortcutItem
1277 completionHandler:handler];
1278 OCMVerify(times(1), [mockLifecycleDelegate windowScene:mockScene
1279 performActionForShortcutItem:shortcutItem
1280 completionHandler:handler]);
1281 OCMVerify(times(1),
1282 [mockAppLifecycleDelegate sceneFallbackPerformActionForShortcutItem:shortcutItem
1283 completionHandler:handler]);
1284 }
1285
1286 - (NSDictionary*)mocksForEvents {
1287 id mockEngine = OCMClassMock([FlutterEngine class]);
1288 id mockLifecycleDelegate = OCMClassMock([FlutterEnginePluginSceneLifeCycleDelegate class]);
1289 OCMStub([mockEngine sceneLifeCycleDelegate]).andReturn(mockLifecycleDelegate);
1290
1291 id mockApplication = OCMClassMock([UIApplication class]);
1292 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1293
1294 id mockAppDelegate = OCMClassMock([FlutterAppDelegate class]);
1295 OCMStub([mockApplication delegate]).andReturn(mockAppDelegate);
1296
1297 id mockAppLifecycleDelegate = OCMClassMock([FlutterPluginAppLifeCycleDelegate class]);
1298 OCMStub([mockAppDelegate lifeCycleDelegate]).andReturn(mockAppLifecycleDelegate);
1299
1300 id mockScene = OCMClassMock([UIWindowScene class]);
1301
1302 return @{
1303 @"mockEngine" : mockEngine,
1304 @"mockScene" : mockScene,
1305 @"mockLifecycleDelegate" : mockLifecycleDelegate,
1306 @"mockAppLifecycleDelegate" : mockAppLifecycleDelegate,
1307 };
1308 }
1309
1310 #pragma mark - FlutterEnginePluginSceneLifeCycleDelegate
1311
1312 - (void)testEngineSceneWillConnectToSession {
1313 FlutterEnginePluginSceneLifeCycleDelegate* delegate =
1314 [[FlutterEnginePluginSceneLifeCycleDelegate alloc] init];
1315
1316 NSObject<FlutterSceneLifeCycleDelegate>* mockPlugin =
1317 OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1318 OCMStub([mockPlugin scene:[OCMArg any] willConnectToSession:[OCMArg any] options:[OCMArg any]])
1319 .andReturn(YES);
1320 NSObject<FlutterSceneLifeCycleDelegate>* mockPlugin2 =
1321 OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1322 OCMStub([mockPlugin2 scene:[OCMArg any] willConnectToSession:[OCMArg any] options:[OCMArg any]])
1323 .andReturn(YES);
1324
1325 [delegate addDelegate:mockPlugin];
1326 [delegate addDelegate:mockPlugin2];
1327
1328 id mockScene = OCMClassMock([UIWindowScene class]);
1329 id mockSession = OCMClassMock([UISceneSession class]);
1330 id mockOptions = OCMClassMock([UISceneConnectionOptions class]);
1331
1332 XCTAssertTrue([delegate scene:mockScene willConnectToSession:mockSession options:mockOptions]);
1333 OCMVerify(times(1), [mockPlugin scene:mockScene
1334 willConnectToSession:mockSession
1335 options:mockOptions]);
1336 OCMVerify(times(1), [mockPlugin2 scene:mockScene willConnectToSession:mockSession options:nil]);
1337 }
1338
1339 - (void)testEngineSceneDidDisconnect {
1340 FlutterEnginePluginSceneLifeCycleDelegate* delegate =
1341 [[FlutterEnginePluginSceneLifeCycleDelegate alloc] init];
1342
1343 id mockPlugin = OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1344 [delegate addDelegate:mockPlugin];
1345 id mockScene = OCMClassMock([UIWindowScene class]);
1346 [delegate sceneDidDisconnect:mockScene];
1347 OCMVerify([mockPlugin sceneDidDisconnect:mockScene]);
1348 }
1349
1350 - (void)testEngineSceneWillEnterForeground {
1351 FlutterEnginePluginSceneLifeCycleDelegate* delegate =
1352 [[FlutterEnginePluginSceneLifeCycleDelegate alloc] init];
1353
1354 id mockPlugin = OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1355 [delegate addDelegate:mockPlugin];
1356 id mockScene = OCMClassMock([UIWindowScene class]);
1357 [delegate sceneWillEnterForeground:mockScene];
1358 OCMVerify([mockPlugin sceneWillEnterForeground:mockScene]);
1359 }
1360
1361 - (void)testEngineSceneDidBecomeActive {
1362 FlutterEnginePluginSceneLifeCycleDelegate* delegate =
1363 [[FlutterEnginePluginSceneLifeCycleDelegate alloc] init];
1364
1365 id mockPlugin = OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1366 [delegate addDelegate:mockPlugin];
1367 id mockScene = OCMClassMock([UIWindowScene class]);
1368 [delegate sceneDidBecomeActive:mockScene];
1369 OCMVerify([mockPlugin sceneDidBecomeActive:mockScene]);
1370 }
1371
1372 - (void)testEngineSceneWillResignActive {
1373 FlutterEnginePluginSceneLifeCycleDelegate* delegate =
1374 [[FlutterEnginePluginSceneLifeCycleDelegate alloc] init];
1375
1376 id mockPlugin = OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1377 [delegate addDelegate:mockPlugin];
1378 id mockScene = OCMClassMock([UIWindowScene class]);
1379 [delegate sceneWillResignActive:mockScene];
1380 OCMVerify([mockPlugin sceneWillResignActive:mockScene]);
1381 }
1382
1383 - (void)testEngineSceneDidEnterBackground {
1384 FlutterEnginePluginSceneLifeCycleDelegate* delegate =
1385 [[FlutterEnginePluginSceneLifeCycleDelegate alloc] init];
1386
1387 id mockPlugin = OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1388 [delegate addDelegate:mockPlugin];
1389 id mockScene = OCMClassMock([UIWindowScene class]);
1390 [delegate sceneDidEnterBackground:mockScene];
1391 OCMVerify([mockPlugin sceneDidEnterBackground:mockScene]);
1392 }
1393
1394 - (void)testEngineSceneOpenURLContexts {
1395 FlutterEnginePluginSceneLifeCycleDelegate* delegate =
1396 [[FlutterEnginePluginSceneLifeCycleDelegate alloc] init];
1397
1398 NSObject<FlutterSceneLifeCycleDelegate>* mockPlugin =
1399 OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1400 OCMStub([mockPlugin scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(YES);
1401 NSObject<FlutterSceneLifeCycleDelegate>* mockPlugin2 =
1402 OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1403 OCMStub([mockPlugin2 scene:[OCMArg any] openURLContexts:[OCMArg any]]).andReturn(YES);
1404
1405 [delegate addDelegate:mockPlugin];
1406 [delegate addDelegate:mockPlugin2];
1407
1408 id mockScene = OCMClassMock([UIWindowScene class]);
1409 id urlContext = OCMClassMock([UIOpenURLContext class]);
1410 NSSet<UIOpenURLContext*>* urlContexts = [NSSet setWithObjects:urlContext, nil];
1411
1412 XCTAssertTrue([delegate scene:mockScene openURLContexts:urlContexts]);
1413 OCMVerify(times(1), [mockPlugin scene:mockScene openURLContexts:urlContexts]);
1414 OCMVerify(times(0), [mockPlugin2 scene:mockScene openURLContexts:urlContexts]);
1415 }
1416
1417 - (void)testEngineSceneContinueUserActivity {
1418 FlutterEnginePluginSceneLifeCycleDelegate* delegate =
1419 [[FlutterEnginePluginSceneLifeCycleDelegate alloc] init];
1420
1421 NSObject<FlutterSceneLifeCycleDelegate>* mockPlugin =
1422 OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1423 OCMStub([mockPlugin scene:[OCMArg any] continueUserActivity:[OCMArg any]]).andReturn(YES);
1424 NSObject<FlutterSceneLifeCycleDelegate>* mockPlugin2 =
1425 OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1426 OCMStub([mockPlugin2 scene:[OCMArg any] continueUserActivity:[OCMArg any]]).andReturn(YES);
1427
1428 [delegate addDelegate:mockPlugin];
1429 [delegate addDelegate:mockPlugin2];
1430
1431 id mockScene = OCMClassMock([UIWindowScene class]);
1432 id userActivity = OCMClassMock([NSUserActivity class]);
1433
1434 XCTAssertTrue([delegate scene:mockScene continueUserActivity:userActivity]);
1435 OCMVerify(times(1), [mockPlugin scene:mockScene continueUserActivity:userActivity]);
1436 OCMVerify(times(0), [mockPlugin2 scene:mockScene continueUserActivity:userActivity]);
1437 }
1438
1439 - (void)testEngineWindowScenePerformActionForShortcutItem {
1440 FlutterEnginePluginSceneLifeCycleDelegate* delegate =
1441 [[FlutterEnginePluginSceneLifeCycleDelegate alloc] init];
1442
1443 NSObject<FlutterSceneLifeCycleDelegate>* mockPlugin =
1444 OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1445 OCMStub([mockPlugin windowScene:[OCMArg any]
1446 performActionForShortcutItem:[OCMArg any]
1447 completionHandler:[OCMArg any]])
1448 .andReturn(YES);
1449 NSObject<FlutterSceneLifeCycleDelegate>* mockPlugin2 =
1450 OCMProtocolMock(@protocol(FlutterSceneLifeCycleDelegate));
1451 OCMStub([mockPlugin2 windowScene:[OCMArg any]
1452 performActionForShortcutItem:[OCMArg any]
1453 completionHandler:[OCMArg any]])
1454 .andReturn(YES);
1455
1456 [delegate addDelegate:mockPlugin];
1457 [delegate addDelegate:mockPlugin2];
1458
1459 id mockScene = OCMClassMock([UIWindowScene class]);
1460 id shortcutItem = OCMClassMock([UIApplicationShortcutItem class]);
1461 id handler = ^(BOOL succeeded) {
1462 };
1463
1464 XCTAssertTrue([delegate windowScene:mockScene
1465 performActionForShortcutItem:shortcutItem
1466 completionHandler:handler]);
1467 OCMVerify(times(1), [mockPlugin windowScene:mockScene
1468 performActionForShortcutItem:shortcutItem
1469 completionHandler:handler]);
1470 OCMVerify(times(0), [mockPlugin2 windowScene:mockScene
1471 performActionForShortcutItem:shortcutItem
1472 completionHandler:handler]);
1473 }
1474
1475 - (void)testFlutterPluginSceneLifeCycleDelegateFromScene {
1476 id mockScene = OCMClassMock([UIWindowScene class]);
1477 id mockSceneDelegate = OCMClassMock([FlutterSwiftUIAppSceneDelegate class]);
1478 id mockSceneLifeCycleDelegate = OCMClassMock([FlutterPluginSceneLifeCycleDelegate class]);
1479
1480 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
1481 OCMStub([mockSceneDelegate sceneLifeCycleDelegate]).andReturn(mockSceneLifeCycleDelegate);
1482
1483 XCTAssertEqual([FlutterPluginSceneLifeCycleDelegate fromScene:mockScene],
1484 mockSceneLifeCycleDelegate);
1485 }
1486 @end
FlutterEngine_Internal.h
FlutterEngine_Test.h
FlutterPluginAppLifeCycleDelegate_internal.h
FlutterRestorationPlugin.h
FlutterSceneLifeCycle_Internal.h
FlutterSceneLifeCycle_Test.h
FlutterSharedApplication.h
viewController
FlutterViewController * viewController
Definition: FlutterTextInputPluginTest.mm:140
engine
id engine
Definition: FlutterTextInputPluginTest.mm:137
FlutterEnginePluginSceneLifeCycleDelegate
Definition: FlutterSceneLifeCycle.mm:511
-[FlutterEnginePluginSceneLifeCycleDelegate sceneDidBecomeActive:]
void sceneDidBecomeActive:(UIScene *scene)
Definition: FlutterSceneLifeCycle.mm:572
-[FlutterEnginePluginSceneLifeCycleDelegate sceneWillEnterForeground:]
void sceneWillEnterForeground:(UIScene *scene)
Definition: FlutterSceneLifeCycle.mm:564
-[FlutterEnginePluginSceneLifeCycleDelegate sceneWillResignActive:]
void sceneWillResignActive:(UIScene *scene)
Definition: FlutterSceneLifeCycle.mm:582
-[FlutterEnginePluginSceneLifeCycleDelegate addDelegate:]
void addDelegate:(NSObject< FlutterSceneLifeCycleDelegate > *delegate)
Definition: FlutterSceneLifeCycle.mm:523
-[FlutterEnginePluginSceneLifeCycleDelegate sceneDidEnterBackground:]
void sceneDidEnterBackground:(UIScene *scene)
Definition: FlutterSceneLifeCycle.mm:590
-[FlutterEnginePluginSceneLifeCycleDelegate sceneDidDisconnect:]
void sceneDidDisconnect:(UIScene *scene)
Definition: FlutterSceneLifeCycle.mm:554
FlutterAppDelegate
Definition: FlutterAppDelegate.h:28
FlutterEngine
Definition: FlutterEngine.h:61
FlutterPluginAppLifeCycleDelegate
Definition: FlutterPluginAppLifeCycleDelegate.h:16
FlutterPluginSceneLifeCycleDelegate
Definition: FlutterSceneLifeCycle.h:149
-[FlutterPluginSceneLifeCycleDelegate scene:openURLContexts:]
void scene:openURLContexts:(UIScene *scene,[openURLContexts] NSSet< UIOpenURLContext * > *URLContexts)
Definition: FlutterSceneLifeCycle.mm:289
-[FlutterPluginSceneLifeCycleDelegate updateFlutterManagedEnginesInScene:]
void updateFlutterManagedEnginesInScene:(UIScene *scene)
-[FlutterPluginSceneLifeCycleDelegate scene:willConnectToSession:options:]
void scene:willConnectToSession:options:(UIScene *scene,[willConnectToSession] UISceneSession *session,[options] UISceneConnectionOptions *connectionOptions)
Definition: FlutterSceneLifeCycle.mm:191
FlutterPluginSceneLifeCycleDelegate::developerManagedEngines
NSPointerArray * developerManagedEngines
Definition: FlutterSceneLifeCycle_Test.h:15
FlutterPluginSceneLifeCycleDelegate::flutterManagedEngines
NSPointerArray * flutterManagedEngines
Definition: FlutterSceneLifeCycle_Test.h:14
-[FlutterPluginSceneLifeCycleDelegate allEngines]
NSArray * allEngines()
-[FlutterPluginSceneLifeCycleDelegate sceneWillEnterForeground:]
void sceneWillEnterForeground:(UIScene *scene)
Definition: FlutterSceneLifeCycle.mm:253
-[FlutterPluginSceneLifeCycleDelegate sceneWillResignActive:]
void sceneWillResignActive:(UIScene *scene)
Definition: FlutterSceneLifeCycle.mm:271
-[FlutterPluginSceneLifeCycleDelegate sceneDidBecomeActive:]
void sceneDidBecomeActive:(UIScene *scene)
Definition: FlutterSceneLifeCycle.mm:261
-[FlutterPluginSceneLifeCycleDelegate sceneDidEnterBackground:]
void sceneDidEnterBackground:(UIScene *scene)
Definition: FlutterSceneLifeCycle.mm:279
-[FlutterPluginSceneLifeCycleDelegate sceneDidDisconnect:]
void sceneDidDisconnect:(UIScene *scene)
Definition: FlutterSceneLifeCycle.mm:243
-[FlutterPluginSceneLifeCycleDelegate windowScene:performActionForShortcutItem:completionHandler:]
void windowScene:performActionForShortcutItem:completionHandler:(UIWindowScene *windowScene,[performActionForShortcutItem] UIApplicationShortcutItem *shortcutItem,[completionHandler] void(^ completionHandler)(BOOL succeeded))
Definition: FlutterSceneLifeCycle.mm:409
-[FlutterPluginSceneLifeCycleDelegate scene:continueUserActivity:]
void scene:continueUserActivity:(UIScene *scene,[continueUserActivity] NSUserActivity *userActivity)
Definition: FlutterSceneLifeCycle.mm:323
FlutterRestorationPlugin
Definition: FlutterRestorationPlugin.h:13
FlutterSceneLifecycleTest
Definition: FlutterSceneLifeCycleTest.mm:50
FlutterSharedApplication
Definition: FlutterSharedApplication.h:13
FlutterSwiftUIAppSceneDelegate
Definition: FlutterSceneLifeCycleTest.mm:24
FlutterSwiftUIAppSceneDelegate::sceneLifeCycleDelegate
FlutterPluginSceneLifeCycleDelegate * sceneLifeCycleDelegate
Definition: FlutterSceneLifeCycleTest.mm:24
FlutterViewController
Definition: FlutterViewController.h:57
TestSceneLifecycleProvider
Definition: FlutterSceneLifeCycleTest.mm:41
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13
FlutterSceneLifeCycleDelegate-p
Definition: FlutterSceneLifeCycle.h:31
-[FlutterSceneLifeCycleEngineRegistration-p unregisterSceneLifeCycleWithFlutterEngine:]
BOOL unregisterSceneLifeCycleWithFlutterEngine:(FlutterEngine *engine)
-[FlutterSceneLifeCycleEngineRegistration-p registerSceneLifeCycleWithFlutterEngine:]
BOOL registerSceneLifeCycleWithFlutterEngine:(FlutterEngine *engine)
FlutterSceneLifeCycleProvider-p
Definition: FlutterSceneLifeCycle.h:214