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");
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 {
145 message:[@"bar" dataUsingEncoding:NSUTF8StringEncoding]
149- (void)testSetMessageHandlerBeforeRun {
154 setMessageHandlerOnChannel:
@"foo"
160- (void)testNilSetMessageHandlerBeforeRun {
165 binaryMessageHandler:nil]);
168- (void)testNotifyPluginOfDealloc {
169 id plugin = OCMProtocolMock(
@protocol(FlutterPlugin));
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(), ^{
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];
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;
355 id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
356 OCMStub([mockMainBundle objectForInfoDictionaryKey:
@"FLTEnableIOSEmbedderAPI"])
359 settings.enable_software_rendering =
true;
366- (void)testFlutterTextInputViewDidResignFirstResponderWillCallTextInputClientConnectionClosed {
376 OCMVerify([mockBinaryMessenger sendOnChannel:
@"flutter/textinput" message:encodedMethodCall]);
379- (void)testFlutterEngineUpdatesDisplays {
381 id mockEngine = OCMPartialMock(
engine);
384 OCMVerify(
times(1), [mockEngine updateDisplays]);
386 OCMVerify(
times(2), [mockEngine updateDisplays]);
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]]);
410 bool switch_value =
false;
411 [engine
shell].GetIsGpuDisabledSyncSwitch()->Execute(
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]]);
439 bool switch_value =
true;
440 [engine
shell].GetIsGpuDisabledSyncSwitch()->Execute(
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;
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 flutterTextInputView:didResignFirstResponderWithTextInputClient:(FlutterTextInputView *textInputView, [didResignFirstResponderWithTextInputClient] int client)
void setBinaryMessenger:(FlutterBinaryMessengerRelay *binaryMessenger)
NSObject< FlutterBinaryMessenger > * binaryMessenger
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)
Represents the 2 code paths available when calling |SyncSwitchExecute|.