Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,24 @@ - (void)testCallsNotifyLowMemory {
OCMVerify([mockEngine notifyLowMemory]);
OCMReject([mockEngine notifyLowMemory]);

XCTNSNotificationExpectation* memoryExpectation = [[XCTNSNotificationExpectation alloc]
initWithName:UIApplicationDidReceiveMemoryWarningNotification];
[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
[self waitForExpectations:@[ memoryExpectation ] timeout:5.0];
OCMVerify([mockEngine notifyLowMemory]);
OCMReject([mockEngine notifyLowMemory]);

XCTNSNotificationExpectation* backgroundExpectation = [[XCTNSNotificationExpectation alloc]
initWithName:UIApplicationDidEnterBackgroundNotification];
[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationDidEnterBackgroundNotification
object:nil];
[self waitForExpectations:@[ backgroundExpectation ] timeout:5.0];

OCMVerify([mockEngine notifyLowMemory]);
[mockEngine stopMocking];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
FLUTTER_ASSERT_ARC

@interface FlutterPluginAppLifeCycleDelegateTest : XCTestCase

@end

@implementation FlutterPluginAppLifeCycleDelegateTest
Expand All @@ -22,51 +21,71 @@ - (void)testCreate {
}

- (void)testDidEnterBackground {
XCTNSNotificationExpectation* expectation = [[XCTNSNotificationExpectation alloc]
initWithName:UIApplicationDidEnterBackgroundNotification];
FlutterPluginAppLifeCycleDelegate* delegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
id plugin = OCMProtocolMock(@protocol(FlutterPlugin));
[delegate addDelegate:plugin];
[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationDidEnterBackgroundNotification
object:nil];

[self waitForExpectations:@[ expectation ] timeout:5.0];
OCMVerify([plugin applicationDidEnterBackground:[UIApplication sharedApplication]]);
}

- (void)testWillEnterForeground {
XCTNSNotificationExpectation* expectation = [[XCTNSNotificationExpectation alloc]
initWithName:UIApplicationWillEnterForegroundNotification];

FlutterPluginAppLifeCycleDelegate* delegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
id plugin = OCMProtocolMock(@protocol(FlutterPlugin));
[delegate addDelegate:plugin];
[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationWillEnterForegroundNotification
object:nil];
[self waitForExpectations:@[ expectation ] timeout:5.0];
OCMVerify([plugin applicationWillEnterForeground:[UIApplication sharedApplication]]);
}

- (void)skip_testWillResignActive {
- (void)testWillResignActive {
XCTNSNotificationExpectation* expectation =
[[XCTNSNotificationExpectation alloc] initWithName:UIApplicationWillResignActiveNotification];

FlutterPluginAppLifeCycleDelegate* delegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
id plugin = OCMProtocolMock(@protocol(FlutterPlugin));
[delegate addDelegate:plugin];
[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationWillResignActiveNotification
object:nil];
[self waitForExpectations:@[ expectation ] timeout:5.0];
OCMVerify([plugin applicationWillResignActive:[UIApplication sharedApplication]]);
}

- (void)skip_testDidBecomeActive {
- (void)testDidBecomeActive {
XCTNSNotificationExpectation* expectation =
[[XCTNSNotificationExpectation alloc] initWithName:UIApplicationDidBecomeActiveNotification];

FlutterPluginAppLifeCycleDelegate* delegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
id plugin = OCMProtocolMock(@protocol(FlutterPlugin));
[delegate addDelegate:plugin];
[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationDidBecomeActiveNotification
object:nil];
[self waitForExpectations:@[ expectation ] timeout:5.0];
OCMVerify([plugin applicationDidBecomeActive:[UIApplication sharedApplication]]);
}

- (void)testWillTerminate {
XCTNSNotificationExpectation* expectation =
[[XCTNSNotificationExpectation alloc] initWithName:UIApplicationWillTerminateNotification];

FlutterPluginAppLifeCycleDelegate* delegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
id plugin = OCMProtocolMock(@protocol(FlutterPlugin));
[delegate addDelegate:plugin];
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationWillTerminateNotification
object:nil];
[self waitForExpectations:@[ expectation ] timeout:5.0];
OCMVerify([plugin applicationWillTerminate:[UIApplication sharedApplication]]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -885,9 +885,8 @@ - (void)testFlutterTokenizerCanParseLines {

- (void)testFlutterTextInputPluginRetainsFlutterTextInputView {
FlutterTextInputPlugin* myInputPlugin;
id myEngine = OCMClassMock([FlutterEngine class]);
myInputPlugin = [[FlutterTextInputPlugin alloc] init];
myInputPlugin.textInputDelegate = myEngine;
myInputPlugin.textInputDelegate = engine;
__weak UIView* activeView;
@autoreleasepool {
FlutterMethodCall* setClientCall = [FlutterMethodCall
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@
class PointerDataPacket {};
}

/// Sometimes we have to use a custom mock to avoid retain cycles in ocmock.
@interface FlutterEnginePartialMock : FlutterEngine
@property(nonatomic, strong) FlutterBasicMessageChannel* lifecycleChannel;
@property(nonatomic, weak) FlutterViewController* viewController;
@property(nonatomic, assign) BOOL didCallNotifyLowMemory;
@end

@implementation FlutterEnginePartialMock
@synthesize viewController;
@synthesize lifecycleChannel;

- (void)notifyLowMemory {
_didCallNotifyLowMemory = YES;
}
@end
Comment on lines +19 to +33
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmagman This is what I had to do in order to get rid of the cycles. I would hope that upgrading ocmock would also fix this, but it was hard to upgrade ocmock because it changed its dependencies, it's complicated to get GN working with it.


@interface FlutterEngine ()
- (BOOL)createShell:(NSString*)entrypoint
libraryURI:(NSString*)libraryURI
Expand Down Expand Up @@ -87,30 +103,37 @@ - (void)tearDown {

- (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController {
id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]);
OCMStub([self.mockEngine lifecycleChannel]).andReturn(lifecycleChannel);
FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init];
mockEngine.lifecycleChannel = lifecycleChannel;
FlutterViewController* viewControllerA =
[[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil];
FlutterViewController* viewControllerB =
[[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil];
id viewControllerMock = OCMPartialMock(viewControllerA);
OCMStub([viewControllerMock surfaceUpdated:NO]);
OCMStub([self.mockEngine viewController]).andReturn(viewControllerB);
mockEngine.viewController = viewControllerB;
[viewControllerA viewDidDisappear:NO];
OCMReject([lifecycleChannel sendMessage:@"AppLifecycleState.paused"]);
OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]);
}

- (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController {
id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]);
OCMStub([self.mockEngine lifecycleChannel]).andReturn(lifecycleChannel);
FlutterViewController* viewController =
[[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil];
id viewControllerMock = OCMPartialMock(viewController);
OCMStub([viewControllerMock surfaceUpdated:NO]);
OCMStub([self.mockEngine viewController]).andReturn(viewController);
[viewController viewDidDisappear:NO];
OCMVerify([lifecycleChannel sendMessage:@"AppLifecycleState.paused"]);
OCMVerify([viewControllerMock surfaceUpdated:NO]);
FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init];
mockEngine.lifecycleChannel = lifecycleChannel;
__weak FlutterViewController* weakViewController;
@autoreleasepool {
FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine
nibName:nil
bundle:nil];
weakViewController = viewController;
id viewControllerMock = OCMPartialMock(viewController);
OCMStub([viewControllerMock surfaceUpdated:NO]);
[viewController viewDidDisappear:NO];
OCMVerify([lifecycleChannel sendMessage:@"AppLifecycleState.paused"]);
OCMVerify([viewControllerMock surfaceUpdated:NO]);
}
XCTAssertNil(weakViewController);
}

- (void)testBinaryMessenger {
Expand Down Expand Up @@ -528,15 +551,16 @@ - (void)testHideOverlay {
}

- (void)testNotifyLowMemory {
FlutterViewController* viewController =
[[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil];
OCMStub([self.mockEngine viewController]).andReturn(viewController);
FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init];
FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine
nibName:nil
bundle:nil];
id viewControllerMock = OCMPartialMock(viewController);
OCMStub([viewControllerMock surfaceUpdated:NO]);

[viewController beginAppearanceTransition:NO animated:NO];
[viewController endAppearanceTransition];
OCMVerify([self.mockEngine notifyLowMemory]);
XCTAssertTrue(mockEngine.didCallNotifyLowMemory);
Copy link
Member

@jmagman jmagman Mar 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you can actually allocate a FlutterEngine subclass without mocking, is the subclass necessary? Does it work if you want for an XCTKVOExpectation?

[self keyValueObservingExpectation:mockEngine keyPath:@"notifyLowMemory" handler:nil]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean could we use a real FlutterEngine? There shouldn't be a keyPath notifyLowMemory on a standard FlutterEngine, that would only get created if there was a property called notifyLowMemory or if we manually registered one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(the subclass is also necessary for setting the lifecycle channel, not just needed for the notifyLowMemory stuff)

[viewControllerMock stopMocking];
}

- (void)testValidKeyUpEvent API_AVAILABLE(ios(13.4)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -970,5 +970,6 @@ - (void)testAccessibilityMessageAfterDeletion {
});
latch.Wait();
OCMVerify([messenger cleanupConnection:connection]);
[engine stopMocking];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be superfluous now. I don't think it hurts anything. Once I got everything working I didn't try to cull any of the previous work I did.

}
@end