blob: dda84f9ac158e0bf0e93f2dc46496da2c3550a82 [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_NOT_ARC
namespace {
fml::RefPtr<fml::TaskRunner> CreateNewThread(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) {}]
autorelease];
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);
[vsyncClient release];
}
- (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] autorelease];
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);
}
[vsyncClient release];
}
- (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] autorelease];
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);
}
[vsyncClient release];
}
- (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] autorelease];
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);
}
[vsyncClient release];
}
- (void)testAwaitAndPauseWillWorkCorrectly {
auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
VSyncClient* vsyncClient = [[[VSyncClient alloc]
initWithTaskRunner:thread_task_runner
callback:[](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {}]
autorelease];
CADisplayLink* link = [vsyncClient getDisplayLink];
XCTAssertTrue(link.isPaused);
[vsyncClient await];
XCTAssertFalse(link.isPaused);
[vsyncClient pause];
XCTAssertTrue(link.isPaused);
[vsyncClient release];
}
- (void)testRefreshRateUpdatedTo80WhenThraedsMerge {
auto platform_thread_task_runner = CreateNewThread("Platform");
auto raster_thread_task_runner = CreateNewThread("Raster");
auto ui_thread_task_runner = CreateNewThread("UI");
auto io_thread_task_runner = CreateNewThread("IO");
auto task_runners =
flutter::TaskRunners("test", platform_thread_task_runner, raster_thread_task_runner,
ui_thread_task_runner, io_thread_task_runner);
id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
double maxFrameRate = 120;
[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
[[[mockDisplayLinkManager stub] andReturnValue:@(YES)] maxRefreshRateEnabledOnIPhone];
auto vsync_waiter = flutter::VsyncWaiterIOS(task_runners);
fml::scoped_nsobject<VSyncClient> vsyncClient = vsync_waiter.GetVsyncClient();
CADisplayLink* link = [vsyncClient.get() 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);
}
const auto merger = fml::RasterThreadMerger::CreateOrShareThreadMerger(
nullptr, platform_thread_task_runner->GetTaskQueueId(),
raster_thread_task_runner->GetTaskQueueId());
merger->MergeWithLease(5);
vsync_waiter.AwaitVSync();
if (@available(iOS 15.0, *)) {
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.maximum, 80, 0.1);
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.preferred, 80, 0.1);
XCTAssertEqualWithAccuracy(link.preferredFrameRateRange.minimum, 60, 0.1);
} else {
XCTAssertEqualWithAccuracy(link.preferredFramesPerSecond, 80, 0.1);
}
merger->UnMergeNowIfLastOne();
vsync_waiter.AwaitVSync();
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);
}
}
@end