Flutter macOS Embedder
FlutterWindowControllerTest.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
7 
12 #import "flutter/testing/testing.h"
13 #import "third_party/googletest/googletest/include/gtest/gtest.h"
14 
15 namespace flutter::testing {
16 
18  public:
20 
21  void SetUp() {
23 
24  [GetFlutterEngine() runWithEntrypoint:@"testWindowController"];
25 
26  bool signalled = false;
27 
28  AddNativeCallback("SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
30  signalled = true;
31  }));
32 
33  while (!signalled) {
34  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
35  }
36  }
37 
38  void TearDown() {
39  [GetFlutterEngine().windowController closeAllWindows];
41  }
42 
43  protected:
45  if (isolate_) {
46  return *isolate_;
47  } else {
48  FML_LOG(ERROR) << "Isolate is not set.";
49  FML_UNREACHABLE();
50  }
51  }
52 
53  std::optional<flutter::Isolate> isolate_;
54 };
55 
56 class FlutterWindowControllerRetainTest : public ::testing::Test {};
57 
58 TEST_F(FlutterWindowControllerTest, CreateRegularWindow) {
60  .has_size = true,
61  .size = {.width = 800, .height = 600},
62  .on_should_close = [] {},
63  .on_will_close = [] {},
64  .notify_listeners = [] {},
65  };
66 
67  FlutterEngine* engine = GetFlutterEngine();
68  int64_t engineId = reinterpret_cast<int64_t>(engine);
69 
70  {
71  IsolateScope isolate_scope(isolate());
72  int64_t handle = InternalFlutter_WindowController_CreateRegularWindow(engineId, &request);
73  EXPECT_EQ(handle, 1);
74 
75  FlutterViewController* viewController = [engine viewControllerForIdentifier:handle];
76  EXPECT_NE(viewController, nil);
77  CGSize size = viewController.view.frame.size;
78  EXPECT_EQ(size.width, 800);
79  EXPECT_EQ(size.height, 600);
80  }
81 }
82 
83 TEST_F(FlutterWindowControllerRetainTest, WindowControllerDoesNotRetainEngine) {
85  .has_size = true,
86  .size = {.width = 800, .height = 600},
87  .on_should_close = [] {},
88  .on_will_close = [] {},
89  .notify_listeners = [] {},
90  };
91 
92  __weak FlutterEngine* weakEngine = nil;
93  @autoreleasepool {
94  NSString* fixtures = @(flutter::testing::GetFixturesPath());
95  NSLog(@"Fixtures path: %@", fixtures);
96  FlutterDartProject* project = [[FlutterDartProject alloc]
97  initWithAssetsPath:fixtures
98  ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
99 
100  static std::optional<flutter::Isolate> isolate;
101  isolate = std::nullopt;
102 
103  project.rootIsolateCreateCallback = [](void*) { isolate = flutter::Isolate::Current(); };
104  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test"
105  project:project
106  allowHeadlessExecution:YES];
107  weakEngine = engine;
108  [engine runWithEntrypoint:@"testWindowControllerRetainCycle"];
109 
110  int64_t engineId = reinterpret_cast<int64_t>(engine);
111 
112  {
113  FML_DCHECK(isolate.has_value());
114  // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
115  IsolateScope isolateScope(*isolate);
116  int64_t handle = InternalFlutter_WindowController_CreateRegularWindow(engineId, &request);
117  EXPECT_EQ(handle, 1);
118  }
119 
120  [engine.windowController closeAllWindows];
121  [engine shutDownEngine];
122  }
123  EXPECT_EQ(weakEngine, nil);
124 }
125 
126 TEST_F(FlutterWindowControllerTest, DestroyRegularWindow) {
128  .has_size = true,
129  .size = {.width = 800, .height = 600},
130  .on_should_close = [] {},
131  .on_will_close = [] {},
132  .notify_listeners = [] {},
133  };
134 
135  FlutterEngine* engine = GetFlutterEngine();
136  int64_t engine_id = reinterpret_cast<int64_t>(engine);
137 
138  IsolateScope isolate_scope(isolate());
139  int64_t handle = InternalFlutter_WindowController_CreateRegularWindow(engine_id, &request);
140  FlutterViewController* viewController = [engine viewControllerForIdentifier:handle];
141 
142  InternalFlutter_Window_Destroy(engine_id, (__bridge void*)viewController.view.window);
143  viewController = [engine viewControllerForIdentifier:handle];
144  EXPECT_EQ(viewController, nil);
145 }
146 
149  .has_size = true,
150  .size = {.width = 800, .height = 600},
151  .on_should_close = [] {},
152  .on_will_close = [] {},
153  .notify_listeners = [] {},
154  };
155 
156  FlutterEngine* engine = GetFlutterEngine();
157  int64_t engine_id = reinterpret_cast<int64_t>(engine);
158 
159  IsolateScope isolate_scope(isolate());
160  int64_t handle = InternalFlutter_WindowController_CreateRegularWindow(engine_id, &request);
161  FlutterViewController* viewController = [engine viewControllerForIdentifier:handle];
162 
163  void* window_handle = InternalFlutter_Window_GetHandle(engine_id, handle);
164  EXPECT_EQ(window_handle, (__bridge void*)viewController.view.window);
165 }
166 
169  .has_size = true,
170  .size = {.width = 800, .height = 600},
171  .on_should_close = [] {},
172  .on_will_close = [] {},
173  .notify_listeners = [] {},
174  };
175 
176  FlutterEngine* engine = GetFlutterEngine();
177  int64_t engine_id = reinterpret_cast<int64_t>(engine);
178 
179  IsolateScope isolate_scope(isolate());
180  int64_t handle = InternalFlutter_WindowController_CreateRegularWindow(engine_id, &request);
181 
182  FlutterViewController* viewController = [engine viewControllerForIdentifier:handle];
183  NSWindow* window = viewController.view.window;
184  void* windowHandle = (__bridge void*)window;
185 
186  EXPECT_EQ(window.zoomed, NO);
187  EXPECT_EQ(window.miniaturized, NO);
188  EXPECT_EQ(window.styleMask & NSWindowStyleMaskFullScreen, 0u);
189 
190  InternalFlutter_Window_SetMaximized(windowHandle, true);
191  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, false);
192  EXPECT_EQ(window.zoomed, YES);
193 
194  InternalFlutter_Window_SetMaximized(windowHandle, false);
195  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, false);
196  EXPECT_EQ(window.zoomed, NO);
197 
198  // FullScreen toggle does not seem to work when the application is not run from a bundle.
199 
200  InternalFlutter_Window_Minimize(windowHandle);
201  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, false);
202  EXPECT_EQ(window.miniaturized, YES);
203 }
204 } // namespace flutter::testing
FLUTTER_DARWIN_EXPORT int64_t InternalFlutter_WindowController_CreateRegularWindow(int64_t engine_id, const FlutterWindowCreationRequest *request)
FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_SetMaximized(void *window, bool maximized)
FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_Destroy(int64_t engine_id, void *window)
FLUTTER_DARWIN_EXPORT void * InternalFlutter_Window_GetHandle(int64_t engine_id, FlutterViewIdentifier view_id)
FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_Minimize(void *window)
static Isolate Current()
Definition: isolate_scope.cc:9
void AddNativeCallback(const char *name, Dart_NativeFunction function)
TEST_F(AccessibilityBridgeMacWindowTest, SendsAccessibilityCreateNotificationFlutterViewWindow)