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)testShellGetters {
78 XCTAssertEqual(
engine.platformTaskRunner.get(),
nullptr);
79 XCTAssertEqual(
engine.uiTaskRunner.get(),
nullptr);
80 XCTAssertEqual(
engine.rasterTaskRunner.get(),
nullptr);
83 - (void)testInfoPlist {
85 NSURL* flutterFrameworkURL =
86 [NSBundle.mainBundle.privateFrameworksURL URLByAppendingPathComponent:@"Flutter.framework"];
87 NSBundle* flutterBundle = [NSBundle bundleWithURL:flutterFrameworkURL];
88 XCTAssertEqualObjects(flutterBundle.bundleIdentifier,
@"io.flutter.flutter");
90 NSDictionary<NSString*, id>* infoDictionary = flutterBundle.infoDictionary;
93 NSError* regexError = NULL;
94 NSRegularExpression* osVersionRegex =
95 [NSRegularExpression regularExpressionWithPattern:@"((0|[1-9]\\d*)\\.)*(0|[1-9]\\d*)"
96 options:NSRegularExpressionCaseInsensitive
98 XCTAssertNil(regexError);
101 NSString* testString =
@"9";
102 NSUInteger versionMatches =
103 [osVersionRegex numberOfMatchesInString:testString
104 options:NSMatchingAnchored
105 range:NSMakeRange(0, testString.length)];
106 XCTAssertEqual(versionMatches, 1UL);
108 versionMatches = [osVersionRegex numberOfMatchesInString:testString
109 options:NSMatchingAnchored
110 range:NSMakeRange(0, testString.length)];
111 XCTAssertEqual(versionMatches, 1UL);
112 testString =
@"9.0.1";
113 versionMatches = [osVersionRegex numberOfMatchesInString:testString
114 options:NSMatchingAnchored
115 range:NSMakeRange(0, testString.length)];
116 XCTAssertEqual(versionMatches, 1UL);
117 testString =
@".0.1";
118 versionMatches = [osVersionRegex numberOfMatchesInString:testString
119 options:NSMatchingAnchored
120 range:NSMakeRange(0, testString.length)];
121 XCTAssertEqual(versionMatches, 0UL);
124 NSString* minimumOSVersion = infoDictionary[@"MinimumOSVersion"];
125 versionMatches = [osVersionRegex numberOfMatchesInString:minimumOSVersion
126 options:NSMatchingAnchored
127 range:NSMakeRange(0, minimumOSVersion.length)];
128 XCTAssertEqual(versionMatches, 1UL);
131 XCTAssertEqual(((NSString*)infoDictionary[
@"FlutterEngine"]).length, 40UL);
136 XCTAssertTrue(((NSString*)infoDictionary[
@"ClangVersion"]).length > 15UL);
139 - (void)testDeallocated {
145 XCTAssertNotNil(weakEngine);
147 XCTAssertNil(weakEngine);
150 - (void)testSendMessageBeforeRun {
154 XCTAssertThrows([
engine.binaryMessenger
156 message:[
@"bar" dataUsingEncoding:NSUTF8StringEncoding]
160 - (void)testSetMessageHandlerBeforeRun {
164 XCTAssertThrows([
engine.binaryMessenger
165 setMessageHandlerOnChannel:
@"foo"
171 - (void)testNilSetMessageHandlerBeforeRun {
175 XCTAssertNoThrow([
engine.binaryMessenger setMessageHandlerOnChannel:
@"foo"
176 binaryMessageHandler:nil]);
179 - (void)testNotifyPluginOfDealloc {
181 OCMStub([plugin detachFromEngineForRegistrar:[OCMArg any]]);
186 [registrar publish:plugin];
189 OCMVerify([plugin detachFromEngineForRegistrar:[OCMArg any]]);
192 - (void)testSetBinaryMessengerToSameBinaryMessenger {
206 - (void)testRunningInitialRouteSendsNavigationMessage {
219 NSData* encodedSetInitialRouteMethod =
221 OCMVerify([mockBinaryMessenger sendOnChannel:
@"flutter/navigation"
222 message:encodedSetInitialRouteMethod]);
225 - (void)testInitialRouteSettingsSendsNavigationMessage {
229 settings.route =
"test";
239 NSData* encodedSetInitialRouteMethod =
241 OCMVerify([mockBinaryMessenger sendOnChannel:
@"flutter/navigation"
242 message:encodedSetInitialRouteMethod]);
245 - (void)testPlatformViewsControllerRenderingMetalBackend {
253 - (void)testWaitForFirstFrameTimeout {
256 XCTestExpectation* timeoutFirstFrame = [
self expectationWithDescription:@"timeoutFirstFrame"];
259 if (timeoutFirstFrame) {
260 [timeoutFirstFrame fulfill];
263 [
self waitForExpectations:@[ timeoutFirstFrame ]];
273 XCTAssertNotNil(spawn);
276 - (void)testSetHandlerAfterRun {
278 XCTestExpectation* gotMessage = [
self expectationWithDescription:@"gotMessage"];
279 dispatch_async(dispatch_get_main_queue(), ^{
281 fml::AutoResetWaitableEvent latch;
284 fml::TaskRunner::RunNowOrPostTask(
285 engine.shell.GetTaskRunners().GetUITaskRunner(), [&latch, &
shell] {
286 flutter::Engine::Delegate& delegate = shell;
287 auto message = std::make_unique<flutter::PlatformMessage>(
"foo", nullptr);
288 delegate.OnEngineHandlePlatformMessage(std::move(message));
292 [registrar.messenger setMessageHandlerOnChannel:@"foo"
293 binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
294 [gotMessage fulfill];
297 [
self waitForExpectations:@[ gotMessage ]];
300 - (void)testThreadPrioritySetCorrectly {
301 XCTestExpectation* prioritiesSet = [
self expectationWithDescription:@"prioritiesSet"];
302 prioritiesSet.expectedFulfillmentCount = 2;
304 IMP mockSetThreadPriority =
305 imp_implementationWithBlock(^(NSThread* thread,
double threadPriority) {
306 if ([thread.name hasSuffix:
@".raster"]) {
307 XCTAssertEqual(threadPriority, 1.0);
308 [prioritiesSet fulfill];
309 }
else if ([thread.name hasSuffix:
@".io"]) {
310 XCTAssertEqual(threadPriority, 0.5);
311 [prioritiesSet fulfill];
314 Method method = class_getInstanceMethod([NSThread
class],
@selector(setThreadPriority:));
315 IMP originalSetThreadPriority = method_getImplementation(method);
316 method_setImplementation(method, mockSetThreadPriority);
320 [
self waitForExpectations:@[ prioritiesSet ]];
322 method_setImplementation(method, originalSetThreadPriority);
325 - (void)testCanEnableDisableEmbedderAPIThroughInfoPlist {
329 settings.enable_software_rendering =
true;
332 XCTAssertFalse(
engine.enableEmbedderAPI);
336 id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
337 OCMStub([mockMainBundle objectForInfoDictionaryKey:
@"FLTEnableIOSEmbedderAPI"])
340 settings.enable_software_rendering =
true;
343 XCTAssertTrue(
engine.enableEmbedderAPI);
347 - (void)testFlutterTextInputViewDidResignFirstResponderWillCallTextInputClientConnectionClosed {
352 [engine flutterTextInputView:nil didResignFirstResponderWithTextInputClient:1];
357 OCMVerify([mockBinaryMessenger sendOnChannel:
@"flutter/textinput" message:encodedMethodCall]);
360 - (void)testFlutterEngineUpdatesDisplays {
362 id mockEngine = OCMPartialMock(
engine);
366 engine.viewController = nil;
370 - (void)testLifeCycleNotificationDidEnterBackground {
374 NSNotification* sceneNotification =
375 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
378 NSNotification* applicationNotification =
379 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
382 id mockEngine = OCMPartialMock(
engine);
383 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
384 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
385 #if APPLICATION_EXTENSION_API_ONLY
386 OCMVerify(times(1), [mockEngine sceneDidEnterBackground:[OCMArg any]]);
388 OCMVerify(times(1), [mockEngine applicationDidEnterBackground:[OCMArg any]]);
390 XCTAssertTrue(
engine.isGpuDisabled);
391 bool switch_value =
false;
392 [engine
shell].GetIsGpuDisabledSyncSwitch()->Execute(
393 fml::SyncSwitch::Handlers().SetIfTrue([&] { switch_value =
true; }).SetIfFalse([&] {
394 switch_value =
false;
396 XCTAssertTrue(switch_value);
399 - (void)testLifeCycleNotificationWillEnterForeground {
403 NSNotification* sceneNotification =
404 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
407 NSNotification* applicationNotification =
408 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
411 id mockEngine = OCMPartialMock(
engine);
412 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
413 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
414 #if APPLICATION_EXTENSION_API_ONLY
415 OCMVerify(times(1), [mockEngine sceneWillEnterForeground:[OCMArg any]]);
417 OCMVerify(times(1), [mockEngine applicationWillEnterForeground:[OCMArg any]]);
419 XCTAssertFalse(
engine.isGpuDisabled);
420 bool switch_value =
true;
421 [engine
shell].GetIsGpuDisabledSyncSwitch()->Execute(
422 fml::SyncSwitch::Handlers().SetIfTrue([&] { switch_value =
true; }).SetIfFalse([&] {
423 switch_value =
false;
425 XCTAssertFalse(switch_value);
428 - (void)testSpawnsShareGpuContext {
435 XCTAssertNotNil(spawn);
436 XCTAssertTrue(
engine.platformView !=
nullptr);
438 std::shared_ptr<flutter::IOSContext> engine_context =
engine.platformView->GetIosContext();
440 XCTAssertEqual(engine_context, spawn_context);
443 - (void)testEnableSemanticsWhenFlutterViewAccessibilityDidCall {
445 engine.ensureSemanticsEnabledCalled = NO;
446 [engine flutterViewAccessibilityDidCall];
447 XCTAssertTrue(
engine.ensureSemanticsEnabledCalled);
450 - (void)testCanMergePlatformAndUIThread {
451 #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR
457 XCTAssertEqual(
engine.shell.GetTaskRunners().GetUITaskRunner(),
458 engine.shell.GetTaskRunners().GetPlatformTaskRunner());
459 #endif // defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR
462 - (void)testCanUnMergePlatformAndUIThread {
463 #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR
465 settings.merged_platform_ui_thread =
false;
470 XCTAssertNotEqual(
engine.shell.GetTaskRunners().GetUITaskRunner(),
471 engine.shell.GetTaskRunners().GetPlatformTaskRunner());
472 #endif // defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR