// Copyright (c) 2013 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 "ui/snapshot/snapshot.h" #include #include #include "base/bind.h" #include "base/macros.h" #include "base/run_loop.h" #include "base/test/task_environment.h" #include "base/test/test_simple_task_runner.h" #include "base/test/test_timeouts.h" #include "base/win/windows_version.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkPixelRef.h" #include "ui/aura/test/aura_test_helper.h" #include "ui/aura/test/test_screen.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/test/test_windows.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/compositor/compositor.h" #include "ui/compositor/layer.h" #include "ui/compositor/paint_recorder.h" #include "ui/compositor/test/draw_waiter_for_test.h" #include "ui/compositor/test/test_context_factories.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/image/image.h" #include "ui/gfx/transform.h" #include "ui/gl/gl_implementation.h" namespace ui { namespace { SkColor GetExpectedColorForPoint(int x, int y) { return SkColorSetRGB(std::min(x, 255), std::min(y, 255), 0); } // Paint simple rectangle on the specified aura window. class TestPaintingWindowDelegate : public aura::test::TestWindowDelegate { public: explicit TestPaintingWindowDelegate(const gfx::Size& window_size) : window_size_(window_size) { } ~TestPaintingWindowDelegate() override {} void OnPaint(const ui::PaintContext& context) override { ui::PaintRecorder recorder(context, window_size_); for (int y = 0; y < window_size_.height(); ++y) { for (int x = 0; x < window_size_.width(); ++x) { recorder.canvas()->FillRect(gfx::Rect(x, y, 1, 1), GetExpectedColorForPoint(x, y)); } } } private: gfx::Size window_size_; DISALLOW_COPY_AND_ASSIGN(TestPaintingWindowDelegate); }; size_t GetFailedPixelsCountWithScaleFactor(const gfx::Image& image, int scale_factor) { const SkBitmap* bitmap = image.ToSkBitmap(); uint32_t* bitmap_data = reinterpret_cast(bitmap->pixelRef()->pixels()); size_t result = 0; for (int y = 0; y < bitmap->height(); y += scale_factor) { for (int x = 0; x < bitmap->width(); x += scale_factor) { if (static_cast(bitmap_data[x + y * bitmap->width()]) != GetExpectedColorForPoint(x / scale_factor, y / scale_factor)) { ++result; } } } return result; } size_t GetFailedPixelsCount(const gfx::Image& image) { return GetFailedPixelsCountWithScaleFactor(image, 1); } } // namespace // Param specifies whether to use SkiaRenderer or not class SnapshotAuraTest : public testing::TestWithParam { public: SnapshotAuraTest() {} ~SnapshotAuraTest() override {} void SetUp() override { testing::Test::SetUp(); task_environment_ = std::make_unique( base::test::TaskEnvironment::MainThreadType::UI); // The ContextFactory must exist before any Compositors are created. // Snapshot test tests real drawing and readback, so needs pixel output. const bool enable_pixel_output = true; context_factories_ = std::make_unique( enable_pixel_output, GetParam()); helper_ = std::make_unique( context_factories_->GetContextFactory()); helper_->SetUp(); } void TearDown() override { test_window_.reset(); delegate_.reset(); helper_->RunAllPendingInMessageLoop(); helper_.reset(); context_factories_.reset(); task_environment_.reset(); testing::Test::TearDown(); } protected: aura::Window* test_window() { return test_window_.get(); } aura::Window* root_window() { return helper_->GetContext(); } aura::TestScreen* test_screen() { return helper_->GetTestScreen(); } void WaitForDraw() { helper_->GetHost()->compositor()->ScheduleDraw(); ui::DrawWaiterForTest::WaitForCompositingEnded( helper_->GetHost()->compositor()); } void SetupTestWindow(const gfx::Rect& window_bounds) { delegate_ = std::make_unique(window_bounds.size()); test_window_.reset(aura::test::CreateTestWindowWithDelegate( delegate_.get(), 0, window_bounds, root_window())); } gfx::Image GrabSnapshotForTestWindow() { gfx::Rect source_rect(test_window_->bounds().size()); aura::Window::ConvertRectToTarget( test_window(), root_window(), &source_rect); scoped_refptr holder(new SnapshotHolder); ui::GrabWindowSnapshotAsync( root_window(), source_rect, base::BindOnce(&SnapshotHolder::SnapshotCallback, holder)); holder->WaitForSnapshot(); DCHECK(holder->completed()); return holder->image(); } private: class SnapshotHolder : public base::RefCountedThreadSafe { public: SnapshotHolder() : completed_(false) {} void SnapshotCallback(gfx::Image image) { DCHECK(!completed_); image_ = image; completed_ = true; run_loop_.Quit(); } void WaitForSnapshot() { run_loop_.Run(); } bool completed() const { return completed_; } const gfx::Image& image() const { return image_; } private: friend class base::RefCountedThreadSafe; virtual ~SnapshotHolder() {} base::RunLoop run_loop_; gfx::Image image_; bool completed_; }; std::unique_ptr task_environment_; std::unique_ptr context_factories_; std::unique_ptr helper_; std::unique_ptr test_window_; std::unique_ptr delegate_; std::vector png_representation_; DISALLOW_COPY_AND_ASSIGN(SnapshotAuraTest); }; INSTANTIATE_TEST_SUITE_P(All, SnapshotAuraTest, ::testing::Bool()); #if defined(OS_WIN) && !defined(NDEBUG) // https://crbug.com/852512 #define MAYBE_FullScreenWindow DISABLED_FullScreenWindow #else #define MAYBE_FullScreenWindow FullScreenWindow #endif TEST_P(SnapshotAuraTest, MAYBE_FullScreenWindow) { #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA) // TODO(https://crbug.com/1143031): Fix this test to run in < action_timeout() // on the Linux Debug & TSAN bots. const base::test::ScopedRunLoopTimeout increased_run_timeout( FROM_HERE, TestTimeouts::action_max_timeout()); #endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA) #if defined(OS_WIN) // TODO(https://crbug.com/850556): Make work on Win10. base::win::Version version = base::win::GetVersion(); if (version >= base::win::Version::WIN10) return; #endif SetupTestWindow(root_window()->bounds()); WaitForDraw(); gfx::Image snapshot = GrabSnapshotForTestWindow(); EXPECT_EQ(test_window()->bounds().size().ToString(), snapshot.Size().ToString()); EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); } TEST_P(SnapshotAuraTest, PartialBounds) { #if defined(OS_WIN) // TODO(https://crbug.com/850556): Make work on Win10. base::win::Version version = base::win::GetVersion(); if (version >= base::win::Version::WIN10) return; #endif gfx::Rect test_bounds(100, 100, 300, 200); SetupTestWindow(test_bounds); WaitForDraw(); gfx::Image snapshot = GrabSnapshotForTestWindow(); EXPECT_EQ(test_bounds.size().ToString(), snapshot.Size().ToString()); EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); } TEST_P(SnapshotAuraTest, Rotated) { #if defined(OS_WIN) // TODO(https://crbug.com/850556): Make work on Win10. base::win::Version version = base::win::GetVersion(); if (version >= base::win::Version::WIN10) return; #endif test_screen()->SetDisplayRotation(display::Display::ROTATE_90); gfx::Rect test_bounds(100, 100, 300, 200); SetupTestWindow(test_bounds); WaitForDraw(); gfx::Image snapshot = GrabSnapshotForTestWindow(); EXPECT_EQ(test_bounds.size().ToString(), snapshot.Size().ToString()); EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); } TEST_P(SnapshotAuraTest, UIScale) { #if defined(OS_WIN) // TODO(https://crbug.com/850556): Make work on Win10. base::win::Version version = base::win::GetVersion(); if (version >= base::win::Version::WIN10) return; #endif const float kUIScale = 0.5f; test_screen()->SetUIScale(kUIScale); gfx::Rect test_bounds(100, 100, 300, 200); SetupTestWindow(test_bounds); WaitForDraw(); // Snapshot always captures the physical pixels. gfx::SizeF snapshot_size(test_bounds.size()); snapshot_size.Scale(1 / kUIScale); gfx::Image snapshot = GrabSnapshotForTestWindow(); EXPECT_EQ(gfx::ToRoundedSize(snapshot_size).ToString(), snapshot.Size().ToString()); EXPECT_EQ(0u, GetFailedPixelsCountWithScaleFactor(snapshot, 1 / kUIScale)); } TEST_P(SnapshotAuraTest, DeviceScaleFactor) { #if defined(OS_WIN) // TODO(https://crbug.com/850556): Make work on Win10. base::win::Version version = base::win::GetVersion(); if (version >= base::win::Version::WIN10) return; #endif test_screen()->SetDeviceScaleFactor(2.0f); gfx::Rect test_bounds(100, 100, 150, 100); SetupTestWindow(test_bounds); WaitForDraw(); // Snapshot always captures the physical pixels. gfx::SizeF snapshot_size(test_bounds.size()); snapshot_size.Scale(2.0f); gfx::Image snapshot = GrabSnapshotForTestWindow(); EXPECT_EQ(gfx::ToRoundedSize(snapshot_size).ToString(), snapshot.Size().ToString()); EXPECT_EQ(0u, GetFailedPixelsCountWithScaleFactor(snapshot, 2)); } TEST_P(SnapshotAuraTest, RotateAndUIScale) { #if defined(OS_WIN) // TODO(https://crbug.com/850556): Make work on Win10. base::win::Version version = base::win::GetVersion(); if (version >= base::win::Version::WIN10) return; #endif const float kUIScale = 0.5f; test_screen()->SetUIScale(kUIScale); test_screen()->SetDisplayRotation(display::Display::ROTATE_90); gfx::Rect test_bounds(100, 100, 200, 300); SetupTestWindow(test_bounds); WaitForDraw(); // Snapshot always captures the physical pixels. gfx::SizeF snapshot_size(test_bounds.size()); snapshot_size.Scale(1 / kUIScale); gfx::Image snapshot = GrabSnapshotForTestWindow(); EXPECT_EQ(gfx::ToRoundedSize(snapshot_size).ToString(), snapshot.Size().ToString()); EXPECT_EQ(0u, GetFailedPixelsCountWithScaleFactor(snapshot, 1 / kUIScale)); } TEST_P(SnapshotAuraTest, RotateAndUIScaleAndScaleFactor) { #if defined(OS_WIN) // TODO(https://crbug.com/850556): Make work on Win10. base::win::Version version = base::win::GetVersion(); if (version >= base::win::Version::WIN10) return; #endif test_screen()->SetDeviceScaleFactor(2.0f); const float kUIScale = 0.5f; test_screen()->SetUIScale(kUIScale); test_screen()->SetDisplayRotation(display::Display::ROTATE_90); gfx::Rect test_bounds(20, 30, 100, 150); SetupTestWindow(test_bounds); WaitForDraw(); // Snapshot always captures the physical pixels. gfx::SizeF snapshot_size(test_bounds.size()); snapshot_size.Scale(2.0f / kUIScale); gfx::Image snapshot = GrabSnapshotForTestWindow(); EXPECT_EQ(gfx::ToRoundedSize(snapshot_size).ToString(), snapshot.Size().ToString()); EXPECT_EQ(0u, GetFailedPixelsCountWithScaleFactor(snapshot, 2 / kUIScale)); } } // namespace ui