| // Copyright (c) 2012 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 "chrome/browser/ui/views/test/view_event_test_base.h" |
| #include "base/memory/raw_ptr.h" |
| |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "chrome/test/base/chrome_unit_test_suite.h" |
| #include "chrome/test/base/interactive_test_utils.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "mojo/core/embedder/embedder.h" |
| #include "ui/display/screen.h" |
| #include "ui/views/layout/fill_layout.h" |
| #include "ui/views/view.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_delegate.h" |
| |
| #if defined(USE_AURA) && !BUILDFLAG(IS_CHROMEOS_ASH) |
| #include "ui/views/widget/desktop_aura/desktop_screen.h" |
| |
| #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && defined(USE_OZONE) |
| #include "ui/views/test/test_desktop_screen_ozone.h" |
| #endif // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && |
| // defined(USE_OZONE) |
| #endif |
| |
| namespace { |
| |
| // View that keeps its preferred size in sync with what |harness| requests. |
| class TestView : public views::View { |
| public: |
| explicit TestView(ViewEventTestBase* harness) : harness_(harness) { |
| SetLayoutManager(std::make_unique<views::FillLayout>()); |
| AddChildView(harness_->CreateContentsView()); |
| } |
| TestView(const TestView&) = delete; |
| TestView& operator=(const TestView&) = delete; |
| ~TestView() override = default; |
| |
| gfx::Size CalculatePreferredSize() const override { |
| return harness_->GetPreferredSizeForContents(); |
| } |
| |
| private: |
| raw_ptr<ViewEventTestBase> harness_; |
| }; |
| |
| } // namespace |
| |
| class TestBaseWidgetDelegate : public views::WidgetDelegate { |
| public: |
| explicit TestBaseWidgetDelegate(ViewEventTestBase* harness) |
| : harness_(harness) { |
| SetCanResize(true); |
| SetOwnedByWidget(true); |
| } |
| TestBaseWidgetDelegate(const TestBaseWidgetDelegate&) = delete; |
| TestBaseWidgetDelegate& operator=(const TestBaseWidgetDelegate&) = delete; |
| ~TestBaseWidgetDelegate() override = default; |
| |
| // views::WidgetDelegate: |
| void WindowClosing() override { harness_->window_ = nullptr; } |
| views::Widget* GetWidget() override { |
| return contents_ ? contents_->GetWidget() : nullptr; |
| } |
| const views::Widget* GetWidget() const override { |
| return contents_ ? contents_->GetWidget() : nullptr; |
| } |
| views::View* GetContentsView() override { |
| // This will first be called by Widget::Init(), which passes the returned |
| // View* to SetContentsView(), which takes ownership. |
| if (!contents_) |
| contents_ = new TestView(harness_); |
| return contents_; |
| } |
| |
| private: |
| raw_ptr<ViewEventTestBase> harness_; |
| raw_ptr<views::View> contents_ = nullptr; |
| }; |
| |
| ViewEventTestBase::ViewEventTestBase() { |
| // The TestingBrowserProcess must be created in the constructor because there |
| // are tests that require it before SetUp() is called. |
| TestingBrowserProcess::CreateInstance(); |
| |
| // Mojo is initialized here similar to how each browser test case initializes |
| // Mojo when starting. This only works because each interactive_ui_test runs |
| // in a new process. |
| mojo::core::Init(); |
| |
| #if defined(USE_AURA) && !BUILDFLAG(IS_CHROMEOS_ASH) |
| // TODO(pkasting): Determine why the TestScreen in AuraTestHelper is |
| // insufficient for these tests, then either bolster/replace it or fix the |
| // tests. |
| DCHECK(!display::Screen::HasScreen()); |
| #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && defined(USE_OZONE) |
| if (!display::Screen::HasScreen()) |
| screen_ = views::test::TestDesktopScreenOzone::Create(); |
| #endif |
| if (!display::Screen::HasScreen()) |
| screen_ = views::CreateDesktopScreen(); |
| #endif |
| } |
| |
| ViewEventTestBase::~ViewEventTestBase() { |
| TestingBrowserProcess::DeleteInstance(); |
| } |
| |
| void ViewEventTestBase::SetUpTestCase() { |
| ChromeUnitTestSuite::InitializeProviders(); |
| ChromeUnitTestSuite::InitializeResourceBundle(); |
| } |
| |
| void ViewEventTestBase::SetUp() { |
| ChromeViewsTestBase::SetUp(); |
| |
| test_views_delegate()->set_use_desktop_native_widgets(true); |
| |
| window_ = AllocateTestWidget().release(); |
| window_->Init(CreateParams(views::Widget::InitParams::TYPE_WINDOW)); |
| window_->Show(); |
| } |
| |
| void ViewEventTestBase::TearDown() { |
| if (window_) { |
| window_->Close(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| ChromeViewsTestBase::TearDown(); |
| } |
| |
| views::Widget::InitParams ViewEventTestBase::CreateParams( |
| views::Widget::InitParams::Type type) { |
| views::Widget::InitParams params = ChromeViewsTestBase::CreateParams(type); |
| params.delegate = new TestBaseWidgetDelegate(this); // Owns itself. |
| return params; |
| } |
| |
| gfx::Size ViewEventTestBase::GetPreferredSizeForContents() const { |
| return gfx::Size(); |
| } |
| |
| void ViewEventTestBase::Done() { |
| drag_event_thread_.reset(); |
| run_loop_.Quit(); |
| } |
| |
| void ViewEventTestBase::StartMessageLoopAndRunTest() { |
| ASSERT_TRUE( |
| ui_test_utils::ShowAndFocusNativeWindow(window_->GetNativeWindow())); |
| |
| // Flush any pending events to make sure we start with a clean slate. |
| base::RunLoop().RunUntilIdle(); |
| |
| // Schedule a task that starts the test. Need to do this as we're going to |
| // run the message loop. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(&ViewEventTestBase::DoTestOnMessageLoop, |
| base::Unretained(this))); |
| |
| run_loop_.Run(); |
| } |
| |
| scoped_refptr<base::SingleThreadTaskRunner> |
| ViewEventTestBase::GetDragTaskRunner() { |
| #if BUILDFLAG(IS_WIN) |
| // Drag events must be posted from a background thread, since starting a drag |
| // triggers a nested message loop that filters messages other than mouse |
| // events, so further tasks on the main message loop will be blocked. |
| if (!drag_event_thread_) { |
| drag_event_thread_ = std::make_unique<base::Thread>("drag-event-thread"); |
| drag_event_thread_->Start(); |
| } |
| return drag_event_thread_->task_runner(); |
| #else |
| // Drag events must be posted from the current thread, since UI events on many |
| // platforms cannot be posted from background threads. The nested drag |
| // message loop on non-Windows does not filter out non-input events, so these |
| // tasks will run. |
| return base::ThreadTaskRunnerHandle::Get(); |
| #endif |
| } |
| |
| void ViewEventTestBase::RunTestMethod(base::OnceClosure task) { |
| std::move(task).Run(); |
| if (HasFatalFailure()) |
| Done(); |
| } |