mirror of
https://github.com/encounter/engine.git
synced 2026-07-10 03:18:43 -07:00
461 lines
20 KiB
Objective-C
461 lines
20 KiB
Objective-C
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#import <OCMock/OCMock.h>
|
|
#import <XCTest/XCTest.h>
|
|
#include "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
|
|
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
|
|
|
|
#include "FlutterBinaryMessenger.h"
|
|
|
|
FLUTTER_ASSERT_ARC
|
|
|
|
extern NSNotificationName const FlutterViewControllerWillDealloc;
|
|
|
|
/// A simple mock class for FlutterEngine.
|
|
///
|
|
/// OCMockClass can't be used for FlutterEngine sometimes because OCMock retains arguments to
|
|
/// invocations and since the init for FlutterViewController calls a method on the
|
|
/// FlutterEngine it creates a retain cycle that stops us from testing behaviors related to
|
|
/// deleting FlutterViewControllers.
|
|
@interface MockEngine : NSObject
|
|
@end
|
|
|
|
@implementation MockEngine
|
|
- (void)setViewController:(FlutterViewController*)viewController {
|
|
// noop
|
|
}
|
|
@end
|
|
|
|
@interface FlutterViewControllerTest : XCTestCase
|
|
@end
|
|
|
|
// The following conditional compilation defines an API 13 concept on earlier API targets so that
|
|
// a compiler compiling against API 12 or below does not blow up due to non-existent members.
|
|
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 130000
|
|
typedef enum UIAccessibilityContrast : NSInteger {
|
|
UIAccessibilityContrastUnspecified = 0,
|
|
UIAccessibilityContrastNormal = 1,
|
|
UIAccessibilityContrastHigh = 2
|
|
} UIAccessibilityContrast;
|
|
|
|
@interface UITraitCollection (MethodsFromNewerSDK)
|
|
- (UIAccessibilityContrast)accessibilityContrast;
|
|
@end
|
|
#endif
|
|
|
|
@interface FlutterViewController (Tests)
|
|
- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences;
|
|
@end
|
|
|
|
@implementation FlutterViewControllerTest
|
|
|
|
- (void)testBinaryMessenger {
|
|
id engine = OCMClassMock([FlutterEngine class]);
|
|
FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
|
|
nibName:nil
|
|
bundle:nil];
|
|
XCTAssertNotNil(vc);
|
|
id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
|
|
OCMStub([engine binaryMessenger]).andReturn(messenger);
|
|
XCTAssertEqual(vc.binaryMessenger, messenger);
|
|
OCMVerify([engine binaryMessenger]);
|
|
}
|
|
|
|
#pragma mark - Platform Brightness
|
|
|
|
- (void)testItReportsLightPlatformBrightnessByDefault {
|
|
// Setup test.
|
|
id engine = OCMClassMock([FlutterEngine class]);
|
|
|
|
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
|
|
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
|
|
|
|
FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
|
|
nibName:nil
|
|
bundle:nil];
|
|
|
|
// Exercise behavior under test.
|
|
[vc traitCollectionDidChange:nil];
|
|
|
|
// Verify behavior.
|
|
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
|
|
return [message[@"platformBrightness"] isEqualToString:@"light"];
|
|
}]]);
|
|
|
|
// Clean up mocks
|
|
[engine stopMocking];
|
|
[settingsChannel stopMocking];
|
|
}
|
|
|
|
- (void)testItReportsPlatformBrightnessWhenViewWillAppear {
|
|
// Setup test.
|
|
id engine = OCMClassMock([FlutterEngine class]);
|
|
|
|
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
|
|
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
|
|
|
|
FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
|
|
nibName:nil
|
|
bundle:nil];
|
|
|
|
// Exercise behavior under test.
|
|
[vc viewWillAppear:false];
|
|
|
|
// Verify behavior.
|
|
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
|
|
return [message[@"platformBrightness"] isEqualToString:@"light"];
|
|
}]]);
|
|
|
|
// Clean up mocks
|
|
[engine stopMocking];
|
|
[settingsChannel stopMocking];
|
|
}
|
|
|
|
- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt {
|
|
if (!@available(iOS 13, *)) {
|
|
return;
|
|
}
|
|
|
|
// Setup test.
|
|
id engine = OCMClassMock([FlutterEngine class]);
|
|
|
|
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
|
|
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
|
|
|
|
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine
|
|
nibName:nil
|
|
bundle:nil];
|
|
id mockTraitCollection =
|
|
[self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
|
|
|
|
// We partially mock the real FlutterViewController to act as the OS and report
|
|
// the UITraitCollection of our choice. Mocking the object under test is not
|
|
// desirable, but given that the OS does not offer a DI approach to providing
|
|
// our own UITraitCollection, this seems to be the least bad option.
|
|
id partialMockVC = OCMPartialMock(realVC);
|
|
OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
|
|
|
|
// Exercise behavior under test.
|
|
[partialMockVC traitCollectionDidChange:nil];
|
|
|
|
// Verify behavior.
|
|
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
|
|
return [message[@"platformBrightness"] isEqualToString:@"dark"];
|
|
}]]);
|
|
|
|
// Clean up mocks
|
|
[partialMockVC stopMocking];
|
|
[engine stopMocking];
|
|
[settingsChannel stopMocking];
|
|
[mockTraitCollection stopMocking];
|
|
}
|
|
|
|
// Creates a mocked UITraitCollection with nil values for everything except userInterfaceStyle,
|
|
// which is set to the given "style".
|
|
- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
|
|
id mockTraitCollection = OCMClassMock([UITraitCollection class]);
|
|
OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
|
|
return mockTraitCollection;
|
|
}
|
|
|
|
#pragma mark - Platform Contrast
|
|
|
|
- (void)testItReportsNormalPlatformContrastByDefault {
|
|
if (!@available(iOS 13, *)) {
|
|
return;
|
|
}
|
|
|
|
// Setup test.
|
|
id engine = OCMClassMock([FlutterEngine class]);
|
|
|
|
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
|
|
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
|
|
|
|
FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
|
|
nibName:nil
|
|
bundle:nil];
|
|
|
|
// Exercise behavior under test.
|
|
[vc traitCollectionDidChange:nil];
|
|
|
|
// Verify behavior.
|
|
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
|
|
return [message[@"platformContrast"] isEqualToString:@"normal"];
|
|
}]]);
|
|
|
|
// Clean up mocks
|
|
[engine stopMocking];
|
|
[settingsChannel stopMocking];
|
|
}
|
|
|
|
- (void)testItReportsPlatformContrastWhenViewWillAppear {
|
|
if (!@available(iOS 13, *)) {
|
|
return;
|
|
}
|
|
|
|
// Setup test.
|
|
id engine = OCMClassMock([FlutterEngine class]);
|
|
|
|
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
|
|
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
|
|
|
|
FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:engine
|
|
nibName:nil
|
|
bundle:nil];
|
|
|
|
// Exercise behavior under test.
|
|
[vc viewWillAppear:false];
|
|
|
|
// Verify behavior.
|
|
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
|
|
return [message[@"platformContrast"] isEqualToString:@"normal"];
|
|
}]]);
|
|
|
|
// Clean up mocks
|
|
[engine stopMocking];
|
|
[settingsChannel stopMocking];
|
|
}
|
|
|
|
- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt {
|
|
if (!@available(iOS 13, *)) {
|
|
return;
|
|
}
|
|
|
|
// Setup test.
|
|
id engine = OCMClassMock([FlutterEngine class]);
|
|
|
|
id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]);
|
|
OCMStub([engine settingsChannel]).andReturn(settingsChannel);
|
|
|
|
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine
|
|
nibName:nil
|
|
bundle:nil];
|
|
id mockTraitCollection = [self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
|
|
|
|
// We partially mock the real FlutterViewController to act as the OS and report
|
|
// the UITraitCollection of our choice. Mocking the object under test is not
|
|
// desirable, but given that the OS does not offer a DI approach to providing
|
|
// our own UITraitCollection, this seems to be the least bad option.
|
|
id partialMockVC = OCMPartialMock(realVC);
|
|
OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
|
|
|
|
// Exercise behavior under test.
|
|
[partialMockVC traitCollectionDidChange:mockTraitCollection];
|
|
|
|
// Verify behavior.
|
|
OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) {
|
|
return [message[@"platformContrast"] isEqualToString:@"high"];
|
|
}]]);
|
|
|
|
// Clean up mocks
|
|
[partialMockVC stopMocking];
|
|
[engine stopMocking];
|
|
[settingsChannel stopMocking];
|
|
[mockTraitCollection stopMocking];
|
|
}
|
|
|
|
- (void)testPerformOrientationUpdateForcesOrientationChange {
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
|
|
currentOrientation:UIInterfaceOrientationLandscapeLeft
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationPortrait];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
|
|
currentOrientation:UIInterfaceOrientationLandscapeRight
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationPortrait];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
|
|
currentOrientation:UIInterfaceOrientationPortraitUpsideDown
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationPortrait];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
|
|
currentOrientation:UIInterfaceOrientationLandscapeLeft
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
|
|
currentOrientation:UIInterfaceOrientationLandscapeRight
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
|
|
currentOrientation:UIInterfaceOrientationPortrait
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
|
|
currentOrientation:UIInterfaceOrientationPortrait
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationLandscapeLeft];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
|
|
currentOrientation:UIInterfaceOrientationPortraitUpsideDown
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationLandscapeLeft];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
|
|
currentOrientation:UIInterfaceOrientationPortrait
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationLandscapeLeft];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
|
|
currentOrientation:UIInterfaceOrientationLandscapeRight
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationLandscapeLeft];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
|
|
currentOrientation:UIInterfaceOrientationPortraitUpsideDown
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationLandscapeLeft];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
|
|
currentOrientation:UIInterfaceOrientationPortrait
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationLandscapeRight];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
|
|
currentOrientation:UIInterfaceOrientationLandscapeLeft
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationLandscapeRight];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
|
|
currentOrientation:UIInterfaceOrientationPortraitUpsideDown
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationLandscapeRight];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
|
|
currentOrientation:UIInterfaceOrientationPortraitUpsideDown
|
|
didChangeOrientation:YES
|
|
resultingOrientation:UIInterfaceOrientationPortrait];
|
|
}
|
|
|
|
- (void)testPerformOrientationUpdateDoesNotForceOrientationChange {
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
|
|
currentOrientation:UIInterfaceOrientationPortrait
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
|
|
currentOrientation:UIInterfaceOrientationPortraitUpsideDown
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
|
|
currentOrientation:UIInterfaceOrientationLandscapeLeft
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
|
|
currentOrientation:UIInterfaceOrientationLandscapeRight
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
|
|
currentOrientation:UIInterfaceOrientationPortrait
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
|
|
currentOrientation:UIInterfaceOrientationLandscapeLeft
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
|
|
currentOrientation:UIInterfaceOrientationLandscapeRight
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
|
|
currentOrientation:UIInterfaceOrientationPortrait
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
|
|
currentOrientation:UIInterfaceOrientationPortraitUpsideDown
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
|
|
currentOrientation:UIInterfaceOrientationLandscapeLeft
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
|
|
currentOrientation:UIInterfaceOrientationLandscapeRight
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
|
|
currentOrientation:UIInterfaceOrientationLandscapeLeft
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
|
|
[self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
|
|
currentOrientation:UIInterfaceOrientationLandscapeRight
|
|
didChangeOrientation:NO
|
|
resultingOrientation:0];
|
|
}
|
|
|
|
// Perform an orientation update test that fails when the expected outcome
|
|
// for an orientation update is not met
|
|
- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
|
|
currentOrientation:(UIInterfaceOrientation)currentOrientation
|
|
didChangeOrientation:(BOOL)didChange
|
|
resultingOrientation:(UIInterfaceOrientation)resultingOrientation {
|
|
id engine = OCMClassMock([FlutterEngine class]);
|
|
|
|
id deviceMock = OCMPartialMock([UIDevice currentDevice]);
|
|
if (!didChange) {
|
|
OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]);
|
|
} else {
|
|
OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]);
|
|
}
|
|
|
|
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine
|
|
nibName:nil
|
|
bundle:nil];
|
|
id mockApplication = OCMClassMock([UIApplication class]);
|
|
OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
|
|
OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation);
|
|
|
|
[realVC performOrientationUpdate:mask];
|
|
OCMVerifyAll(deviceMock);
|
|
[engine stopMocking];
|
|
[deviceMock stopMocking];
|
|
[mockApplication stopMocking];
|
|
}
|
|
|
|
// Creates a mocked UITraitCollection with nil values for everything except accessibilityContrast,
|
|
// which is set to the given "contrast".
|
|
- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
|
|
id mockTraitCollection = OCMClassMock([UITraitCollection class]);
|
|
OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
|
|
return mockTraitCollection;
|
|
}
|
|
|
|
- (void)testWillDeallocNotification {
|
|
XCTestExpectation* expectation =
|
|
[[XCTestExpectation alloc] initWithDescription:@"notification called"];
|
|
id engine = [[MockEngine alloc] init];
|
|
@autoreleasepool {
|
|
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine
|
|
nibName:nil
|
|
bundle:nil];
|
|
id observer =
|
|
[[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
|
|
object:nil
|
|
queue:[NSOperationQueue mainQueue]
|
|
usingBlock:^(NSNotification* _Nonnull note) {
|
|
[expectation fulfill];
|
|
}];
|
|
realVC = nil;
|
|
}
|
|
[self waitForExpectations:@[ expectation ] timeout:1.0];
|
|
}
|
|
|
|
@end
|