5 #import <Foundation/Foundation.h>
6 #import <OCMock/OCMock.h>
7 #import <XCTest/XCTest.h>
9 #import <objc/runtime.h>
11 #import "flutter/common/settings.h"
12 #include "flutter/fml/synchronization/sync_switch.h"
30 _ensureSemanticsEnabledCalled = YES;
44 @property(nonatomic, assign) BOOL failOnDealloc;
50 XCTFail(
"FakeBinaryMessageRelay should not be deallocated");
72 - (void)testInfoPlist {
74 NSURL* flutterFrameworkURL =
75 [NSBundle.mainBundle.privateFrameworksURL URLByAppendingPathComponent:@"Flutter.framework"];
76 NSBundle* flutterBundle = [NSBundle bundleWithURL:flutterFrameworkURL];
77 XCTAssertEqualObjects(flutterBundle.bundleIdentifier,
@"io.flutter.flutter");
79 NSDictionary<NSString*, id>* infoDictionary = flutterBundle.infoDictionary;
82 NSError* regexError = NULL;
83 NSRegularExpression* osVersionRegex =
84 [NSRegularExpression regularExpressionWithPattern:@"((0|[1-9]\\d*)\\.)*(0|[1-9]\\d*)"
85 options:NSRegularExpressionCaseInsensitive
87 XCTAssertNil(regexError);
90 NSString* testString =
@"9";
91 NSUInteger versionMatches =
92 [osVersionRegex numberOfMatchesInString:testString
93 options:NSMatchingAnchored
94 range:NSMakeRange(0, testString.length)];
95 XCTAssertEqual(versionMatches, 1UL);
97 versionMatches = [osVersionRegex numberOfMatchesInString:testString
98 options:NSMatchingAnchored
99 range:NSMakeRange(0, testString.length)];
100 XCTAssertEqual(versionMatches, 1UL);
101 testString =
@"9.0.1";
102 versionMatches = [osVersionRegex numberOfMatchesInString:testString
103 options:NSMatchingAnchored
104 range:NSMakeRange(0, testString.length)];
105 XCTAssertEqual(versionMatches, 1UL);
106 testString =
@".0.1";
107 versionMatches = [osVersionRegex numberOfMatchesInString:testString
108 options:NSMatchingAnchored
109 range:NSMakeRange(0, testString.length)];
110 XCTAssertEqual(versionMatches, 0UL);
113 NSString* minimumOSVersion = infoDictionary[@"MinimumOSVersion"];
114 versionMatches = [osVersionRegex numberOfMatchesInString:minimumOSVersion
115 options:NSMatchingAnchored
116 range:NSMakeRange(0, minimumOSVersion.length)];
117 XCTAssertEqual(versionMatches, 1UL);
120 XCTAssertEqual(((NSString*)infoDictionary[
@"FlutterEngine"]).length, 40UL);
125 XCTAssertTrue(((NSString*)infoDictionary[
@"ClangVersion"]).length > 15UL);
128 - (void)testDeallocated {
134 XCTAssertNotNil(weakEngine);
136 XCTAssertNil(weakEngine);
139 - (void)testSendMessageBeforeRun {
143 XCTAssertThrows([
engine.binaryMessenger
145 message:[
@"bar" dataUsingEncoding:NSUTF8StringEncoding]
149 - (void)testSetMessageHandlerBeforeRun {
153 XCTAssertThrows([
engine.binaryMessenger
154 setMessageHandlerOnChannel:
@"foo"
160 - (void)testNilSetMessageHandlerBeforeRun {
164 XCTAssertNoThrow([
engine.binaryMessenger setMessageHandlerOnChannel:
@"foo"
165 binaryMessageHandler:nil]);
168 - (void)testNotifyPluginOfDealloc {
170 OCMStub([plugin detachFromEngineForRegistrar:[OCMArg any]]);
175 [registrar publish:plugin];
178 OCMVerify([plugin detachFromEngineForRegistrar:[OCMArg any]]);
181 - (void)testSetBinaryMessengerToSameBinaryMessenger {
195 - (void)testRunningInitialRouteSendsNavigationMessage {
208 NSData* encodedSetInitialRouteMethod =
210 OCMVerify([mockBinaryMessenger sendOnChannel:
@"flutter/navigation"
211 message:encodedSetInitialRouteMethod]);
214 - (void)testInitialRouteSettingsSendsNavigationMessage {
218 settings.route =
"test";
228 NSData* encodedSetInitialRouteMethod =
230 OCMVerify([mockBinaryMessenger sendOnChannel:
@"flutter/navigation"
231 message:encodedSetInitialRouteMethod]);
234 - (void)testPlatformViewsControllerRenderingMetalBackend {
242 - (void)testPlatformViewsControllerRenderingSoftware {
244 settings.enable_software_rendering =
true;
253 - (void)testWaitForFirstFrameTimeout {
256 XCTestExpectation* timeoutFirstFrame = [
self expectationWithDescription:@"timeoutFirstFrame"];
259 if (timeoutFirstFrame) {
260 [timeoutFirstFrame fulfill];
263 [
self waitForExpectationsWithTimeout:5 handler:nil];
273 XCTAssertNotNil(spawn);
276 - (void)testDeallocNotification {
277 XCTestExpectation* deallocNotification = [
self expectationWithDescription:@"deallocNotification"];
278 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
279 id<NSObject> observer;
282 observer = [center addObserverForName:kFlutterEngineWillDealloc
284 queue:[NSOperationQueue mainQueue]
285 usingBlock:^(NSNotification* note) {
286 [deallocNotification fulfill];
289 [
self waitForExpectationsWithTimeout:1 handler:nil];
290 [center removeObserver:observer];
293 - (void)testSetHandlerAfterRun {
295 XCTestExpectation* gotMessage = [
self expectationWithDescription:@"gotMessage"];
296 dispatch_async(dispatch_get_main_queue(), ^{
298 fml::AutoResetWaitableEvent latch;
301 engine.shell.GetTaskRunners().GetUITaskRunner()->PostTask([&latch, &
shell] {
302 flutter::Engine::Delegate& delegate =
shell;
303 auto message = std::make_unique<flutter::PlatformMessage>(
"foo",
nullptr);
304 delegate.OnEngineHandlePlatformMessage(std::move(message));
308 [registrar.messenger setMessageHandlerOnChannel:@"foo"
309 binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
310 [gotMessage fulfill];
313 [
self waitForExpectationsWithTimeout:1 handler:nil];
316 - (void)testThreadPrioritySetCorrectly {
317 XCTestExpectation* prioritiesSet = [
self expectationWithDescription:@"prioritiesSet"];
318 prioritiesSet.expectedFulfillmentCount = 3;
320 IMP mockSetThreadPriority =
321 imp_implementationWithBlock(^(NSThread* thread,
double threadPriority) {
322 if ([thread.name hasSuffix:
@".ui"]) {
323 XCTAssertEqual(threadPriority, 1.0);
324 [prioritiesSet fulfill];
325 }
else if ([thread.name hasSuffix:
@".raster"]) {
326 XCTAssertEqual(threadPriority, 1.0);
327 [prioritiesSet fulfill];
328 }
else if ([thread.name hasSuffix:
@".io"]) {
329 XCTAssertEqual(threadPriority, 0.5);
330 [prioritiesSet fulfill];
333 Method method = class_getInstanceMethod([NSThread
class],
@selector(setThreadPriority:));
334 IMP originalSetThreadPriority = method_getImplementation(method);
335 method_setImplementation(method, mockSetThreadPriority);
339 [
self waitForExpectationsWithTimeout:1 handler:nil];
341 method_setImplementation(method, originalSetThreadPriority);
344 - (void)testCanEnableDisableEmbedderAPIThroughInfoPlist {
348 settings.enable_software_rendering =
true;
351 XCTAssertFalse(
engine.enableEmbedderAPI);
355 id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
356 OCMStub([mockMainBundle objectForInfoDictionaryKey:
@"FLTEnableIOSEmbedderAPI"])
359 settings.enable_software_rendering =
true;
362 XCTAssertTrue(
engine.enableEmbedderAPI);
366 - (void)testFlutterTextInputViewDidResignFirstResponderWillCallTextInputClientConnectionClosed {
371 [engine flutterTextInputView:nil didResignFirstResponderWithTextInputClient:1];
376 OCMVerify([mockBinaryMessenger sendOnChannel:
@"flutter/textinput" message:encodedMethodCall]);
379 - (void)testFlutterEngineUpdatesDisplays {
381 id mockEngine = OCMPartialMock(
engine);
385 engine.viewController = nil;
389 - (void)testLifeCycleNotificationDidEnterBackground {
393 NSNotification* sceneNotification =
394 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
397 NSNotification* applicationNotification =
398 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
401 id mockEngine = OCMPartialMock(
engine);
402 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
403 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
404 #if APPLICATION_EXTENSION_API_ONLY
405 OCMVerify(times(1), [mockEngine sceneDidEnterBackground:[OCMArg any]]);
407 OCMVerify(times(1), [mockEngine applicationDidEnterBackground:[OCMArg any]]);
409 XCTAssertTrue(
engine.isGpuDisabled);
410 bool switch_value =
false;
411 [engine
shell].GetIsGpuDisabledSyncSwitch()->Execute(
412 fml::SyncSwitch::Handlers().SetIfTrue([&] { switch_value =
true; }).SetIfFalse([&] {
413 switch_value =
false;
415 XCTAssertTrue(switch_value);
418 - (void)testLifeCycleNotificationWillEnterForeground {
422 NSNotification* sceneNotification =
423 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
426 NSNotification* applicationNotification =
427 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
430 id mockEngine = OCMPartialMock(
engine);
431 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
432 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
433 #if APPLICATION_EXTENSION_API_ONLY
434 OCMVerify(times(1), [mockEngine sceneWillEnterForeground:[OCMArg any]]);
436 OCMVerify(times(1), [mockEngine applicationWillEnterForeground:[OCMArg any]]);
438 XCTAssertFalse(
engine.isGpuDisabled);
439 bool switch_value =
true;
440 [engine
shell].GetIsGpuDisabledSyncSwitch()->Execute(
441 fml::SyncSwitch::Handlers().SetIfTrue([&] { switch_value =
true; }).SetIfFalse([&] {
442 switch_value =
false;
444 XCTAssertFalse(switch_value);
447 - (void)testSpawnsShareGpuContext {
454 XCTAssertNotNil(spawn);
455 XCTAssertTrue([
engine iosPlatformView] !=
nullptr);
456 XCTAssertTrue([spawn iosPlatformView] !=
nullptr);
457 std::shared_ptr<flutter::IOSContext> engine_context = [engine iosPlatformView]->GetIosContext();
458 std::shared_ptr<flutter::IOSContext> spawn_context = [spawn iosPlatformView]->GetIosContext();
459 XCTAssertEqual(engine_context, spawn_context);
462 XCTAssertTrue(engine_context->GetMainContext() !=
nullptr);
463 XCTAssertEqual(engine_context->GetMainContext(), spawn_context->GetMainContext());
466 - (void)testEnableSemanticsWhenFlutterViewAccessibilityDidCall {
468 engine.ensureSemanticsEnabledCalled = NO;
469 [engine flutterViewAccessibilityDidCall];
470 XCTAssertTrue(
engine.ensureSemanticsEnabledCalled);