blob: cbe58384f6fdf3acfd477c1b0614a4ac04cee5af [file] [log] [blame]
// 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/fml/raster_thread_merger.h"
#include "flutter/fml/thread.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
FLUTTER_ASSERT_ARC
namespace {
fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
auto thread = std::make_unique<fml::Thread>(name);
auto runner = thread->GetTaskRunner();
return runner;
}
} // namespace
@interface VSyncClient (Testing)
- (CADisplayLink*)getDisplayLink;
- (void)onDisplayLink:(CADisplayLink*)link;
@end
@interface VsyncWaiterIosTest : XCTestCase
@end
@implementation VsyncWaiterIosTest
- (void)testSetAllowPauseAfterVsyncCorrect {
auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
VSyncClient* vsyncClient = [[VSyncClient alloc]
initWithTaskRunner:thread_task_runner
callback:[](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {}];
CADisplayLink* link = [vsyncClient getDisplayLink];
vsyncClient.allowPauseAfterVsync = NO;
[vsyncClient await];
[vsyncClient onDisplayLink:link];
XCTAssertFalse(link.isPaused);
vsyncClient.allowPauseAfterVsync = YES;
[vsyncClient await];
[vsyncClient onDisplayLink:link];
XCTAssertTrue(link.isPaused);
}
- (void)testSetCorrectVariableRefreshRates {
auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
auto callback = [](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {};
id bundleMock = OCMPartialMock([NSBundle mainBundle]);
OCMStub([bundleMock objectForInfoDictionaryKey:@"CADisableMinimumFrameDurationOnPhone"])
.andReturn(@YES);
id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
double maxFrameRate = 120;
[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
VSyncClient* vsyncClient = [[VSyncClient alloc] initWithTaskRunner:thread_task_runner
callback:callback];
CADisplayLink* link = [vsyncClient getDisplayLink];
if (@available(iOS 15.0, *)) {
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, maxFrameRate, 0.1);
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, maxFrameRate, 0.1);
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, maxFrameRate / 2, 0.1);
} else {
XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, maxFrameRate, 0.1);
}
}
- (void)testDoNotSetVariableRefreshRatesIfCADisableMinimumFrameDurationOnPhoneIsNotOn {
auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
auto callback = [](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {};
id bundleMock = OCMPartialMock([NSBundle mainBundle]);
OCMStub([bundleMock objectForInfoDictionaryKey:@"CADisableMinimumFrameDurationOnPhone"])
.andReturn(@NO);
id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
double maxFrameRate = 120;
[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
VSyncClient* vsyncClient = [[VSyncClient alloc] initWithTaskRunner:thread_task_runner
callback:callback];
CADisplayLink* link = [vsyncClient getDisplayLink];
if (@available(iOS 15.0, *)) {
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, 0, 0.1);
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, 0, 0.1);
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, 0, 0.1);
} else {
XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, 0, 0.1);
}
}
- (void)testDoNotSetVariableRefreshRatesIfCADisableMinimumFrameDurationOnPhoneIsNotSet {
auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
auto callback = [](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {};
id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
double maxFrameRate = 120;
[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
VSyncClient* vsyncClient = [[VSyncClient alloc] initWithTaskRunner:thread_task_runner
callback:callback];
CADisplayLink* link = [vsyncClient getDisplayLink];
if (@available(iOS 15.0, *)) {
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, 0, 0.1);
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, 0, 0.1);
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, 0, 0.1);
} else {
XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, 0, 0.1);
}
}
- (void)testAwaitAndPauseWillWorkCorrectly {
auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
VSyncClient* vsyncClient = [[VSyncClient alloc]
initWithTaskRunner:thread_task_runner
callback:[](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {}];
CADisplayLink* link = [vsyncClient getDisplayLink];
XCTAssertTrue(link.isPaused);
[vsyncClient await];
XCTAssertFalse(link.isPaused);
[vsyncClient pause];
XCTAssertTrue(link.isPaused);
}
- (void)testReleasesLinkOnInvalidation {
__weak CADisplayLink* weakLink;
@autoreleasepool {
auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
VSyncClient* vsyncClient = [[VSyncClient alloc]
initWithTaskRunner:thread_task_runner
callback:[](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {}];
weakLink = [vsyncClient getDisplayLink];
XCTAssertNotNil(weakLink);
[vsyncClient invalidate];
}
// VSyncClient has released the CADisplayLink.
XCTAssertNil(weakLink);
}
@end