Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 23a1c22

Browse files
committed
[macOS] Clean up allocations in menu plugin test
Runs all FlutterMenuPlugin tests in an AutoReleasepoolTest, which ensures all allocations are cleaned up. Also extracts out some hackery into the FlutterMenuPluginTest fixture to ensure that NSApplication instantiates everything necessary to do menu bar manipulation. Also replaces the use of OCMock in FlutterMenuPluginTest.mm with a fake FakePluginRegistrar class. This avoids unnecessary use of OCMock in the tests, which has been responsible for flakiness in some tests, in particular where the mock is used across threads. This test was not problematic, but the fake makes the tests more readable. Issue: flutter/flutter#104789 Issue: flutter/flutter#127441 Issue: flutter/flutter#124840
1 parent 30327ea commit 23a1c22

File tree

1 file changed

+69
-42
lines changed

1 file changed

+69
-42
lines changed

shell/platform/darwin/macos/framework/Source/FlutterMenuPluginTest.mm

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,86 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin.h"
6+
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin_Internal.h"
7+
8+
#import "flutter/shell/platform/common/platform_provided_menu.h"
9+
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
510
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginMacOS.h"
611
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h"
712
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h"
813
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h"
914
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h"
10-
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin.h"
11-
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin_Internal.h"
15+
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h"
1216
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.h"
1317
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
14-
15-
#include "flutter/shell/platform/common/platform_provided_menu.h"
18+
#include "flutter/testing/autoreleasepool_test.h"
19+
#include "flutter/testing/testing.h"
1620
#include "gtest/gtest.h"
1721

18-
#import <OCMock/OCMock.h>
19-
#import "flutter/testing/testing.h"
22+
@interface FakePluginRegistrar : NSObject <FlutterPluginRegistrar>
23+
@property(nonatomic, readonly) id<FlutterPlugin> plugin;
24+
@property(nonatomic, readonly) FlutterMethodChannel* channel;
25+
@end
26+
27+
@implementation FakePluginRegistrar
28+
@synthesize messenger;
29+
@synthesize textures;
30+
@synthesize view;
31+
32+
- (void)addMethodCallDelegate:(nonnull id<FlutterPlugin>)delegate
33+
channel:(nonnull FlutterMethodChannel*)channel {
34+
_plugin = delegate;
35+
_channel = channel;
36+
[_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
37+
[delegate handleMethodCall:call result:result];
38+
}];
39+
}
40+
41+
- (void)addApplicationDelegate:(nonnull NSObject<FlutterAppLifecycleDelegate>*)delegate {
42+
}
43+
44+
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
45+
withId:(nonnull NSString*)factoryId {
46+
}
2047

21-
@interface FlutterMenuPluginTestObjc : NSObject
22-
- (bool)testSetMenu;
48+
- (void)publish:(nonnull NSObject*)value {
49+
}
50+
51+
- (nonnull NSString*)lookupKeyForAsset:(nonnull NSString*)asset {
52+
return @"";
53+
}
54+
55+
- (nonnull NSString*)lookupKeyForAsset:(nonnull NSString*)asset
56+
fromPackage:(nonnull NSString*)package {
57+
return @"";
58+
}
2359
@end
2460

25-
@implementation FlutterMenuPluginTestObjc
61+
namespace flutter::testing {
2662

27-
- (bool)testSetMenu {
28-
// Workaround to deflake the test.
29-
// See: https://github.com/flutter/flutter/issues/104748#issuecomment-1159336728
30-
NSView* view = [[NSView alloc] initWithFrame:NSZeroRect];
31-
view.wantsLayer = YES;
63+
// FlutterMenuPluginTest is an AutoreleasePoolTest that allocates an NSView.
64+
//
65+
// This supports the use of NSApplication features that rely on the assumption of a view, such as
66+
// when modifying the application menu bar, or even accessing the NSApplication.localizedName
67+
// property.
68+
//
69+
// See: https://github.com/flutter/flutter/issues/104748#issuecomment-1159336728
70+
class FlutterMenuPluginTest : public AutoreleasePoolTest {
71+
public:
72+
FlutterMenuPluginTest();
73+
~FlutterMenuPluginTest() = default;
74+
75+
private:
76+
NSView* view_;
77+
};
78+
79+
FlutterMenuPluginTest::FlutterMenuPluginTest() {
80+
view_ = [[NSView alloc] initWithFrame:NSZeroRect];
81+
view_.wantsLayer = YES;
82+
}
3283

84+
TEST_F(FlutterMenuPluginTest, TestSetMenu) {
3385
// Build a simulation of the default main menu.
3486
NSMenu* mainMenu = [[NSMenu alloc] init];
3587
NSMenuItem* appNameMenu = [[NSMenuItem alloc] initWithTitle:@"APP_NAME"
@@ -43,26 +95,9 @@ - (bool)testSetMenu {
4395
[mainMenu addItem:appNameMenu];
4496
[NSApp setMainMenu:mainMenu];
4597

46-
id<FlutterPluginRegistrar> pluginRegistrarMock =
47-
OCMProtocolMock(@protocol(FlutterPluginRegistrar));
48-
__block FlutterMethodChannel* pluginChannel;
49-
__block FlutterMenuPlugin* plugin;
50-
id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
51-
OCMStub([pluginRegistrarMock messenger]).andReturn(binaryMessengerMock);
52-
OCMStub( // NOLINT(google-objc-avoid-throwing-exception)
53-
[pluginRegistrarMock addMethodCallDelegate:[OCMArg any] channel:[OCMArg any]])
54-
.andDo(^(NSInvocation* invocation) {
55-
id<FlutterPlugin> delegate;
56-
FlutterMethodChannel* channel;
57-
[invocation getArgument:&delegate atIndex:2];
58-
[invocation getArgument:&channel atIndex:3];
59-
pluginChannel = channel;
60-
plugin = delegate;
61-
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
62-
[delegate handleMethodCall:call result:result];
63-
}];
64-
});
65-
[FlutterMenuPlugin registerWithRegistrar:pluginRegistrarMock];
98+
FakePluginRegistrar* registrar = [[FakePluginRegistrar alloc] init];
99+
[FlutterMenuPlugin registerWithRegistrar:registrar];
100+
FlutterMenuPlugin* plugin = [registrar plugin];
66101

67102
NSDictionary* testMenus = @{
68103
@"0" : @[
@@ -173,14 +208,6 @@ - (bool)testSetMenu {
173208
EXPECT_TRUE(
174209
[NSStringFromSelector([secondMenuLast action]) isEqualToString:@"flutterMenuItemSelected:"]);
175210
EXPECT_EQ([secondMenuLast tag], 7);
176-
177-
return true;
178211
}
179212

180-
@end
181-
182-
namespace flutter::testing {
183-
TEST(FlutterMenuPluginTest, TestSetMenu) {
184-
ASSERT_TRUE([[FlutterMenuPluginTestObjc alloc] testSetMenu]);
185-
}
186213
} // namespace flutter::testing

0 commit comments

Comments
 (0)