Commit 8dc3a74c authored by Joey Arhar's avatar Joey Arhar Committed by Commit Bot

Add pixel browser tests for focus rings

Focus rings are rendered differently on different platforms, and the
platform specific differences are currently untested because layout
tests force a platform-agnostic focus ring rendering code path. This
browser test is needed in order to run the platform specific code.

These tests need to be interactive_ui_tests because the window needs
dedicated focus in order to make the focus rings show up consistently.

A similar test was added in the content layer in
http://crrev.com/c/1969772

Bug: 1038425
Change-Id: I84ce398ab5d782e657cc13d0e073bf9997056bff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1986972
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarMason Freed <masonfreed@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758587}
parent 2021dc0e
// Copyright 2020 The Chromium 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 "base/path_service.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/test/screenshot_test_utils.h"
#include "ui/base/ui_base_features.h"
// TODO(crbug.com/958242): Move the baselines to skia gold for easier
// rebaselining when all platforms are supported
// To rebaseline this test on all platforms:
// 1. Run a CQ+1 dry run.
// 2. Click the failing bots for android, windows, mac, and linux.
// 3. Find the failing interactive_ui_browsertests step.
// 4. Click the "Deterministic failure" link for the failing test case.
// 5. Copy the "Actual pixels" data url and paste into browser.
// 6. Save the image into your chromium checkout in
// chrome/test/data/focus_rings.
class FocusRingBrowserTest : public InProcessBrowserTest {
public:
FocusRingBrowserTest() {
feature_list_.InitWithFeatures({features::kFormControlsRefresh}, {});
}
void SetUp() override {
EnablePixelOutput();
InProcessBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
content::SetUpCommandLineForScreenshotTest(command_line);
}
void RunTest(const std::string& screenshot_filename,
const std::string& body_html,
int screenshot_width,
int screenshot_height) {
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(features::IsFormControlsRefreshEnabled());
base::FilePath dir_test_data;
ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &dir_test_data));
base::FilePath golden_screenshot_filepath =
dir_test_data.AppendASCII("focus_rings")
.AppendASCII(screenshot_filename + ".png");
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::NavigateToURL(
web_contents,
GURL("data:text/html,<!DOCTYPE html><body>" + body_html + "</body>")));
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
content::RunScreenshotTest(web_contents, golden_screenshot_filepath,
screenshot_width, screenshot_height);
}
private:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(FocusRingBrowserTest, Checkbox) {
RunTest("focus_ring_browsertest_checkbox",
"<input type=checkbox autofocus>"
"<input type=checkbox>",
/* screenshot_width */ 60,
/* screenshot_height */ 40);
}
IN_PROC_BROWSER_TEST_F(FocusRingBrowserTest, Radio) {
RunTest("focus_ring_browsertest_radio",
"<input type=radio autofocus>"
"<input type=radio>",
/* screenshot_width */ 60,
/* screenshot_height */ 40);
}
IN_PROC_BROWSER_TEST_F(FocusRingBrowserTest, Button) {
RunTest("focus_ring_browsertest_button",
"<button autofocus>button</button>"
"<br>"
"<br>"
"<button>button</button>",
/* screenshot_width */ 80,
/* screenshot_height */ 80);
}
IN_PROC_BROWSER_TEST_F(FocusRingBrowserTest, Anchor) {
RunTest("focus_ring_browsertest_anchor",
"<div style='text-align: center; width: 80px;'>"
" <a href='foo' autofocus>line one<br>two</a>"
"</div>"
"<br>"
"<div style='text-align: center; width: 80px;'>"
" <a href='foo'>line one<br>two</a>"
"</div>",
/* screenshot_width */ 90,
/* screenshot_height */ 130);
}
...@@ -5689,6 +5689,7 @@ if (!is_android) { ...@@ -5689,6 +5689,7 @@ if (!is_android) {
"../browser/extensions/extension_keybinding_apitest.cc", "../browser/extensions/extension_keybinding_apitest.cc",
"../browser/extensions/omnibox_focus_interactive_test.cc", "../browser/extensions/omnibox_focus_interactive_test.cc",
"../browser/extensions/window_open_interactive_apitest.cc", "../browser/extensions/window_open_interactive_apitest.cc",
"../browser/focus_ring_browsertest.cc",
"../browser/global_keyboard_shortcuts_mac_browsertest.mm", "../browser/global_keyboard_shortcuts_mac_browsertest.mm",
"../browser/mouse_events_interactive_uitest.cc", "../browser/mouse_events_interactive_uitest.cc",
"../browser/notifications/platform_notification_service_interactive_uitest.cc", "../browser/notifications/platform_notification_service_interactive_uitest.cc",
......
...@@ -3,37 +3,35 @@ ...@@ -3,37 +3,35 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "base/path_service.h" #include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h" #include "content/public/browser/render_widget_host_view.h"
#include "build/build_config.h"
#include "cc/test/pixel_comparator.h"
#include "cc/test/pixel_test_utils.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/public/common/content_paths.h" #include "content/public/common/content_paths.h"
#include "content/public/test/browser_test_utils.h" #include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h" #include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/screenshot_test_utils.h"
#include "content/public/test/test_utils.h" #include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h" #include "content/shell/browser/shell.h"
#include "ui/base/ui_base_features.h" #include "ui/base/ui_base_features.h"
#include "ui/display/display_switches.h"
#include "ui/gfx/image/image.h" // TODO(crbug.com/958242): Move the baselines to skia gold for easier
#include "ui/gfx/skbitmap_operations.h" // rebaselining when all platforms are supported.
// To rebaseline this test on android: // To rebaseline this test on all platforms:
// 1. Run a CQ+1 dry run // 1. Run a CQ+1 dry run.
// 2. Click the failing android bot // 2. Click the failing bots for android, windows, mac, and linux.
// 3. Find the failing content_browsertests step // 3. Find the failing interactive_ui_browsertests step.
// 4. Click the "Deterministic failure" link for the failing test case // 4. Click the "Deterministic failure" link for the failing test case.
// 5. Copy the "Actual pixels" data url and paste into browser // 5. Copy the "Actual pixels" data url and paste into browser.
// 6. Save the image into your chromium checkout in content/test/data/forms/ // 6. Save the image into your chromium checkout in content/test/data/forms/.
namespace content { namespace content {
class FormControlsBrowserTest : public ContentBrowserTest { class FormControlsBrowserTest : public ContentBrowserTest {
public: public:
FormControlsBrowserTest() = default; FormControlsBrowserTest() {
feature_list_.InitWithFeatures({features::kFormControlsRefresh}, {});
}
void SetUp() override { void SetUp() override {
EnablePixelOutput(); EnablePixelOutput();
...@@ -42,128 +40,61 @@ class FormControlsBrowserTest : public ContentBrowserTest { ...@@ -42,128 +40,61 @@ class FormControlsBrowserTest : public ContentBrowserTest {
void SetUpCommandLine(base::CommandLine* command_line) override { void SetUpCommandLine(base::CommandLine* command_line) override {
ContentBrowserTest::SetUpCommandLine(command_line); ContentBrowserTest::SetUpCommandLine(command_line);
// The --force-device-scale-factor flag helps make the pixel output of SetUpCommandLineForScreenshotTest(command_line);
// different android trybots more similar.
command_line->AppendSwitchASCII(switches::kForceDeviceScaleFactor, "1.0");
feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
feature_list_->InitWithFeatures({features::kFormControlsRefresh}, {});
}
void TearDown() override { feature_list_.reset(); }
void AsyncSnapshotCallback(const gfx::Image& image) {
got_snapshot_ = true;
snapshot_ = image;
} }
void RunFormControlsTest(const std::string& expected_filename, void RunTest(const std::string& screenshot_filename,
const std::string& body_html, const std::string& body_html,
int screenshot_width, int screenshot_width,
int screenshot_height) { int screenshot_height) {
base::ScopedAllowBlockingForTesting allow_blocking; base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(features::IsFormControlsRefreshEnabled());
std::string url = ASSERT_TRUE(features::IsFormControlsRefreshEnabled());
"data:text/html,<!DOCTYPE html>"
"<head>"
// The <meta name=viewport> tag helps make the pixel output of
// different android trybots more similar.
" <meta name=\"viewport\" content=\"width=640, initial-scale=1, "
" maximum-scale=1, minimum-scale=1\">"
"</head>"
"<body>" +
body_html + "</body>";
ASSERT_TRUE(NavigateToURL(shell(), GURL(url)));
RenderWidgetHostImpl* const rwh =
RenderWidgetHostImpl::From(shell()
->web_contents()
->GetRenderWidgetHostView()
->GetRenderWidgetHost());
CHECK(rwh);
rwh->GetSnapshotFromBrowser(
base::BindOnce(&FormControlsBrowserTest::AsyncSnapshotCallback,
base::Unretained(this)),
/* from_surface */ true);
while (!got_snapshot_)
base::RunLoop().RunUntilIdle();
SkBitmap bitmap = SkBitmapOperations::CreateTiledBitmap(
*snapshot_.ToSkBitmap(), /* src_x */ 0, /* src_y */ 0, screenshot_width,
screenshot_height);
base::FilePath dir_test_data; base::FilePath dir_test_data;
ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &dir_test_data)); ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &dir_test_data));
std::string filename_with_extension = expected_filename; base::FilePath golden_screenshot_filepath =
#if defined(OS_ANDROID) dir_test_data.AppendASCII("forms").AppendASCII(screenshot_filename +
filename_with_extension += "_android"; ".png");
#endif
filename_with_extension += ".png"; ASSERT_TRUE(NavigateToURL(
base::FilePath expected_path = shell()->web_contents(),
dir_test_data.AppendASCII("forms").AppendASCII(filename_with_extension); GURL("data:text/html,<!DOCTYPE html><body>" + body_html + "</body>")));
SkBitmap expected_bitmap;
ASSERT_TRUE(cc::ReadPNGFile(expected_path, &expected_bitmap)); RunScreenshotTest(shell()->web_contents(), golden_screenshot_filepath,
screenshot_width, screenshot_height);
EXPECT_TRUE(cc::MatchesBitmap(
bitmap, expected_bitmap,
#if defined(OS_MACOSX)
// The Mac 10.12 trybot has more significant subpixel rendering
// differences which we accommodate for here with a large avg/max
// per-pixel error limit.
// TODO(crbug.com/1037971): Remove this special case for mac once this
// bug is resolved.
cc::FuzzyPixelComparator(/* discard_alpha */ true,
/* error_pixels_percentage_limit */ 7.f,
/* small_error_pixels_percentage_limit */ 0.f,
/* avg_abs_error_limit */ 16.f,
/* max_abs_error_limit */ 79.f,
/* small_error_threshold */ 0)));
#else
// We use a fuzzy comparator to accommodate for slight
// differences between the kitkat and marshmallow trybots that aren't
// visible to the human eye. We use a very low error limit because the
// pixels that are different are very similar shades of color.
cc::FuzzyPixelComparator(/* discard_alpha */ true,
/* error_pixels_percentage_limit */ 6.f,
/* small_error_pixels_percentage_limit */ 0.f,
/* avg_abs_error_limit */ 4.f,
/* max_abs_error_limit */ 4.f,
/* small_error_threshold */ 0)));
#endif
} }
bool got_snapshot_ = false; private:
gfx::Image snapshot_; base::test::ScopedFeatureList feature_list_;
std::unique_ptr<base::test::ScopedFeatureList> feature_list_;
}; };
IN_PROC_BROWSER_TEST_F(FormControlsBrowserTest, Checkbox) { IN_PROC_BROWSER_TEST_F(FormControlsBrowserTest, Checkbox) {
RunFormControlsTest( RunTest("form_controls_browsertest_checkbox",
"form_controls_browsertest_checkbox", "<input type=checkbox>"
"<input type=checkbox>" "<input type=checkbox checked>"
"<input type=checkbox checked>" "<input type=checkbox disabled>"
"<input type=checkbox disabled>" "<input type=checkbox checked disabled>"
"<input type=checkbox checked disabled>" "<input type=checkbox id=\"indeterminate\">"
"<input type=checkbox id=\"indeterminate\">" "<script>"
"<script>" " document.getElementById('indeterminate').indeterminate = true"
" document.getElementById('indeterminate').indeterminate = true" "</script>",
"</script>", /* screenshot_width */ 130,
/* screenshot_width */ 130, /* screenshot_height */ 40);
/* screenshot_height */ 40);
} }
IN_PROC_BROWSER_TEST_F(FormControlsBrowserTest, Radio) { IN_PROC_BROWSER_TEST_F(FormControlsBrowserTest, Radio) {
RunFormControlsTest( RunTest("form_controls_browsertest_radio",
"form_controls_browsertest_radio", "<input type=radio>"
"<input type=radio>" "<input type=radio checked>"
"<input type=radio checked>" "<input type=radio disabled>"
"<input type=radio disabled>" "<input type=radio checked disabled>"
"<input type=radio checked disabled>" "<input type=radio id=\"indeterminate\">"
"<input type=radio id=\"indeterminate\">" "<script>"
"<script>" " document.getElementById('indeterminate').indeterminate = true"
" document.getElementById('indeterminate').indeterminate = true" "</script>",
"</script>", /* screenshot_width */ 140,
/* screenshot_width */ 140, /* screenshot_height */ 40);
/* screenshot_height */ 40);
} }
// TODO(jarhar): Add tests for other elements from // TODO(jarhar): Add tests for other elements from
......
// Copyright 2020 The Chromium 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 "content/public/test/screenshot_test_utils.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "cc/test/pixel_comparator.h"
#include "cc/test/pixel_test_utils.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/display_switches.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/skbitmap_operations.h"
#include "ui/gl/gl_switches.h"
#if defined(OS_ANDROID)
#include "base/android/build_info.h"
#endif
namespace content {
void SetUpCommandLineForScreenshotTest(base::CommandLine* command_line) {
// The --force-device-scale-factor flag helps make the pixel output of
// different android trybots more similar.
command_line->AppendSwitchASCII(switches::kForceDeviceScaleFactor, "1.0");
// The --disable-lcd-text flag helps text render more similarly on
// different bots and platform.
command_line->AppendSwitch(switches::kDisableLCDText);
}
void RunScreenshotTest(WebContents* web_contents,
const base::FilePath& golden_filepath_default,
int screenshot_width,
int screenshot_height) {
// Asserts for SetUpCommandLineForScreenshotTest.
ASSERT_EQ(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kForceDeviceScaleFactor),
"1.0");
ASSERT_TRUE(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableLCDText));
// Asserts that BrowserTestBase::EnablePixelOutput was called.
ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableGLDrawingForTests));
base::ScopedAllowBlockingForTesting allow_blocking;
content::RenderWidgetHostImpl* const rwh =
content::RenderWidgetHostImpl::From(
web_contents->GetRenderWidgetHostView()->GetRenderWidgetHost());
ASSERT_TRUE(rwh);
gfx::Image snapshot;
base::RunLoop screenshot_callback_runloop;
base::RepeatingClosure quit_closure =
screenshot_callback_runloop.QuitClosure();
rwh->GetSnapshotFromBrowser(
base::BindLambdaForTesting([&](const gfx::Image& image) {
snapshot = image;
quit_closure.Run();
}),
/* from_surface */ true);
screenshot_callback_runloop.Run();
SkBitmap bitmap = SkBitmapOperations::CreateTiledBitmap(
*snapshot.ToSkBitmap(), /* src_x */ 0, /* src_y */ 0, screenshot_width,
screenshot_height);
base::StringPiece platform_suffix;
#if defined(OS_MACOSX)
platform_suffix = "_mac";
#elif defined(OS_WIN)
platform_suffix = "_win";
#elif defined(OS_CHROMEOS)
platform_suffix = "_chromeos";
#elif defined(OS_ANDROID)
int sdk_int = base::android::BuildInfo::GetInstance()->sdk_int();
if (sdk_int == base::android::SDK_VERSION_KITKAT) {
platform_suffix = "_android_kitkat";
} else if (sdk_int == base::android::SDK_VERSION_MARSHMALLOW) {
platform_suffix = "_android_marshmallow";
} else {
platform_suffix = "_android";
}
#endif
base::FilePath golden_filepath_platform =
golden_filepath_default.InsertBeforeExtensionASCII(platform_suffix);
SkBitmap expected_bitmap;
if (!cc::ReadPNGFile(golden_filepath_platform, &expected_bitmap)) {
ASSERT_TRUE(cc::ReadPNGFile(golden_filepath_default, &expected_bitmap));
}
#if defined(OS_MACOSX)
// The Mac 10.12 trybot has more significant subpixel rendering
// differences which we accommodate for here with a large avg/max
// per-pixel error limit.
// TODO(crbug.com/1037971): Remove this special case for mac once this
// bug is resolved.
cc::FuzzyPixelComparator comparator(
/* discard_alpha */ true,
/* error_pixels_percentage_limit */ 7.f,
/* small_error_pixels_percentage_limit */ 0.f,
/* avg_abs_error_limit */ 16.f,
/* max_abs_error_limit */ 79.f,
/* small_error_threshold */ 0);
#else
cc::ExactPixelComparator comparator(/* disard_alpha */ true);
#endif
EXPECT_TRUE(cc::MatchesBitmap(bitmap, expected_bitmap, comparator));
}
} // namespace content
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_PUBLIC_TEST_SCREENSHOT_TEST_UTILS_H_
#define CONTENT_PUBLIC_TEST_SCREENSHOT_TEST_UTILS_H_
#include <string>
namespace base {
class CommandLine;
class FilePath;
} // namespace base
namespace content {
class WebContents;
// This file contains functions to help build browsertests which take
// screenshots of web content and make pixel comparisons to golden baseline
// images. While you might normally use web_tests to make pixel tests of web
// content, making a browsertest helps highlight platform specific differences
// not rendered in web_tests like the different rendering of focus rings.
// Adds command-line flags to help unify rendering across devices and
// platforms. This should be called in the SetUpCommandLine function of browser
// tests.
void SetUpCommandLineForScreenshotTest(base::CommandLine* command_line);
// Runs a screenshot test by taking a screenshot of the given |web_contents|
// and comparing it to a golden baseline image file.
//
// |golden_screenshot_filepath| is the filepath to the golden expected
// screenshot for the test to compare to. For platform-specific differences, a
// different file for that platform can be provided and will be used
// automatically if present and conforms to the correct naming scheme. If no
// such platform specific golden image is present, the "default" one without a
// platform specific extension will be used, which is always used for Linux.
// The KitKat Android bot tends to render differently enough from the other
// Android bot that it is tracked separately. If no kitkat golden image is
// provided, it will default to the Linux golden, like all other platforms.
// Here is an example of all of the golden files present for a test which
// renders differently on all platforms:
// my_screenshot_test.png
// my_screenshot_test_mac.png
// my_screenshot_test_win.png
// my_screenshot_test_chromeos.png
// my_screenshot_test_android.png
// my_screenshot_test_android_kitkat.png
void RunScreenshotTest(WebContents* web_contents,
const base::FilePath& golden_screenshot_filepath,
int screenshot_width,
int screenshot_height);
} // namespace content
#endif // CONTENT_PUBLIC_TEST_SCREENSHOT_TEST_UTILS_H_
...@@ -169,6 +169,8 @@ jumbo_static_library("test_support") { ...@@ -169,6 +169,8 @@ jumbo_static_library("test_support") {
"../public/test/render_view_test.h", "../public/test/render_view_test.h",
"../public/test/scoped_overscroll_modes.cc", "../public/test/scoped_overscroll_modes.cc",
"../public/test/scoped_overscroll_modes.h", "../public/test/scoped_overscroll_modes.h",
"../public/test/screenshot_test_utils.cc",
"../public/test/screenshot_test_utils.h",
"../public/test/service_worker_host_interceptor.cc", "../public/test/service_worker_host_interceptor.cc",
"../public/test/service_worker_host_interceptor.h", "../public/test/service_worker_host_interceptor.h",
"../public/test/service_worker_test_helpers.cc", "../public/test/service_worker_test_helpers.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment