| Flutter iOS Embedder |

VsyncWaiterIosTest.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
5 #import <OCMock/OCMock.h>
6 #import <XCTest/XCTest.h>
7
8 #include "flutter/fml/raster_thread_merger.h"
9 #include "flutter/fml/thread.h"
10
11 #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
12 #import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
13
14 FLUTTER_ASSERT_NOT_ARC
15 namespace {
16 fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
17 auto thread = std::make_unique<fml::Thread>(name);
18 auto runner = thread->GetTaskRunner();
19 return runner;
20 }
21 } // namespace
22
23 @interface VSyncClient (Testing)
24
25 - (CADisplayLink*)getDisplayLink;
26 - (void)onDisplayLink:(CADisplayLink*)link;
27
28 @end
29
30 @interface VsyncWaiterIosTest : XCTestCase
31 @end
32
33 @implementation VsyncWaiterIosTest
34
35 - (void)testSetAllowPauseAfterVsyncCorrect {
36 auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
37 VSyncClient* vsyncClient = [[[VSyncClient alloc]
38 initWithTaskRunner:thread_task_runner
39 callback:[](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {}]
40 autorelease];
41 CADisplayLink* link = [vsyncClient getDisplayLink];
42 vsyncClient.allowPauseAfterVsync = NO;
43 [vsyncClient await];
44 [vsyncClient onDisplayLink:link];
45 XCTAssertFalse(link.isPaused);
46
47 vsyncClient.allowPauseAfterVsync = YES;
48 [vsyncClient await];
49 [vsyncClient onDisplayLink:link];
50 XCTAssertTrue(link.isPaused);
51 }
52
53 - (void)testSetCorrectVariableRefreshRates {
54 auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
55 auto callback = [](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {};
56 id bundleMock = OCMPartialMock([NSBundle mainBundle]);
57 OCMStub([bundleMock objectForInfoDictionaryKey:@"CADisableMinimumFrameDurationOnPhone"])
58 .andReturn(@YES);
59 id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
60 double maxFrameRate = 120;
61 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
62
63 VSyncClient* vsyncClient = [[[VSyncClient alloc] initWithTaskRunner:thread_task_runner
64 callback:callback] autorelease];
65 CADisplayLink* link = [vsyncClient getDisplayLink];
66 if (@available(iOS 15.0, *)) {
67 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, maxFrameRate, 0.1);
68 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, maxFrameRate, 0.1);
69 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, maxFrameRate / 2, 0.1);
70 } else {
71 XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, maxFrameRate, 0.1);
72 }
73 }
74
75 - (void)testDoNotSetVariableRefreshRatesIfCADisableMinimumFrameDurationOnPhoneIsNotOn {
76 auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
77 auto callback = [](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {};
78 id bundleMock = OCMPartialMock([NSBundle mainBundle]);
79 OCMStub([bundleMock objectForInfoDictionaryKey:@"CADisableMinimumFrameDurationOnPhone"])
80 .andReturn(@NO);
81 id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
82 double maxFrameRate = 120;
83 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
84
85 VSyncClient* vsyncClient = [[[VSyncClient alloc] initWithTaskRunner:thread_task_runner
86 callback:callback] autorelease];
87 CADisplayLink* link = [vsyncClient getDisplayLink];
88 if (@available(iOS 15.0, *)) {
89 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, 0, 0.1);
90 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, 0, 0.1);
91 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, 0, 0.1);
92 } else {
93 XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, 0, 0.1);
94 }
95 }
96
97 - (void)testDoNotSetVariableRefreshRatesIfCADisableMinimumFrameDurationOnPhoneIsNotSet {
98 auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
99 auto callback = [](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {};
100 id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
101 double maxFrameRate = 120;
102 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
103 VSyncClient* vsyncClient = [[[VSyncClient alloc] initWithTaskRunner:thread_task_runner
104 callback:callback] autorelease];
105 CADisplayLink* link = [vsyncClient getDisplayLink];
106 if (@available(iOS 15.0, *)) {
107 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, 0, 0.1);
108 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, 0, 0.1);
109 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, 0, 0.1);
110 } else {
111 XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, 0, 0.1);
112 }
113 }
114
115 - (void)testAwaitAndPauseWillWorkCorrectly {
116 auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
117 VSyncClient* vsyncClient = [[[VSyncClient alloc]
118 initWithTaskRunner:thread_task_runner
119 callback:[](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {}]
120 autorelease];
121
122 CADisplayLink* link = [vsyncClient getDisplayLink];
123 XCTAssertTrue(link.isPaused);
124
125 [vsyncClient await];
126 XCTAssertFalse(link.isPaused);
127
128 [vsyncClient pause];
129 XCTAssertTrue(link.isPaused);
130 }
131
132 - (void)testRefreshRateUpdatedTo80WhenThraedsMerge {
133 auto platform_thread_task_runner = CreateNewThread("Platform");
134 auto raster_thread_task_runner = CreateNewThread("Raster");
135 auto ui_thread_task_runner = CreateNewThread("UI");
136 auto io_thread_task_runner = CreateNewThread("IO");
137 auto task_runners =
138 flutter::TaskRunners("test", platform_thread_task_runner, raster_thread_task_runner,
139 ui_thread_task_runner, io_thread_task_runner);
140
141 id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
142 double maxFrameRate = 120;
143 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
144 [[[mockDisplayLinkManager stub] andReturnValue:@(YES)] maxRefreshRateEnabledOnIPhone];
145 auto vsync_waiter = flutter::VsyncWaiterIOS(task_runners);
146
147 fml::scoped_nsobject<VSyncClient> vsyncClient = vsync_waiter.GetVsyncClient();
148 CADisplayLink* link = [vsyncClient.get() getDisplayLink];
149
150 if (@available(iOS 15.0, *)) {
151 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, maxFrameRate, 0.1);
152 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, maxFrameRate, 0.1);
153 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, maxFrameRate / 2, 0.1);
154 } else {
155 XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, maxFrameRate, 0.1);
156 }
157
158 const auto merger = fml::RasterThreadMerger::CreateOrShareThreadMerger(
159 nullptr, platform_thread_task_runner->GetTaskQueueId(),
160 raster_thread_task_runner->GetTaskQueueId());
161
162 merger->MergeWithLease(5);
163 vsync_waiter.AwaitVSync();
164
165 if (@available(iOS 15.0, *)) {
166 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, 80, 0.1);
167 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, 80, 0.1);
168 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, 60, 0.1);
169 } else {
170 XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, 80, 0.1);
171 }
172
173 merger->UnMergeNowIfLastOne();
174 vsync_waiter.AwaitVSync();
175
176 if (@available(iOS 15.0, *)) {
177 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, maxFrameRate, 0.1);
178 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, maxFrameRate, 0.1);
179 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, maxFrameRate / 2, 0.1);
180 } else {
181 XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, maxFrameRate, 0.1);
182 }
183
184 if (@available(iOS 14.0, *)) {
185 // Fake response that we are running on Mac.
186 id processInfo = [NSProcessInfo processInfo];
187 id processInfoPartialMock = OCMPartialMock(processInfo);
188 bool iOSAppOnMac = true;
189 [OCMStub([processInfoPartialMock isiOSAppOnMac]) andReturnValue:OCMOCK_VALUE(iOSAppOnMac)];
190
191 merger->MergeWithLease(5);
192 vsync_waiter.AwaitVSync();
193
194 // On Mac, framerate should be uncapped.
195 if (@available(iOS 15.0, *)) {
196 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, maxFrameRate, 0.1);
197 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, maxFrameRate, 0.1);
198 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, maxFrameRate / 2, 0.1);
199 } else {
200 XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, 80, 0.1);
201 }
202
203 merger->UnMergeNowIfLastOne();
204 vsync_waiter.AwaitVSync();
205
206 if (@available(iOS 15.0, *)) {
207 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, maxFrameRate, 0.1);
208 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, maxFrameRate, 0.1);
209 XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, maxFrameRate / 2, 0.1);
210 } else {
211 XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, maxFrameRate, 0.1);
212 }
213 }
214 }
215
216 @end
FLUTTER_ASSERT_NOT_ARC::CreateNewThread
fml::RefPtr< fml::TaskRunner > CreateNewThread(const std::string &name)
Definition: VsyncWaiterIosTest.mm:16
FLUTTER_ASSERT_NOT_ARC
Definition: VsyncWaiterIosTest.mm:15
-[VSyncClient pause]
void pause()
Definition: vsync_waiter_ios.mm:125
VsyncWaiterIosTest
Definition: VsyncWaiterIosTest.mm:30
-[VSyncClient(Testing) getDisplayLink]
CADisplayLink * getDisplayLink()
DisplayLinkManager
Definition: vsync_waiter_ios.h:16
-[VSyncClient await]
void await()
Definition: vsync_waiter_ios.mm:121
flutter::VsyncWaiterIOS
Definition: vsync_waiter_ios.h:67
FlutterMacros.h
VSyncClient::allowPauseAfterVsync
BOOL allowPauseAfterVsync
Default value is YES. Vsync client will pause vsync callback after receiving a vsync signal....
Definition: vsync_waiter_ios.h:48
VSyncClient(Testing)
Definition: FlutterViewControllerTest_mrc.mm:21
vsync_waiter_ios.h
VSyncClient
Definition: vsync_waiter_ios.h:38