| // Copyright 2020 The Fuchsia 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 "src/ui/tests/integration_flutter_tests/embedder/flutter-embedder-test.h" |
| |
| #include <fuchsia/logger/cpp/fidl.h> |
| #include <fuchsia/scheduler/cpp/fidl.h> |
| #include <fuchsia/tracing/provider/cpp/fidl.h> |
| #include <fuchsia/ui/app/cpp/fidl.h> |
| #include <fuchsia/ui/display/singleton/cpp/fidl.h> |
| #include <fuchsia/ui/pointerinjector/cpp/fidl.h> |
| #include <fuchsia/vulkan/loader/cpp/fidl.h> |
| #include <lib/sys/component/cpp/testing/realm_builder_types.h> |
| #include <lib/ui/scenic/cpp/view_ref_pair.h> |
| |
| #include <unordered_set> |
| |
| #include "src/ui/testing/util/screenshot_helper.h" |
| |
| namespace { |
| |
| // Types imported for the realm_builder library. |
| using component_testing::ChildRef; |
| using component_testing::ConfigValue; |
| using component_testing::DirectoryContents; |
| using component_testing::ParentRef; |
| using component_testing::Protocol; |
| using component_testing::RealmRoot; |
| using component_testing::Route; |
| using component_testing::StartupMode; |
| |
| static constexpr auto kChildFlutterRealm = "child_flutter"; |
| static constexpr auto kChildFlutterRealmRef = ChildRef{kChildFlutterRealm}; |
| static constexpr auto kParentFlutterRealm = "parent_flutter"; |
| static constexpr auto kParentFlutterRealmRef = ChildRef{kParentFlutterRealm}; |
| static constexpr auto kTestUIStack = "ui"; |
| static constexpr auto kTestUIStackRef = ChildRef{kTestUIStack}; |
| static constexpr auto kUsePointerInjection2Args = "--usePointerInjection2"; |
| |
| bool CheckViewExistsInSnapshot(const fuchsia::ui::observation::geometry::ViewTreeSnapshot& snapshot, |
| zx_koid_t view_ref_koid) { |
| if (!snapshot.has_views()) { |
| return false; |
| } |
| |
| auto snapshot_count = std::count_if( |
| snapshot.views().begin(), snapshot.views().end(), |
| [view_ref_koid](const auto& view) { return view.view_ref_koid() == view_ref_koid; }); |
| |
| return snapshot_count > 0; |
| } |
| |
| bool CheckViewExistsInUpdates( |
| const std::vector<fuchsia::ui::observation::geometry::ViewTreeSnapshot>& updates, |
| zx_koid_t view_ref_koid) { |
| auto update_count = |
| std::count_if(updates.begin(), updates.end(), [view_ref_koid](auto& snapshot) { |
| return CheckViewExistsInSnapshot(snapshot, view_ref_koid); |
| }); |
| |
| return update_count > 0; |
| } |
| |
| } // namespace |
| |
| namespace flutter_embedder_test { |
| |
| constexpr char kChildViewUrl[] = "fuchsia-pkg://fuchsia.com/child-view#meta/child-view-realm.cm"; |
| constexpr char kParentViewUrl[] = "fuchsia-pkg://fuchsia.com/parent-view#meta/parent-view-realm.cm"; |
| |
| constexpr auto kTestUIStackUrl = "#meta/test-ui-stack.cm"; |
| |
| const ui_testing::Pixel kParentBackgroundColor(0xFF, 0x00, 0x00, 0xFF); // Blue |
| const ui_testing::Pixel kParentTappedColor(0x00, 0x00, 0x00, 0xFF); // Black |
| const ui_testing::Pixel kChildBackgroundColor(0xFF, 0x00, 0xFF, 0xFF); // Pink |
| const ui_testing::Pixel kChildTappedColor(0x00, 0xFF, 0xFF, 0xFF); // Yellow |
| |
| // Overlay values for GFX. |
| const ui_testing::Pixel kGFXOverlayBackgroundColor1(0x0E, 0xFF, 0x00, |
| 0xFF); // Green, blended with blue (FEMU local) |
| const ui_testing::Pixel kGFXOverlayBackgroundColor2(0x0E, 0xFF, 0x0E, |
| 0xFF); // Green, blended with pink (FEMU local) |
| const ui_testing::Pixel kGFXOverlayBackgroundColor3(0x0D, 0xFF, 0x00, |
| 0xFF); // Green, blended with blue (AEMU infra) |
| const ui_testing::Pixel kGFXOverlayBackgroundColor4(0x0D, 0xFF, 0x0D, |
| 0xFF); // Green, blended with pink (AEMU infra) |
| const ui_testing::Pixel kGFXOverlayBackgroundColor5(0x0D, 0xFE, 0x00, |
| 0xFF); // Green, blended with blue (NUC) |
| const ui_testing::Pixel kGFXOverlayBackgroundColor6(0x00, 0xFF, 0x0D, |
| 0xFF); // Green, blended with pink (NUC) |
| |
| // Overlay values for Flatland. Note that these values are different than GFX because flutter sets |
| // the Overlay layer opacity as 254 for GFX. For Flatland, flutter does not use this hack and sets |
| // the opacity value between [0,1]. |
| const ui_testing::Pixel kFlatlandOverlayBackgroundColor1(0x0D, 0xFE, 0x00, |
| 0xFF); // Green blended with blue. |
| const ui_testing::Pixel kFlatlandOverlayBackgroundColor2(0x0D, 0xFE, 0x0D, |
| 0xFF); // Green blended with pink. |
| const ui_testing::Pixel kFlatlandOverlayBackgroundColor3(0x00, 0xFE, 0x0D, |
| 0xFF); // Green blended with yellow. |
| |
| std::set<ui_testing::Pixel> GetGFXOverlayPixels() { |
| return {kGFXOverlayBackgroundColor1, kGFXOverlayBackgroundColor2, kGFXOverlayBackgroundColor3, |
| kGFXOverlayBackgroundColor4, kGFXOverlayBackgroundColor5, kGFXOverlayBackgroundColor6}; |
| } |
| |
| std::set<ui_testing::Pixel> GetFlatlandOverlayPixels() { |
| return {kFlatlandOverlayBackgroundColor1, kFlatlandOverlayBackgroundColor2, |
| kFlatlandOverlayBackgroundColor3}; |
| } |
| |
| static uint32_t OverlayPixelCount(std::map<ui_testing::Pixel, uint32_t>& histogram, |
| bool use_flatland) { |
| const auto overlay_pixels = use_flatland ? GetFlatlandOverlayPixels() : GetGFXOverlayPixels(); |
| int pixel_count = 0; |
| for (const auto pixel : overlay_pixels) { |
| pixel_count += histogram[pixel]; |
| } |
| return pixel_count; |
| } |
| |
| std::vector<UIStackConfig> UIStackConfigsToTest() { |
| std::vector<UIStackConfig> configs; |
| |
| // GFX x Root presenter |
| configs.push_back({.use_scene_manager = false, .use_flatland = false}); |
| |
| // GFX x Scene manager |
| configs.push_back({.use_scene_manager = true, .use_flatland = false}); |
| |
| // Flatland X Scene manger |
| configs.push_back({.use_scene_manager = true, .use_flatland = true}); |
| |
| return configs; |
| } |
| |
| void FlutterEmbedderTest::SetUpRealmBase() { |
| FX_LOGS(INFO) << "Setting up realm base."; |
| |
| // Add test UI stack component. |
| realm_builder_.AddChild(kTestUIStack, kTestUIStackUrl); |
| |
| auto ui_stack_config = GetParam(); |
| realm_builder_.InitMutableConfigToEmpty(kTestUIStack); |
| realm_builder_.SetConfigValue(kTestUIStack, "use_scene_manager", |
| ConfigValue::Bool(ui_stack_config.use_scene_manager)); |
| realm_builder_.SetConfigValue(kTestUIStack, "use_flatland", |
| ConfigValue::Bool(ui_stack_config.use_flatland)); |
| realm_builder_.SetConfigValue(kTestUIStack, "display_rotation", ConfigValue::Uint32(0)); |
| realm_builder_.SetConfigValue(kTestUIStack, "device_pixel_ratio", ConfigValue("1.0")); |
| |
| // Add embedded child component to realm. |
| realm_builder_.AddChild(kChildFlutterRealm, kChildViewUrl); |
| |
| // Add child flutter app routes. Note that we do not route ViewProvider to ParentRef{} as it is |
| // embedded. |
| realm_builder_.AddRoute( |
| Route{.capabilities = {Protocol{fuchsia::ui::scenic::Scenic::Name_}, |
| Protocol{fuchsia::ui::composition::Flatland::Name_}, |
| Protocol{fuchsia::ui::composition::Allocator::Name_}}, |
| .source = kTestUIStackRef, |
| .targets = {kChildFlutterRealmRef}}); |
| |
| // Route base system services to flutter and the test UI stack. |
| realm_builder_.AddRoute( |
| Route{.capabilities = {Protocol{fuchsia::logger::LogSink::Name_}, |
| Protocol{fuchsia::scheduler::ProfileProvider::Name_}, |
| Protocol{fuchsia::sys::Environment::Name_}, |
| Protocol{fuchsia::sysmem::Allocator::Name_}, |
| Protocol{fuchsia::vulkan::loader::Loader::Name_}, |
| Protocol{fuchsia::tracing::provider::Registry::Name_}}, |
| .source = ParentRef{}, |
| .targets = {kChildFlutterRealmRef, kTestUIStackRef}}); |
| |
| // Capabilities routed to test driver. |
| realm_builder_.AddRoute( |
| Route{.capabilities = {Protocol{fuchsia::ui::test::input::Registry::Name_}, |
| Protocol{fuchsia::ui::test::scene::Controller::Name_}, |
| Protocol{fuchsia::ui::scenic::Scenic::Name_}, |
| Protocol{fuchsia::ui::composition::Screenshot::Name_}, |
| Protocol{fuchsia::ui::display::singleton::Info::Name_}}, |
| .source = kTestUIStackRef, |
| .targets = {ParentRef{}}}); |
| } |
| |
| // Checks whether the view with |view_ref_koid| has connected to the view tree. The response of a |
| // f.u.o.g.Provider.Watch call is stored in |watch_response| if it contains |view_ref_koid|. |
| bool FlutterEmbedderTest::HasViewConnected( |
| const fuchsia::ui::observation::geometry::ViewTreeWatcherPtr& view_tree_watcher, |
| std::optional<fuchsia::ui::observation::geometry::WatchResponse>& watch_response, |
| zx_koid_t view_ref_koid) { |
| std::optional<fuchsia::ui::observation::geometry::WatchResponse> view_tree_result; |
| view_tree_watcher->Watch( |
| [&view_tree_result](auto response) { view_tree_result = std::move(response); }); |
| FX_LOGS(INFO) << "Waiting for view tree result"; |
| RunLoopUntil([&view_tree_result] { return view_tree_result.has_value(); }); |
| FX_LOGS(INFO) << "Received view tree result"; |
| if (CheckViewExistsInUpdates(view_tree_result->updates(), view_ref_koid)) { |
| watch_response = std::move(view_tree_result); |
| }; |
| return watch_response.has_value(); |
| } |
| |
| void FlutterEmbedderTest::BuildRealmAndLaunchApp(const std::string& component_url, |
| const std::vector<std::string>& component_args, |
| bool usePointerInjection2) { |
| FX_LOGS(INFO) << "Building realm with component: " << component_url; |
| realm_builder_.AddChild(kParentFlutterRealm, kParentViewUrl); |
| |
| // Capabilities routed to embedded flutter app. |
| realm_builder_.AddRoute( |
| Route{.capabilities = {Protocol{fuchsia::ui::scenic::Scenic::Name_}, |
| Protocol{fuchsia::ui::composition::Flatland::Name_}, |
| Protocol{fuchsia::ui::composition::Allocator::Name_}}, |
| .source = kTestUIStackRef, |
| .targets = {kParentFlutterRealmRef}}); |
| |
| realm_builder_.AddRoute( |
| Route{.capabilities = {Protocol{fuchsia::ui::pointerinjector::Registry::Name_}}, |
| .source = kTestUIStackRef, |
| .targets = {kParentFlutterRealmRef}}); |
| |
| realm_builder_.AddRoute( |
| Route{.capabilities = {Protocol{fuchsia::logger::LogSink::Name_}, |
| Protocol{fuchsia::sys::Environment::Name_}, |
| Protocol{fuchsia::sysmem::Allocator::Name_}, |
| Protocol{fuchsia::tracing::provider::Registry::Name_}, |
| Protocol{fuchsia::vulkan::loader::Loader::Name_}}, |
| .source = ParentRef{}, |
| .targets = {kParentFlutterRealmRef}}); |
| |
| realm_builder_.AddRoute(Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}}, |
| .source = kParentFlutterRealmRef, |
| .targets = {ParentRef()}}); |
| |
| realm_builder_.AddRoute(Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}}, |
| .source = kChildFlutterRealmRef, |
| .targets = {kParentFlutterRealmRef}}); |
| |
| // Construct a args.csv file containing the specified comma-separated component args. |
| std::string csv; |
| for (const auto& arg : component_args) { |
| csv += arg + ','; |
| } |
| |
| if (usePointerInjection2) { |
| csv += kUsePointerInjection2Args; |
| } else { |
| if (csv.length() > 0) { |
| // Remove last comma. |
| csv.pop_back(); |
| } |
| } |
| |
| if (csv.length() > 0) { |
| // Route the /config/data to the parent view. |
| auto config_directory_contents = DirectoryContents(); |
| |
| config_directory_contents.AddFile("args.csv", csv); |
| |
| if (usePointerInjection2) { |
| config_directory_contents.AddFile("flutter_runner_config", GetPointerInjectorArgs()); |
| } |
| |
| realm_builder_.RouteReadOnlyDirectory("config-data", {kParentFlutterRealmRef}, |
| std::move(config_directory_contents)); |
| } |
| |
| realm_ = std::make_unique<RealmRoot>(realm_builder_.Build()); |
| |
| // Get the display information using the |fuchsia.ui.display.singleton.Info|. |
| std::optional<bool> has_completed; |
| fuchsia::ui::display::singleton::InfoPtr display_info = |
| realm_->Connect<fuchsia::ui::display::singleton::Info>(); |
| display_info->GetMetrics([this, &has_completed](auto info) { |
| display_width_ = info.extent_in_px().width; |
| display_height_ = info.extent_in_px().height; |
| has_completed = true; |
| }); |
| |
| screenshotter_ = realm_->Connect<fuchsia::ui::composition::Screenshot>(); |
| |
| RunLoopUntil([&has_completed] { return has_completed.has_value(); }); |
| |
| FX_LOGS(INFO) << "Got display_width " << display_width_ << " display_height " << display_height_; |
| |
| // Register fake touch screen device. |
| RegisterTouchScreen(); |
| |
| // Instruct Root Presenter to present test's View. |
| std::optional<zx_koid_t> view_ref_koid; |
| scene_provider_ = realm_->Connect<fuchsia::ui::test::scene::Controller>(); |
| scene_provider_.set_error_handler( |
| [](auto) { FX_LOGS(ERROR) << "Error from test scene provider"; }); |
| fuchsia::ui::test::scene::ControllerAttachClientViewRequest request; |
| request.set_view_provider(realm_->Connect<fuchsia::ui::app::ViewProvider>()); |
| scene_provider_->RegisterViewTreeWatcher(view_tree_watcher_.NewRequest(), []() {}); |
| scene_provider_->AttachClientView( |
| std::move(request), |
| [&view_ref_koid](auto client_view_ref_koid) { view_ref_koid = client_view_ref_koid; }); |
| |
| FX_LOGS(INFO) << "Waiting for client view ref koid"; |
| RunLoopUntil([&view_ref_koid] { return view_ref_koid.has_value(); }); |
| |
| // Wait for the client view to get attached to the view tree. |
| std::optional<fuchsia::ui::observation::geometry::WatchResponse> watch_response; |
| FX_LOGS(INFO) << "Waiting for client view to render"; |
| RunLoopUntil([this, &watch_response, &view_ref_koid] { |
| return HasViewConnected(view_tree_watcher_, watch_response, *view_ref_koid); |
| }); |
| FX_LOGS(INFO) << "Client view has rendered"; |
| |
| scenic_ = realm_->Connect<fuchsia::ui::scenic::Scenic>(); |
| FX_LOGS(INFO) << "Launched component: " << component_url; |
| } |
| |
| void FlutterEmbedderTest::RegisterTouchScreen() { |
| FX_LOGS(INFO) << "Registering fake touch screen"; |
| input_registry_ = realm_->Connect<fuchsia::ui::test::input::Registry>(); |
| input_registry_.set_error_handler([](auto) { FX_LOGS(ERROR) << "Error from input helper"; }); |
| bool touchscreen_registered = false; |
| fuchsia::ui::test::input::RegistryRegisterTouchScreenRequest request; |
| request.set_device(fake_touchscreen_.NewRequest()); |
| input_registry_->RegisterTouchScreen( |
| std::move(request), [&touchscreen_registered]() { touchscreen_registered = true; }); |
| RunLoopUntil([&touchscreen_registered] { return touchscreen_registered; }); |
| FX_LOGS(INFO) << "Touchscreen registered"; |
| } |
| |
| void FlutterEmbedderTest::InjectTap(int32_t x, int32_t y) { |
| fuchsia::ui::test::input::TouchScreenSimulateTapRequest tap_request; |
| tap_request.mutable_tap_location()->x = x; |
| tap_request.mutable_tap_location()->y = y; |
| fake_touchscreen_->SimulateTap(std::move(tap_request), [x, y]() { |
| FX_LOGS(INFO) << "Tap injected at (" << x << ", " << y << ")"; |
| }); |
| } |
| |
| void FlutterEmbedderTest::TryInject(int32_t x, int32_t y) { |
| InjectTap(x, y); |
| async::PostDelayedTask( |
| dispatcher(), [this, x, y] { TryInject(x, y); }, kTapRetryInterval); |
| } |
| |
| std::string FlutterEmbedderTest::GetPointerInjectorArgs() { |
| std::ostringstream config; |
| |
| config << "{" |
| << " \"intercept_all_input\" : true" |
| << "}"; |
| |
| return config.str(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(FlutterEmbedderTestWithParams, FlutterEmbedderTest, |
| ::testing::ValuesIn(UIStackConfigsToTest())); |
| |
| TEST_P(FlutterEmbedderTest, Embedding) { |
| BuildRealmAndLaunchApp(kParentViewUrl); |
| |
| // Take screenshot until we see the child-view's embedded color. |
| ASSERT_TRUE(TakeScreenshotUntil( |
| kChildBackgroundColor, [](std::map<ui_testing::Pixel, uint32_t> histogram) { |
| // Expect parent and child background colors, with parent color > child color. |
| EXPECT_GT(histogram[kParentBackgroundColor], 0u); |
| EXPECT_GT(histogram[kChildBackgroundColor], 0u); |
| EXPECT_GT(histogram[kParentBackgroundColor], histogram[kChildBackgroundColor]); |
| })); |
| } |
| |
| TEST_P(FlutterEmbedderTest, HittestEmbedding) { |
| BuildRealmAndLaunchApp(kParentViewUrl); |
| |
| // Take screenshot until we see the child-view's embedded color. |
| ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor)); |
| |
| // Simulate a tap at the center of the child view. |
| TryInject(/* x = */ 0, /* y = */ 0); |
| |
| // Take screenshot until we see the child-view's tapped color. |
| ASSERT_TRUE( |
| TakeScreenshotUntil(kChildTappedColor, [](std::map<ui_testing::Pixel, uint32_t> histogram) { |
| // Expect parent and child background colors, with parent color > child color. |
| EXPECT_GT(histogram[kParentBackgroundColor], 0u); |
| EXPECT_EQ(histogram[kChildBackgroundColor], 0u); |
| EXPECT_GT(histogram[kChildTappedColor], 0u); |
| EXPECT_GT(histogram[kParentBackgroundColor], histogram[kChildTappedColor]); |
| })); |
| } |
| |
| TEST_P(FlutterEmbedderTest, HittestDisabledEmbedding) { |
| // TODO(fxb/114135): Add Flatland support for this test. |
| if (GetParam().use_flatland) { |
| GTEST_SKIP(); |
| } |
| |
| BuildRealmAndLaunchApp(kParentViewUrl, {"--no-hitTestable"}); |
| |
| // Take screenshots until we see the child-view's embedded color. |
| ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor)); |
| |
| // Simulate a tap at the center of the child view. |
| TryInject(/* x = */ 0, /* y = */ 0); |
| |
| // The parent-view should change color. |
| ASSERT_TRUE( |
| TakeScreenshotUntil(kParentTappedColor, [](std::map<ui_testing::Pixel, uint32_t> histogram) { |
| // Expect parent and child background colors, with parent color > child color. |
| EXPECT_EQ(histogram[kParentBackgroundColor], 0u); |
| EXPECT_GT(histogram[kParentTappedColor], 0u); |
| EXPECT_GT(histogram[kChildBackgroundColor], 0u); |
| EXPECT_EQ(histogram[kChildTappedColor], 0u); |
| EXPECT_GT(histogram[kParentTappedColor], histogram[kChildBackgroundColor]); |
| })); |
| } |
| |
| TEST_P(FlutterEmbedderTest, EmbeddingWithOverlay) { |
| BuildRealmAndLaunchApp(kParentViewUrl, {"--showOverlay"}); |
| |
| // Take screenshot until we see the child-view's embedded color. |
| ASSERT_TRUE(TakeScreenshotUntil( |
| kChildBackgroundColor, [](std::map<ui_testing::Pixel, uint32_t> histogram) { |
| // Expect parent, overlay and child background colors. |
| // With parent color > child color and overlay color > child color. |
| const size_t overlay_pixel_count = OverlayPixelCount(histogram, GetParam().use_flatland); |
| EXPECT_GT(histogram[kParentBackgroundColor], 0u); |
| EXPECT_GT(overlay_pixel_count, 0u); |
| EXPECT_GT(histogram[kChildBackgroundColor], 0u); |
| EXPECT_GT(histogram[kParentBackgroundColor], histogram[kChildBackgroundColor]); |
| EXPECT_GT(overlay_pixel_count, histogram[kChildBackgroundColor]); |
| })); |
| } |
| |
| TEST_P(FlutterEmbedderTest, HittestEmbeddingWithOverlay) { |
| if (GetParam().use_flatland) { |
| BuildRealmAndLaunchApp(kParentViewUrl, {"--showOverlay"}, true /*usePointerInjection2*/); |
| } else { |
| BuildRealmAndLaunchApp(kParentViewUrl, {"--showOverlay"}); |
| } |
| |
| // Take screenshot until we see the child-view's embedded color. |
| ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor)); |
| |
| // The bottom-left corner of the overlay is at the center of the screen, |
| // which is at (0, 0) in the injection coordinate space. Inject a pointer |
| // event just outside the overlay's bounds, and ensure that it goes to the |
| // embedded view. |
| TryInject(/* x = */ -1, /* y = */ 1); |
| |
| // Take screenshot until we see the child-view's tapped color. |
| ASSERT_TRUE( |
| TakeScreenshotUntil(kChildTappedColor, [](std::map<ui_testing::Pixel, uint32_t> histogram) { |
| // Expect parent, overlay and child background colors. |
| // With parent color > child color and overlay color > child color. |
| const size_t overlay_pixel_count = OverlayPixelCount(histogram, GetParam().use_flatland); |
| EXPECT_GT(histogram[kParentBackgroundColor], 0u); |
| EXPECT_GT(overlay_pixel_count, 0u); |
| EXPECT_EQ(histogram[kChildBackgroundColor], 0u); |
| EXPECT_GT(histogram[kChildTappedColor], 0u); |
| EXPECT_GT(histogram[kParentBackgroundColor], histogram[kChildTappedColor]); |
| EXPECT_GT(overlay_pixel_count, histogram[kChildTappedColor]); |
| })); |
| } |
| |
| TEST_P(FlutterEmbedderTest, ChildViewReinjectionTest) { |
| BuildRealmAndLaunchApp(kParentViewUrl, {}, true); |
| |
| // Take screenshot until we see the child-view's embedded color. |
| ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor)); |
| |
| // Simulate a tap at the center of the child view. |
| TryInject(/* x = */ 0, /* y = */ 0); |
| |
| // Take screenshot until we see the child-view's tapped color. |
| ASSERT_TRUE( |
| TakeScreenshotUntil(kChildTappedColor, [](std::map<ui_testing::Pixel, uint32_t> histogram) { |
| // Expect parent and child background colors, with parent color > child color. |
| EXPECT_GT(histogram[kParentBackgroundColor], 0u); |
| EXPECT_EQ(histogram[kChildBackgroundColor], 0u); |
| EXPECT_GT(histogram[kChildTappedColor], 0u); |
| EXPECT_GT(histogram[kParentBackgroundColor], histogram[kChildTappedColor]); |
| })); |
| } |
| |
| } // namespace flutter_embedder_test |