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"
13#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
14#import "flutter/shell/platform/darwin/common/framework/Source/FlutterBinaryMessengerRelay.h"
15#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
16#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
17#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h"
18#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
19#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
30 _ensureSemanticsEnabledCalled = YES;
44@property(nonatomic, assign)
BOOL failOnDealloc;
50 XCTFail(
"FakeBinaryMessageRelay should not be deallocated");
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, 0
UL);
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);
134 XCTAssertNotNil(weakEngine);
136 XCTAssertNil(weakEngine);
145 message:[
@"bar" dataUsingEncoding:NSUTF8StringEncoding]
154 setMessageHandlerOnChannel:
@"foo"
165 binaryMessageHandler:nil]);
169 id plugin = OCMProtocolMock(
@protocol(FlutterPlugin));
170 OCMStub([plugin detachFromEngineForRegistrar:[OCMArg
any]]);
175 [registrar publish:plugin];
178 OCMVerify([plugin detachFromEngineForRegistrar:[OCMArg
any]]);
208 NSData* encodedSetInitialRouteMethod =
210 OCMVerify([mockBinaryMessenger sendOnChannel:
@"flutter/navigation"
211 message:encodedSetInitialRouteMethod]);
228 NSData* encodedSetInitialRouteMethod =
230 OCMVerify([mockBinaryMessenger sendOnChannel:
@"flutter/navigation"
231 message:encodedSetInitialRouteMethod]);
244 settings.enable_software_rendering =
true;
256 XCTestExpectation* timeoutFirstFrame = [
self expectationWithDescription:@"timeoutFirstFrame"];
259 if (timeoutFirstFrame) {
260 [timeoutFirstFrame fulfill];
263 [
self waitForExpectationsWithTimeout:5 handler:nil];
273 XCTAssertNotNil(
spawn);
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];
295 XCTestExpectation* gotMessage = [
self expectationWithDescription:@"gotMessage"];
296 dispatch_async(dispatch_get_main_queue(), ^{
303 auto message = std::make_unique<flutter::PlatformMessage>(
"foo",
nullptr);
308 [registrar.messenger setMessageHandlerOnChannel:@"foo"
309 binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
310 [gotMessage fulfill];
313 [
self waitForExpectationsWithTimeout:1 handler:nil];
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);
348 settings.enable_software_rendering =
true;
355 id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
356 OCMStub([mockMainBundle objectForInfoDictionaryKey:
@"FLTEnableIOSEmbedderAPI"])
359 settings.enable_software_rendering =
true;
376 OCMVerify([mockBinaryMessenger sendOnChannel:
@"flutter/textinput" message:encodedMethodCall]);
381 id mockEngine = OCMPartialMock(
engine);
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]]);
410 bool switch_value =
false;
411 [engine
shell].GetIsGpuDisabledSyncSwitch()->Execute(
413 switch_value =
false;
415 XCTAssertTrue(switch_value);
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]]);
439 bool switch_value =
true;
440 [engine
shell].GetIsGpuDisabledSyncSwitch()->Execute(
442 switch_value =
false;
444 XCTAssertFalse(switch_value);
454 XCTAssertNotNil(
spawn);
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());
468 engine.ensureSemanticsEnabledCalled = NO;
470 XCTAssertTrue(
engine.ensureSemanticsEnabledCalled);
static SkISize times(const SkISize &size, float factor)
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
static SkScalar center(float pos0, float pos1)
While the engine operates entirely on the UI task runner, it needs the capabilities of the other comp...
virtual void OnEngineHandlePlatformMessage(std::unique_ptr< PlatformMessage > message)=0
When the Flutter application has a message to send to the underlying platform, the message needs to b...
const TaskRunners & GetTaskRunners() const override
If callers wish to interact directly with any shell subcomponents, they must (on the platform thread)...
void ensureSemanticsEnabled()
BOOL ensureSemanticsEnabledCalled
void testSetBinaryMessengerToSameBinaryMessenger()
void testRunningInitialRouteSendsNavigationMessage()
void testInitialRouteSettingsSendsNavigationMessage()
void testWaitForFirstFrameTimeout()
void testCanEnableDisableEmbedderAPIThroughInfoPlist()
void testLifeCycleNotificationDidEnterBackground()
void testEnableSemanticsWhenFlutterViewAccessibilityDidCall()
void testThreadPrioritySetCorrectly()
void testSendMessageBeforeRun()
void testSpawnsShareGpuContext()
void testFlutterEngineUpdatesDisplays()
void testDeallocNotification()
void testSetHandlerAfterRun()
void testFlutterTextInputViewDidResignFirstResponderWillCallTextInputClientConnectionClosed()
void testSetMessageHandlerBeforeRun()
void testPlatformViewsControllerRenderingMetalBackend()
void testPlatformViewsControllerRenderingSoftware()
void testNilSetMessageHandlerBeforeRun()
void testNotifyPluginOfDealloc()
void testLifeCycleNotificationWillEnterForeground()
void flutterTextInputView:didResignFirstResponderWithTextInputClient:(FlutterTextInputView *textInputView, [didResignFirstResponderWithTextInputClient] int client)
void setBinaryMessenger:(FlutterBinaryMessengerRelay *binaryMessenger)
NSObject< FlutterBinaryMessenger > * binaryMessenger
FlutterDartProject * project()
flutter::IOSRenderingAPI platformViewsRenderingAPI()
void flutterViewAccessibilityDidCall()
void waitForFirstFrame:callback:(NSTimeInterval timeout, [callback] void(^_Nonnull callback)(BOOL didTimeout))
NSObject< FlutterPluginRegistrar > * registrarForPlugin:(NSString *pluginKey)
flutter::PlatformViewIOS * iosPlatformView()
BOOL runWithEntrypoint:initialRoute:(nullable NSString *entrypoint,[initialRoute] nullable NSString *initialRoute)
FlutterEngine * spawnWithEntrypoint:libraryURI:initialRoute:entrypointArgs:(/*nullable */NSString *entrypoint, [libraryURI]/*nullable */NSString *libraryURI, [initialRoute]/*nullable */NSString *initialRoute, [entrypointArgs]/*nullable */NSArray< NSString * > *entrypointArgs)
FlutterViewController * viewController
instancetype sharedInstance()
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
flutter::Settings FLTDefaultSettingsForBundle(NSBundle *bundle, NSProcessInfo *processInfoOrNil)
SIT bool any(const Vec< 1, T > &x)
Represents the 2 code paths available when calling |SyncSwitch::Execute|.