-
Notifications
You must be signed in to change notification settings - Fork 6k
Remove flake inducing timeouts #25847
Changes from all commits
9af420d
e821e4f
bdb97e3
1800e04
7b7441a
26643da
fa0f6c8
52e7f7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -672,7 +672,7 @@ TEST_F(ShellTest, ExternalEmbedderNoThreadMerger) { | |
SkPaint(SkColor4f::FromColor(SK_ColorRED))); | ||
auto sk_picture = recorder.finishRecordingAsPicture(); | ||
fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>( | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); | ||
auto picture_layer = std::make_shared<PictureLayer>( | ||
SkPoint::Make(10, 10), | ||
flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false); | ||
|
@@ -727,7 +727,7 @@ TEST_F(ShellTest, | |
SkPaint(SkColor4f::FromColor(SK_ColorRED))); | ||
auto sk_picture = recorder.finishRecordingAsPicture(); | ||
fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>( | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); | ||
auto picture_layer = std::make_shared<PictureLayer>( | ||
SkPoint::Make(10, 10), | ||
flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false); | ||
|
@@ -779,7 +779,7 @@ TEST_F(ShellTest, | |
SkPaint(SkColor4f::FromColor(SK_ColorRED))); | ||
auto sk_picture = recorder.finishRecordingAsPicture(); | ||
fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>( | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); | ||
auto picture_layer = std::make_shared<PictureLayer>( | ||
SkPoint::Make(10, 10), | ||
flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false); | ||
|
@@ -788,9 +788,9 @@ TEST_F(ShellTest, | |
|
||
PumpOneFrame(shell.get(), 100, 100, builder); | ||
|
||
auto result = | ||
shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); | ||
ASSERT_TRUE(result.ok()); | ||
auto result = shell->WaitForFirstFrame(fml::TimeDelta::Max()); | ||
ASSERT_TRUE(result.ok()) << "Result: " << static_cast<int>(result.code()) | ||
<< ": " << result.message(); | ||
|
||
ASSERT_TRUE(raster_thread_merger->IsEnabled()); | ||
|
||
|
@@ -800,7 +800,6 @@ TEST_F(ShellTest, | |
// Validate the platform view can be recreated and destroyed again | ||
ValidateShell(shell.get()); | ||
ASSERT_TRUE(raster_thread_merger->IsEnabled()); | ||
|
||
DestroyShell(std::move(shell)); | ||
} | ||
|
||
|
@@ -853,7 +852,7 @@ TEST_F(ShellTest, | |
SkPaint(SkColor4f::FromColor(SK_ColorRED))); | ||
auto sk_picture = recorder.finishRecordingAsPicture(); | ||
fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>( | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); | ||
auto picture_layer = std::make_shared<PictureLayer>( | ||
SkPoint::Make(10, 10), | ||
flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false); | ||
|
@@ -928,7 +927,7 @@ TEST_F(ShellTest, | |
SkPaint(SkColor4f::FromColor(SK_ColorRED))); | ||
auto sk_picture = recorder.finishRecordingAsPicture(); | ||
fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>( | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); | ||
auto picture_layer = std::make_shared<PictureLayer>( | ||
SkPoint::Make(10, 10), | ||
flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false); | ||
|
@@ -1001,7 +1000,7 @@ TEST_F(ShellTest, | |
SkPaint(SkColor4f::FromColor(SK_ColorRED))); | ||
auto sk_picture = recorder.finishRecordingAsPicture(); | ||
fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>( | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); | ||
auto picture_layer = std::make_shared<PictureLayer>( | ||
SkPoint::Make(10, 10), | ||
flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false); | ||
|
@@ -1049,7 +1048,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWithoutRasterThreadMerger) { | |
SkPaint(SkColor4f::FromColor(SK_ColorRED))); | ||
auto sk_picture = recorder.finishRecordingAsPicture(); | ||
fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>( | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); | ||
auto picture_layer = std::make_shared<PictureLayer>( | ||
SkPoint::Make(10, 10), | ||
flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false); | ||
|
@@ -1120,7 +1119,7 @@ TEST_F(ShellTest, | |
SkPaint(SkColor4f::FromColor(SK_ColorRED))); | ||
auto sk_picture = recorder.finishRecordingAsPicture(); | ||
fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>( | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); | ||
auto picture_layer = std::make_shared<PictureLayer>( | ||
SkPoint::Make(10, 10), | ||
flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false); | ||
|
@@ -1263,57 +1262,6 @@ TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { | |
} | ||
} | ||
|
||
#if FLUTTER_RELEASE | ||
TEST_F(ShellTest, ReportTimingsIsCalledLaterInReleaseMode) { | ||
#else | ||
TEST_F(ShellTest, ReportTimingsIsCalledSoonerInNonReleaseMode) { | ||
#endif | ||
fml::TimePoint start = fml::TimePoint::Now(); | ||
auto settings = CreateSettingsForFixture(); | ||
std::unique_ptr<Shell> shell = CreateShell(settings); | ||
|
||
// Create the surface needed by rasterizer | ||
PlatformViewNotifyCreated(shell.get()); | ||
|
||
auto configuration = RunConfiguration::InferFromSettings(settings); | ||
ASSERT_TRUE(configuration.IsValid()); | ||
configuration.SetEntrypoint("reportTimingsMain"); | ||
|
||
// Wait for 2 reports: the first one is the immediate callback of the first | ||
// frame; the second one will exercise the batching logic. | ||
fml::CountDownLatch reportLatch(2); | ||
std::vector<int64_t> timestamps; | ||
auto nativeTimingCallback = [&reportLatch, | ||
×tamps](Dart_NativeArguments args) { | ||
Dart_Handle exception = nullptr; | ||
timestamps = tonic::DartConverter<std::vector<int64_t>>::FromArguments( | ||
args, 0, exception); | ||
reportLatch.CountDown(); | ||
}; | ||
AddNativeCallback("NativeReportTimingsCallback", | ||
CREATE_NATIVE_ENTRY(nativeTimingCallback)); | ||
RunEngine(shell.get(), std::move(configuration)); | ||
|
||
PumpOneFrame(shell.get()); | ||
PumpOneFrame(shell.get()); | ||
|
||
reportLatch.Wait(); | ||
DestroyShell(std::move(shell)); | ||
|
||
fml::TimePoint finish = fml::TimePoint::Now(); | ||
fml::TimeDelta elapsed = finish - start; | ||
|
||
#if FLUTTER_RELEASE | ||
// Our batch time is 1000ms. Hopefully the 800ms limit is relaxed enough to | ||
// make it not too flaky. | ||
ASSERT_TRUE(elapsed >= fml::TimeDelta::FromMilliseconds(800)); | ||
#else | ||
// Our batch time is 100ms. Hopefully the 500ms limit is relaxed enough to | ||
// make it not too flaky. | ||
ASSERT_TRUE(elapsed <= fml::TimeDelta::FromMilliseconds(500)); | ||
#endif | ||
Comment on lines
-1306
to
-1314
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like you could get what you want by just removing this assert right? That would eliminate all timeout except the one that's for the whole test. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm just realizing that's the whole point of this test. It doesn't really test much without it =T There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deleting this LGTM, I'd double check with the author of it just to see if they can think of another way to assert what they want. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Author is @liyuqian There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah the more I think about this, this either needs to be a benchmark and run as one, or it needs to mock out the clock to assert how many ticks can pass before events are emitted. |
||
} | ||
|
||
TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) { | ||
auto settings = CreateSettingsForFixture(); | ||
std::unique_ptr<Shell> shell = CreateShell(settings); | ||
|
@@ -1391,8 +1339,7 @@ TEST_F(ShellTest, WaitForFirstFrame) { | |
|
||
RunEngine(shell.get(), std::move(configuration)); | ||
PumpOneFrame(shell.get()); | ||
fml::Status result = | ||
shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); | ||
fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max()); | ||
ASSERT_TRUE(result.ok()); | ||
|
||
DestroyShell(std::move(shell)); | ||
|
@@ -1410,8 +1357,7 @@ TEST_F(ShellTest, WaitForFirstFrameZeroSizeFrame) { | |
|
||
RunEngine(shell.get(), std::move(configuration)); | ||
PumpOneFrame(shell.get(), {1.0, 0.0, 0.0}); | ||
fml::Status result = | ||
shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); | ||
fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Zero()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would make us wait forever and time out the test. I'm not sure how much value this test adds over the other test(s) that make sure we timeout if we don't wait long enough. The problem is, even if we keep it at 1s, there's no good way of knowing whether we timed out because it fails to produce a frame by design, or we timed out because we were running on hardware slow enough that 1s isn't long enough to produce a frame. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @iskakaushik is in the blame for this, might have ideas. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, sorry, I missed the ASSERT below this line saying that the wait is expected to timeout. So, is this testing that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding is that this test wants to assert that I think a better place to test this would be e.g. in the animator unittests, making sure that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (that's where the shell would flip the wait condition) |
||
ASSERT_FALSE(result.ok()); | ||
ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); | ||
|
||
|
@@ -1429,8 +1375,7 @@ TEST_F(ShellTest, WaitForFirstFrameTimeout) { | |
configuration.SetEntrypoint("emptyMain"); | ||
|
||
RunEngine(shell.get(), std::move(configuration)); | ||
fml::Status result = | ||
shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(10)); | ||
fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Zero()); | ||
ASSERT_FALSE(result.ok()); | ||
ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); | ||
|
||
|
@@ -1449,11 +1394,10 @@ TEST_F(ShellTest, WaitForFirstFrameMultiple) { | |
|
||
RunEngine(shell.get(), std::move(configuration)); | ||
PumpOneFrame(shell.get()); | ||
fml::Status result = | ||
shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); | ||
fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max()); | ||
ASSERT_TRUE(result.ok()); | ||
for (int i = 0; i < 100; ++i) { | ||
result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1)); | ||
result = shell->WaitForFirstFrame(fml::TimeDelta::Zero()); | ||
ASSERT_TRUE(result.ok()); | ||
} | ||
|
||
|
@@ -1480,13 +1424,12 @@ TEST_F(ShellTest, WaitForFirstFrameInlined) { | |
PumpOneFrame(shell.get()); | ||
fml::AutoResetWaitableEvent event; | ||
task_runner->PostTask([&shell, &event] { | ||
fml::Status result = | ||
shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); | ||
fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max()); | ||
ASSERT_FALSE(result.ok()); | ||
ASSERT_EQ(result.code(), fml::StatusCode::kFailedPrecondition); | ||
event.Signal(); | ||
}); | ||
ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(1000))); | ||
ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::Max())); | ||
|
||
DestroyShell(std::move(shell), std::move(task_runners)); | ||
} | ||
|
@@ -1815,7 +1758,7 @@ TEST_F(ShellTest, Screenshot) { | |
SkPaint(SkColor4f::FromColor(SK_ColorRED))); | ||
auto sk_picture = recorder.finishRecordingAsPicture(); | ||
fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>( | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); | ||
this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); | ||
auto picture_layer = std::make_shared<PictureLayer>( | ||
SkPoint::Make(10, 10), | ||
flutter::SkiaGPUObject<SkPicture>({sk_picture, queue}), false, false); | ||
|
@@ -2105,7 +2048,7 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { | |
// 1. Construct a picture and a picture layer to be raster cached. | ||
sk_sp<SkPicture> picture = MakeSizedPicture(10, 10); | ||
fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>( | ||
GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); | ||
GetCurrentTaskRunner(), fml::TimeDelta::Zero()); | ||
auto picture_layer = std::make_shared<PictureLayer>( | ||
SkPoint::Make(0, 0), | ||
flutter::SkiaGPUObject<SkPicture>({MakeSizedPicture(100, 100), queue}), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gaaclarke - mind taking another look at this?
I had to change this because otherwise we're susceptible to overflows when adding
timeout
tostd::chrono::steady_clock::now()
.wait_for
does that addition behind the scenes and I guess treats the overflow as UB. I'm using wait_until to avoid the steady clock advancing while we do other calcuations and overflowing even though we checked.This is covered by tests now that the timeouts use max/zero values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, this is an actual bug for us today - if someone were to pass a very large value into here, they'd get a timeout response due to overflow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It sounds like the API docs in the header file here https://github.com/flutter/engine/blob/master/shell/common/shell.h#L301 could use some more details or a warning about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a doc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. I think an easier fix would just be to say
auto duration = min(max_duration, desired_duration);
and use wait_for. Since that time is so long, no one will ever be able to tell the difference in their lifetime. That removes a branch and is much easier to verify it is correct assuming what was there before was correct.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Though technically isn't std::min a branch? :) Anyway I agree the code is more readable/maintainable this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh. We can't do that.
wait_for
is still going to add tostead_clock::now()
, which has incremented since checked for overflow, and now might overflow again.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could do this:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds good to me too, as long as there isn't that branch I think it's more straightforward.