|  | // 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. | 
|  |  | 
|  | #include "flutter/shell/common/engine.h" | 
|  |  | 
|  | // #include <cstring> | 
|  |  | 
|  | #include "flutter/common/constants.h" | 
|  | #include "flutter/lib/ui/compositing/scene_builder.h" | 
|  | #include "flutter/shell/common/shell_test.h" | 
|  | #include "flutter/testing/fixture_test.h" | 
|  | #include "gmock/gmock.h" | 
|  |  | 
|  | // CREATE_NATIVE_ENTRY is leaky by design | 
|  | // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape) | 
|  |  | 
|  | namespace flutter { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using ::testing::Invoke; | 
|  | using ::testing::ReturnRef; | 
|  |  | 
|  | fml::AutoResetWaitableEvent native_latch; | 
|  |  | 
|  | void PostSync(const fml::RefPtr<fml::TaskRunner>& task_runner, | 
|  | const fml::closure& task) { | 
|  | fml::AutoResetWaitableEvent latch; | 
|  | fml::TaskRunner::RunNowOrPostTask(task_runner, [&latch, &task] { | 
|  | task(); | 
|  | latch.Signal(); | 
|  | }); | 
|  | latch.Wait(); | 
|  | } | 
|  |  | 
|  | // Sort the argument list of `LayerTreeTask` into a new list that is sorted by | 
|  | // their view IDs. `FrameItem::layer_tree_tasks` might not come sorted. | 
|  | std::vector<const LayerTreeTask*> Sorted( | 
|  | const std::vector<std::unique_ptr<LayerTreeTask>>& layer_tree_tasks) { | 
|  | std::vector<const LayerTreeTask*> result; | 
|  | result.reserve(layer_tree_tasks.size()); | 
|  | for (auto& task_ptr : layer_tree_tasks) { | 
|  | result.push_back(task_ptr.get()); | 
|  | } | 
|  | std::sort(result.begin(), result.end(), | 
|  | [](const LayerTreeTask* a, const LayerTreeTask* b) { | 
|  | return a->view_id < b->view_id; | 
|  | }); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | class MockDelegate : public Engine::Delegate { | 
|  | public: | 
|  | MOCK_METHOD(void, | 
|  | OnEngineUpdateSemantics, | 
|  | (SemanticsNodeUpdates, CustomAccessibilityActionUpdates), | 
|  | (override)); | 
|  | MOCK_METHOD(void, | 
|  | OnEngineHandlePlatformMessage, | 
|  | (std::unique_ptr<PlatformMessage>), | 
|  | (override)); | 
|  | MOCK_METHOD(void, OnPreEngineRestart, (), (override)); | 
|  | MOCK_METHOD(void, OnRootIsolateCreated, (), (override)); | 
|  | MOCK_METHOD(void, | 
|  | UpdateIsolateDescription, | 
|  | (const std::string, int64_t), | 
|  | (override)); | 
|  | MOCK_METHOD(void, SetNeedsReportTimings, (bool), (override)); | 
|  | MOCK_METHOD(std::unique_ptr<std::vector<std::string>>, | 
|  | ComputePlatformResolvedLocale, | 
|  | (const std::vector<std::string>&), | 
|  | (override)); | 
|  | MOCK_METHOD(void, RequestDartDeferredLibrary, (intptr_t), (override)); | 
|  | MOCK_METHOD(fml::TimePoint, GetCurrentTimePoint, (), (override)); | 
|  | MOCK_METHOD(const std::shared_ptr<PlatformMessageHandler>&, | 
|  | GetPlatformMessageHandler, | 
|  | (), | 
|  | (const, override)); | 
|  | MOCK_METHOD(void, OnEngineChannelUpdate, (std::string, bool), (override)); | 
|  | MOCK_METHOD(double, | 
|  | GetScaledFontSize, | 
|  | (double font_size, int configuration_id), | 
|  | (const, override)); | 
|  | }; | 
|  |  | 
|  | class MockAnimatorDelegate : public Animator::Delegate { | 
|  | public: | 
|  | /* Animator::Delegate */ | 
|  | MOCK_METHOD(void, | 
|  | OnAnimatorBeginFrame, | 
|  | (fml::TimePoint frame_target_time, uint64_t frame_number), | 
|  | (override)); | 
|  | MOCK_METHOD(void, | 
|  | OnAnimatorNotifyIdle, | 
|  | (fml::TimeDelta deadline), | 
|  | (override)); | 
|  | MOCK_METHOD(void, | 
|  | OnAnimatorUpdateLatestFrameTargetTime, | 
|  | (fml::TimePoint frame_target_time), | 
|  | (override)); | 
|  | MOCK_METHOD(void, | 
|  | OnAnimatorDraw, | 
|  | (std::shared_ptr<FramePipeline> pipeline), | 
|  | (override)); | 
|  | MOCK_METHOD(void, | 
|  | OnAnimatorDrawLastLayerTrees, | 
|  | (std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder), | 
|  | (override)); | 
|  | }; | 
|  |  | 
|  | class MockPlatformMessageHandler : public PlatformMessageHandler { | 
|  | public: | 
|  | MOCK_METHOD(void, | 
|  | HandlePlatformMessage, | 
|  | (std::unique_ptr<PlatformMessage> message), | 
|  | (override)); | 
|  | MOCK_METHOD(bool, | 
|  | DoesHandlePlatformMessageOnPlatformThread, | 
|  | (), | 
|  | (const, override)); | 
|  | MOCK_METHOD(void, | 
|  | InvokePlatformMessageResponseCallback, | 
|  | (int response_id, std::unique_ptr<fml::Mapping> mapping), | 
|  | (override)); | 
|  | MOCK_METHOD(void, | 
|  | InvokePlatformMessageEmptyResponseCallback, | 
|  | (int response_id), | 
|  | (override)); | 
|  | }; | 
|  |  | 
|  | class EngineAnimatorTest : public testing::FixtureTest { | 
|  | public: | 
|  | EngineAnimatorTest() | 
|  | : thread_host_("EngineAnimatorTest", | 
|  | ThreadHost::Type::kPlatform | ThreadHost::Type::kIo | | 
|  | ThreadHost::Type::kUi | ThreadHost::Type::kRaster), | 
|  | task_runners_({ | 
|  | "EngineAnimatorTest", | 
|  | thread_host_.platform_thread->GetTaskRunner(),  // platform | 
|  | thread_host_.raster_thread->GetTaskRunner(),    // raster | 
|  | thread_host_.ui_thread->GetTaskRunner(),        // ui | 
|  | thread_host_.io_thread->GetTaskRunner()         // io | 
|  | }) {} | 
|  |  | 
|  | void PostUITaskSync(const std::function<void()>& function) { | 
|  | fml::AutoResetWaitableEvent latch; | 
|  | task_runners_.GetUITaskRunner()->PostTask([&] { | 
|  | function(); | 
|  | latch.Signal(); | 
|  | }); | 
|  | latch.Wait(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | void SetUp() override { | 
|  | settings_ = CreateSettingsForFixture(); | 
|  | dispatcher_maker_ = [](PointerDataDispatcher::Delegate&) { | 
|  | return nullptr; | 
|  | }; | 
|  | } | 
|  |  | 
|  | MockDelegate delegate_; | 
|  | PointerDataDispatcherMaker dispatcher_maker_; | 
|  | ThreadHost thread_host_; | 
|  | TaskRunners task_runners_; | 
|  | Settings settings_; | 
|  | std::unique_ptr<Animator> animator_; | 
|  | fml::WeakPtr<IOManager> io_manager_; | 
|  | std::unique_ptr<RuntimeController> runtime_controller_; | 
|  | std::shared_ptr<fml::ConcurrentTaskRunner> image_decoder_task_runner_; | 
|  | fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate_; | 
|  | }; | 
|  |  | 
|  | // A class that can launch an Engine with the specified Engine::Delegate. | 
|  | // | 
|  | // To use this class, contruct this class with Create, call Run, and use the | 
|  | // engine with EngineTaskSync(). | 
|  | class EngineContext { | 
|  | public: | 
|  | using EngineCallback = std::function<void(Engine&)>; | 
|  |  | 
|  | [[nodiscard]] static std::unique_ptr<EngineContext> Create( | 
|  | Engine::Delegate& delegate,       // | 
|  | Settings settings,                // | 
|  | const TaskRunners& task_runners,  // | 
|  | std::unique_ptr<Animator> animator) { | 
|  | auto [vm, isolate_snapshot] = Shell::InferVmInitDataFromSettings(settings); | 
|  | FML_CHECK(vm) << "Must be able to initialize the VM."; | 
|  | // Construct the class with `new` because `make_unique` has no access to the | 
|  | // private constructor. | 
|  | EngineContext* raw_pointer = | 
|  | new EngineContext(delegate, settings, task_runners, std::move(animator), | 
|  | vm, isolate_snapshot); | 
|  | return std::unique_ptr<EngineContext>(raw_pointer); | 
|  | } | 
|  |  | 
|  | void Run(RunConfiguration configuration) { | 
|  | PostSync(task_runners_.GetUITaskRunner(), [this, &configuration] { | 
|  | Engine::RunStatus run_status = engine_->Run(std::move(configuration)); | 
|  | FML_CHECK(run_status == Engine::RunStatus::Success) | 
|  | << "Engine failed to run."; | 
|  | (void)run_status;  // Suppress unused-variable warning | 
|  | }); | 
|  | } | 
|  |  | 
|  | // Run a task that operates the Engine on the UI thread, and wait for the | 
|  | // task to end. | 
|  | // | 
|  | // If called on the UI thread, the task is executed synchronously. | 
|  | void EngineTaskSync(EngineCallback task) { | 
|  | ASSERT_TRUE(engine_); | 
|  | ASSERT_TRUE(task); | 
|  | auto runner = task_runners_.GetUITaskRunner(); | 
|  | if (runner->RunsTasksOnCurrentThread()) { | 
|  | task(*engine_); | 
|  | } else { | 
|  | PostSync(task_runners_.GetUITaskRunner(), [&]() { task(*engine_); }); | 
|  | } | 
|  | } | 
|  |  | 
|  | ~EngineContext() { | 
|  | PostSync(task_runners_.GetUITaskRunner(), [this] { engine_.reset(); }); | 
|  | } | 
|  |  | 
|  | private: | 
|  | EngineContext(Engine::Delegate& delegate,          // | 
|  | Settings settings,                   // | 
|  | const TaskRunners& task_runners,     // | 
|  | std::unique_ptr<Animator> animator,  // | 
|  | const DartVMRef& vm,                 // | 
|  | fml::RefPtr<const DartSnapshot> isolate_snapshot) | 
|  | : task_runners_(task_runners), vm_(vm) { | 
|  | PostSync(task_runners.GetUITaskRunner(), [this, &settings, &animator, | 
|  | &delegate, &isolate_snapshot] { | 
|  | auto dispatcher_maker = | 
|  | [](DefaultPointerDataDispatcher::Delegate& delegate) { | 
|  | return std::make_unique<DefaultPointerDataDispatcher>(delegate); | 
|  | }; | 
|  | engine_ = std::make_unique<Engine>( | 
|  | /*delegate=*/delegate, | 
|  | /*dispatcher_maker=*/dispatcher_maker, | 
|  | /*vm=*/*&vm_, | 
|  | /*isolate_snapshot=*/std::move(isolate_snapshot), | 
|  | /*task_runners=*/task_runners_, | 
|  | /*platform_data=*/PlatformData(), | 
|  | /*settings=*/settings, | 
|  | /*animator=*/std::move(animator), | 
|  | /*io_manager=*/io_manager_, | 
|  | /*unref_queue=*/nullptr, | 
|  | /*snapshot_delegate=*/snapshot_delegate_, | 
|  | /*volatile_path_tracker=*/nullptr, | 
|  | /*gpu_disabled_switch=*/std::make_shared<fml::SyncSwitch>()); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TaskRunners task_runners_; | 
|  | DartVMRef vm_; | 
|  | std::unique_ptr<Engine> engine_; | 
|  |  | 
|  | fml::WeakPtr<IOManager> io_manager_; | 
|  | fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate_; | 
|  | }; | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(EngineAnimatorTest, AnimatorAcceptsMultipleRenders) { | 
|  | MockAnimatorDelegate animator_delegate; | 
|  | std::unique_ptr<EngineContext> engine_context; | 
|  |  | 
|  | std::shared_ptr<PlatformMessageHandler> platform_message_handler = | 
|  | std::make_shared<MockPlatformMessageHandler>(); | 
|  | EXPECT_CALL(delegate_, GetPlatformMessageHandler) | 
|  | .WillOnce(ReturnRef(platform_message_handler)); | 
|  | fml::AutoResetWaitableEvent draw_latch; | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorDraw) | 
|  | .WillOnce( | 
|  | Invoke([&draw_latch](const std::shared_ptr<FramePipeline>& pipeline) { | 
|  | auto status = | 
|  | pipeline->Consume([&](std::unique_ptr<FrameItem> item) { | 
|  | auto tasks = Sorted(item->layer_tree_tasks); | 
|  | EXPECT_EQ(tasks.size(), 2u); | 
|  | EXPECT_EQ(tasks[0]->view_id, 1); | 
|  | EXPECT_EQ(tasks[1]->view_id, 2); | 
|  | }); | 
|  | EXPECT_EQ(status, PipelineConsumeResult::Done); | 
|  | draw_latch.Signal(); | 
|  | })); | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame) | 
|  | .WillOnce(Invoke([&engine_context](fml::TimePoint frame_target_time, | 
|  | uint64_t frame_number) { | 
|  | engine_context->EngineTaskSync([&](Engine& engine) { | 
|  | engine.BeginFrame(frame_target_time, frame_number); | 
|  | }); | 
|  | })); | 
|  |  | 
|  | native_latch.Reset(); | 
|  | AddNativeCallback("NotifyNative", [](auto args) { native_latch.Signal(); }); | 
|  |  | 
|  | std::unique_ptr<Animator> animator; | 
|  | PostSync(task_runners_.GetUITaskRunner(), | 
|  | [&animator, &animator_delegate, &task_runners = task_runners_] { | 
|  | animator = std::make_unique<Animator>( | 
|  | animator_delegate, task_runners, | 
|  | static_cast<std::unique_ptr<VsyncWaiter>>( | 
|  | std::make_unique<testing::ConstantFiringVsyncWaiter>( | 
|  | task_runners))); | 
|  | }); | 
|  |  | 
|  | engine_context = EngineContext::Create(delegate_, settings_, task_runners_, | 
|  | std::move(animator)); | 
|  | auto configuration = RunConfiguration::InferFromSettings(settings_); | 
|  | configuration.SetEntrypoint("onDrawFrameRenderAllViews"); | 
|  | engine_context->Run(std::move(configuration)); | 
|  |  | 
|  | engine_context->EngineTaskSync([](Engine& engine) { | 
|  | engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0}, | 
|  | [](bool added) { ASSERT_TRUE(added); }); | 
|  | engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0}, | 
|  | [](bool added) { ASSERT_TRUE(added); }); | 
|  | }); | 
|  |  | 
|  | native_latch.Wait(); | 
|  |  | 
|  | engine_context->EngineTaskSync( | 
|  | [](Engine& engine) { engine.ScheduleFrame(); }); | 
|  | draw_latch.Wait(); | 
|  | } | 
|  |  | 
|  | TEST_F(EngineAnimatorTest, IgnoresOutOfFrameRenders) { | 
|  | MockAnimatorDelegate animator_delegate; | 
|  | std::unique_ptr<EngineContext> engine_context; | 
|  |  | 
|  | std::shared_ptr<PlatformMessageHandler> platform_message_handler = | 
|  | std::make_shared<MockPlatformMessageHandler>(); | 
|  | EXPECT_CALL(delegate_, GetPlatformMessageHandler) | 
|  | .WillOnce(ReturnRef(platform_message_handler)); | 
|  | fml::AutoResetWaitableEvent draw_latch; | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorDraw) | 
|  | .WillOnce( | 
|  | Invoke([&draw_latch](const std::shared_ptr<FramePipeline>& pipeline) { | 
|  | auto status = | 
|  | pipeline->Consume([&](std::unique_ptr<FrameItem> item) { | 
|  | // View 1 is rendered before the frame, and is ignored. | 
|  | // View 2 is rendered within the frame, and is accepted. | 
|  | EXPECT_EQ(item->layer_tree_tasks.size(), 1u); | 
|  | EXPECT_EQ(item->layer_tree_tasks[0]->view_id, 2); | 
|  | }); | 
|  | EXPECT_EQ(status, PipelineConsumeResult::Done); | 
|  | draw_latch.Signal(); | 
|  | })); | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame) | 
|  | .WillOnce(Invoke([&engine_context](fml::TimePoint frame_target_time, | 
|  | uint64_t frame_number) { | 
|  | engine_context->EngineTaskSync([&](Engine& engine) { | 
|  | engine.BeginFrame(frame_target_time, frame_number); | 
|  | }); | 
|  | })); | 
|  |  | 
|  | std::unique_ptr<Animator> animator; | 
|  | PostSync(task_runners_.GetUITaskRunner(), | 
|  | [&animator, &animator_delegate, &task_runners = task_runners_] { | 
|  | animator = std::make_unique<Animator>( | 
|  | animator_delegate, task_runners, | 
|  | static_cast<std::unique_ptr<VsyncWaiter>>( | 
|  | std::make_unique<testing::ConstantFiringVsyncWaiter>( | 
|  | task_runners))); | 
|  | }); | 
|  |  | 
|  | engine_context = EngineContext::Create(delegate_, settings_, task_runners_, | 
|  | std::move(animator)); | 
|  |  | 
|  | engine_context->EngineTaskSync([](Engine& engine) { | 
|  | engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0}, | 
|  | [](bool added) { ASSERT_TRUE(added); }); | 
|  | engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0}, | 
|  | [](bool added) { ASSERT_TRUE(added); }); | 
|  | }); | 
|  |  | 
|  | auto configuration = RunConfiguration::InferFromSettings(settings_); | 
|  | configuration.SetEntrypoint("renderViewsInFrameAndOutOfFrame"); | 
|  | engine_context->Run(std::move(configuration)); | 
|  |  | 
|  | draw_latch.Wait(); | 
|  | } | 
|  |  | 
|  | TEST_F(EngineAnimatorTest, IgnoresDuplicateRenders) { | 
|  | MockAnimatorDelegate animator_delegate; | 
|  | std::unique_ptr<EngineContext> engine_context; | 
|  |  | 
|  | std::vector<std::shared_ptr<Layer>> benchmark_layers; | 
|  | auto capture_root_layer = [&benchmark_layers](Dart_NativeArguments args) { | 
|  | auto handle = Dart_GetNativeArgument(args, 0); | 
|  | intptr_t peer = 0; | 
|  | Dart_Handle result = Dart_GetNativeInstanceField( | 
|  | handle, tonic::DartWrappable::kPeerIndex, &peer); | 
|  | ASSERT_FALSE(Dart_IsError(result)); | 
|  | SceneBuilder* scene_builder = reinterpret_cast<SceneBuilder*>(peer); | 
|  | ASSERT_TRUE(scene_builder); | 
|  | std::shared_ptr<ContainerLayer> root_layer = | 
|  | scene_builder->layer_stack()[0]; | 
|  | ASSERT_TRUE(root_layer); | 
|  | benchmark_layers = root_layer->layers(); | 
|  | }; | 
|  |  | 
|  | std::shared_ptr<PlatformMessageHandler> platform_message_handler = | 
|  | std::make_shared<MockPlatformMessageHandler>(); | 
|  | EXPECT_CALL(delegate_, GetPlatformMessageHandler) | 
|  | .WillOnce(ReturnRef(platform_message_handler)); | 
|  | fml::AutoResetWaitableEvent draw_latch; | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorDraw) | 
|  | .WillOnce(Invoke([&draw_latch, &benchmark_layers]( | 
|  | const std::shared_ptr<FramePipeline>& pipeline) { | 
|  | auto status = pipeline->Consume([&](std::unique_ptr<FrameItem> item) { | 
|  | EXPECT_EQ(item->layer_tree_tasks.size(), 1u); | 
|  | EXPECT_EQ(item->layer_tree_tasks[0]->view_id, kFlutterImplicitViewId); | 
|  | ContainerLayer* root_layer = reinterpret_cast<ContainerLayer*>( | 
|  | item->layer_tree_tasks[0]->layer_tree->root_layer()); | 
|  | std::vector<std::shared_ptr<Layer>> result_layers = | 
|  | root_layer->layers(); | 
|  | EXPECT_EQ(result_layers.size(), benchmark_layers.size()); | 
|  | EXPECT_EQ(result_layers[0], benchmark_layers[0]); | 
|  | }); | 
|  | EXPECT_EQ(status, PipelineConsumeResult::Done); | 
|  | draw_latch.Signal(); | 
|  | })); | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame) | 
|  | .WillOnce(Invoke([&engine_context](fml::TimePoint frame_target_time, | 
|  | uint64_t frame_number) { | 
|  | engine_context->EngineTaskSync([&](Engine& engine) { | 
|  | engine.BeginFrame(frame_target_time, frame_number); | 
|  | }); | 
|  | })); | 
|  |  | 
|  | AddNativeCallback("CaptureRootLayer", | 
|  | CREATE_NATIVE_ENTRY(capture_root_layer)); | 
|  |  | 
|  | std::unique_ptr<Animator> animator; | 
|  | PostSync(task_runners_.GetUITaskRunner(), | 
|  | [&animator, &animator_delegate, &task_runners = task_runners_] { | 
|  | animator = std::make_unique<Animator>( | 
|  | animator_delegate, task_runners, | 
|  | static_cast<std::unique_ptr<VsyncWaiter>>( | 
|  | std::make_unique<testing::ConstantFiringVsyncWaiter>( | 
|  | task_runners))); | 
|  | }); | 
|  |  | 
|  | engine_context = EngineContext::Create(delegate_, settings_, task_runners_, | 
|  | std::move(animator)); | 
|  |  | 
|  | engine_context->EngineTaskSync([](Engine& engine) { | 
|  | engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1, 10, 10, 22, 0}, | 
|  | [](bool added) { ASSERT_TRUE(added); }); | 
|  | }); | 
|  |  | 
|  | auto configuration = RunConfiguration::InferFromSettings(settings_); | 
|  | configuration.SetEntrypoint("renderTwiceForOneView"); | 
|  | engine_context->Run(std::move(configuration)); | 
|  |  | 
|  | draw_latch.Wait(); | 
|  | } | 
|  |  | 
|  | TEST_F(EngineAnimatorTest, AnimatorSubmitsImplicitViewBeforeDrawFrameEnds) { | 
|  | MockAnimatorDelegate animator_delegate; | 
|  | std::unique_ptr<EngineContext> engine_context; | 
|  |  | 
|  | std::shared_ptr<PlatformMessageHandler> platform_message_handler = | 
|  | std::make_shared<MockPlatformMessageHandler>(); | 
|  | EXPECT_CALL(delegate_, GetPlatformMessageHandler) | 
|  | .WillOnce(ReturnRef(platform_message_handler)); | 
|  |  | 
|  | bool rasterization_started = false; | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorDraw) | 
|  | .WillOnce(Invoke([&rasterization_started]( | 
|  | const std::shared_ptr<FramePipeline>& pipeline) { | 
|  | rasterization_started = true; | 
|  | auto status = pipeline->Consume([&](std::unique_ptr<FrameItem> item) { | 
|  | EXPECT_EQ(item->layer_tree_tasks.size(), 1u); | 
|  | EXPECT_EQ(item->layer_tree_tasks[0]->view_id, kFlutterImplicitViewId); | 
|  | }); | 
|  | EXPECT_EQ(status, PipelineConsumeResult::Done); | 
|  | })); | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame) | 
|  | .WillRepeatedly(Invoke([&engine_context](fml::TimePoint frame_target_time, | 
|  | uint64_t frame_number) { | 
|  | engine_context->EngineTaskSync([&](Engine& engine) { | 
|  | engine.BeginFrame(frame_target_time, frame_number); | 
|  | }); | 
|  | })); | 
|  |  | 
|  | std::unique_ptr<Animator> animator; | 
|  | PostSync(task_runners_.GetUITaskRunner(), | 
|  | [&animator, &animator_delegate, &task_runners = task_runners_] { | 
|  | animator = std::make_unique<Animator>( | 
|  | animator_delegate, task_runners, | 
|  | static_cast<std::unique_ptr<VsyncWaiter>>( | 
|  | std::make_unique<testing::ConstantFiringVsyncWaiter>( | 
|  | task_runners))); | 
|  | }); | 
|  |  | 
|  | native_latch.Reset(); | 
|  | // The native_latch is signaled at the end of handleDrawFrame. | 
|  | AddNativeCallback("NotifyNative", | 
|  | CREATE_NATIVE_ENTRY([&rasterization_started](auto args) { | 
|  | EXPECT_EQ(rasterization_started, true); | 
|  | native_latch.Signal(); | 
|  | })); | 
|  |  | 
|  | engine_context = EngineContext::Create(delegate_, settings_, task_runners_, | 
|  | std::move(animator)); | 
|  |  | 
|  | engine_context->EngineTaskSync([](Engine& engine) { | 
|  | engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1.0, 10, 10, 1, 0}, | 
|  | [](bool added) { ASSERT_TRUE(added); }); | 
|  | }); | 
|  |  | 
|  | auto configuration = RunConfiguration::InferFromSettings(settings_); | 
|  | configuration.SetEntrypoint("renderSingleViewAndCallAfterOnDrawFrame"); | 
|  | engine_context->Run(std::move(configuration)); | 
|  |  | 
|  | native_latch.Wait(); | 
|  | } | 
|  |  | 
|  | // The animator should submit to the pipeline the implicit view rendered in a | 
|  | // warm up frame if there's already a continuation (i.e. Animator::BeginFrame | 
|  | // has been called) | 
|  | TEST_F(EngineAnimatorTest, AnimatorSubmitWarmUpImplicitView) { | 
|  | MockAnimatorDelegate animator_delegate; | 
|  | std::unique_ptr<EngineContext> engine_context; | 
|  |  | 
|  | std::shared_ptr<PlatformMessageHandler> platform_message_handler = | 
|  | std::make_shared<MockPlatformMessageHandler>(); | 
|  | EXPECT_CALL(delegate_, GetPlatformMessageHandler) | 
|  | .WillOnce(ReturnRef(platform_message_handler)); | 
|  |  | 
|  | fml::AutoResetWaitableEvent continuation_ready_latch; | 
|  | fml::AutoResetWaitableEvent draw_latch; | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorDraw) | 
|  | .WillOnce(Invoke([&draw_latch]( | 
|  | const std::shared_ptr<FramePipeline>& pipeline) { | 
|  | auto status = pipeline->Consume([&](std::unique_ptr<FrameItem> item) { | 
|  | EXPECT_EQ(item->layer_tree_tasks.size(), 1u); | 
|  | EXPECT_EQ(item->layer_tree_tasks[0]->view_id, kFlutterImplicitViewId); | 
|  | }); | 
|  | EXPECT_EQ(status, PipelineConsumeResult::Done); | 
|  | draw_latch.Signal(); | 
|  | })); | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame) | 
|  | .WillRepeatedly( | 
|  | Invoke([&engine_context, &continuation_ready_latch]( | 
|  | fml::TimePoint frame_target_time, uint64_t frame_number) { | 
|  | continuation_ready_latch.Signal(); | 
|  | engine_context->EngineTaskSync([&](Engine& engine) { | 
|  | engine.BeginFrame(frame_target_time, frame_number); | 
|  | }); | 
|  | })); | 
|  |  | 
|  | std::unique_ptr<Animator> animator; | 
|  | PostSync(task_runners_.GetUITaskRunner(), | 
|  | [&animator, &animator_delegate, &task_runners = task_runners_] { | 
|  | animator = std::make_unique<Animator>( | 
|  | animator_delegate, task_runners, | 
|  | static_cast<std::unique_ptr<VsyncWaiter>>( | 
|  | std::make_unique<testing::ConstantFiringVsyncWaiter>( | 
|  | task_runners))); | 
|  | }); | 
|  |  | 
|  | engine_context = EngineContext::Create(delegate_, settings_, task_runners_, | 
|  | std::move(animator)); | 
|  |  | 
|  | engine_context->EngineTaskSync([](Engine& engine) { | 
|  | // Schedule a frame to trigger Animator::BeginFrame to create a | 
|  | // continuation. The continuation needs to be available before `Engine::Run` | 
|  | // since the Dart program immediately schedules a warm up frame. | 
|  | engine.ScheduleFrame(true); | 
|  | // Add the implicit view so that the engine recognizes it and that its | 
|  | // metrics is not empty. | 
|  | engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1.0, 10, 10, 1, 0}, | 
|  | [](bool added) { ASSERT_TRUE(added); }); | 
|  | }); | 
|  | continuation_ready_latch.Wait(); | 
|  |  | 
|  | auto configuration = RunConfiguration::InferFromSettings(settings_); | 
|  | configuration.SetEntrypoint("renderWarmUpImplicitView"); | 
|  | engine_context->Run(std::move(configuration)); | 
|  |  | 
|  | draw_latch.Wait(); | 
|  | } | 
|  |  | 
|  | // The warm up frame should work if only some of the registered views are | 
|  | // included. | 
|  | // | 
|  | // This test also verifies that the warm up frame can render multiple views. | 
|  | TEST_F(EngineAnimatorTest, AnimatorSubmitPartialViewsForWarmUp) { | 
|  | MockAnimatorDelegate animator_delegate; | 
|  | std::unique_ptr<EngineContext> engine_context; | 
|  |  | 
|  | std::shared_ptr<PlatformMessageHandler> platform_message_handler = | 
|  | std::make_shared<MockPlatformMessageHandler>(); | 
|  | EXPECT_CALL(delegate_, GetPlatformMessageHandler) | 
|  | .WillOnce(ReturnRef(platform_message_handler)); | 
|  |  | 
|  | fml::AutoResetWaitableEvent continuation_ready_latch; | 
|  | fml::AutoResetWaitableEvent draw_latch; | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorDraw) | 
|  | .WillOnce( | 
|  | Invoke([&draw_latch](const std::shared_ptr<FramePipeline>& pipeline) { | 
|  | auto status = | 
|  | pipeline->Consume([&](std::unique_ptr<FrameItem> item) { | 
|  | auto tasks = Sorted(item->layer_tree_tasks); | 
|  | EXPECT_EQ(tasks.size(), 2u); | 
|  | EXPECT_EQ(tasks[0]->view_id, 1); | 
|  | EXPECT_EQ(tasks[1]->view_id, 2); | 
|  | }); | 
|  | EXPECT_EQ(status, PipelineConsumeResult::Done); | 
|  | draw_latch.Signal(); | 
|  | })); | 
|  | EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame) | 
|  | .WillRepeatedly( | 
|  | Invoke([&engine_context, &continuation_ready_latch]( | 
|  | fml::TimePoint frame_target_time, uint64_t frame_number) { | 
|  | continuation_ready_latch.Signal(); | 
|  | engine_context->EngineTaskSync([&](Engine& engine) { | 
|  | engine.BeginFrame(frame_target_time, frame_number); | 
|  | }); | 
|  | })); | 
|  |  | 
|  | std::unique_ptr<Animator> animator; | 
|  | PostSync(task_runners_.GetUITaskRunner(), | 
|  | [&animator, &animator_delegate, &task_runners = task_runners_] { | 
|  | animator = std::make_unique<Animator>( | 
|  | animator_delegate, task_runners, | 
|  | static_cast<std::unique_ptr<VsyncWaiter>>( | 
|  | std::make_unique<testing::ConstantFiringVsyncWaiter>( | 
|  | task_runners))); | 
|  | }); | 
|  |  | 
|  | engine_context = EngineContext::Create(delegate_, settings_, task_runners_, | 
|  | std::move(animator)); | 
|  |  | 
|  | engine_context->EngineTaskSync([](Engine& engine) { | 
|  | // Schedule a frame to make the animator create a continuation. | 
|  | engine.ScheduleFrame(true); | 
|  | // Add multiple views. | 
|  | engine.AddView(0, ViewportMetrics{1, 10, 10, 22, 0}, | 
|  | [](bool added) { ASSERT_TRUE(added); }); | 
|  | engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0}, | 
|  | [](bool added) { ASSERT_TRUE(added); }); | 
|  | engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0}, | 
|  | [](bool added) { ASSERT_TRUE(added); }); | 
|  | }); | 
|  |  | 
|  | continuation_ready_latch.Wait(); | 
|  |  | 
|  | auto configuration = RunConfiguration::InferFromSettings(settings_); | 
|  | configuration.SetEntrypoint("renderWarmUpView1and2"); | 
|  | engine_context->Run(std::move(configuration)); | 
|  |  | 
|  | draw_latch.Wait(); | 
|  | } | 
|  |  | 
|  | }  // namespace flutter | 
|  |  | 
|  | // NOLINTEND(clang-analyzer-core.StackAddressEscape) |