1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/views/test/view_event_test_base.h" 6 7 #include "base/bind.h" 8 #include "base/location.h" 9 #include "base/threading/thread_task_runner_handle.h" 10 #include "build/build_config.h" 11 #include "chrome/test/base/chrome_unit_test_suite.h" 12 #include "chrome/test/base/interactive_test_utils.h" 13 #include "chrome/test/base/testing_browser_process.h" 14 #include "mojo/core/embedder/embedder.h" 15 #include "ui/views/layout/fill_layout.h" 16 #include "ui/views/view.h" 17 #include "ui/views/widget/widget.h" 18 #include "ui/views/widget/widget_delegate.h" 19 20 #if defined(USE_AURA) && !defined(OS_CHROMEOS) 21 #include "ui/display/screen.h" 22 #include "ui/views/widget/desktop_aura/desktop_screen.h" 23 24 #if defined(USE_X11) 25 #include "ui/views/test/test_desktop_screen_x11.h" 26 #endif // defined(USE_X11) 27 28 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_OZONE) 29 #include "ui/views/test/test_desktop_screen_ozone.h" 30 #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_OZONE) 31 #endif 32 33 namespace { 34 35 // View that keeps its preferred size in sync with what |harness| requests. 36 class TestView : public views::View { 37 public: TestView(ViewEventTestBase * harness)38 explicit TestView(ViewEventTestBase* harness) : harness_(harness) { 39 SetLayoutManager(std::make_unique<views::FillLayout>()); 40 AddChildView(harness_->CreateContentsView()); 41 } 42 TestView(const TestView&) = delete; 43 TestView& operator=(const TestView&) = delete; 44 ~TestView() override = default; 45 CalculatePreferredSize() const46 gfx::Size CalculatePreferredSize() const override { 47 return harness_->GetPreferredSizeForContents(); 48 } 49 50 private: 51 ViewEventTestBase* harness_; 52 }; 53 54 } // namespace 55 56 class TestBaseWidgetDelegate : public views::WidgetDelegate { 57 public: TestBaseWidgetDelegate(ViewEventTestBase * harness)58 explicit TestBaseWidgetDelegate(ViewEventTestBase* harness) 59 : harness_(harness) { 60 SetCanResize(true); 61 SetOwnedByWidget(true); 62 } 63 TestBaseWidgetDelegate(const TestBaseWidgetDelegate&) = delete; 64 TestBaseWidgetDelegate& operator=(const TestBaseWidgetDelegate&) = delete; 65 ~TestBaseWidgetDelegate() override = default; 66 67 // views::WidgetDelegate: WindowClosing()68 void WindowClosing() override { harness_->window_ = nullptr; } GetWidget()69 views::Widget* GetWidget() override { 70 return contents_ ? contents_->GetWidget() : nullptr; 71 } GetWidget() const72 const views::Widget* GetWidget() const override { 73 return contents_ ? contents_->GetWidget() : nullptr; 74 } GetContentsView()75 views::View* GetContentsView() override { 76 // This will first be called by Widget::Init(), which passes the returned 77 // View* to SetContentsView(), which takes ownership. 78 if (!contents_) 79 contents_ = new TestView(harness_); 80 return contents_; 81 } 82 83 private: 84 ViewEventTestBase* harness_; 85 views::View* contents_ = nullptr; 86 }; 87 ViewEventTestBase()88ViewEventTestBase::ViewEventTestBase() { 89 // The TestingBrowserProcess must be created in the constructor because there 90 // are tests that require it before SetUp() is called. 91 TestingBrowserProcess::CreateInstance(); 92 93 // Mojo is initialized here similar to how each browser test case initializes 94 // Mojo when starting. This only works because each interactive_ui_test runs 95 // in a new process. 96 mojo::core::Init(); 97 98 #if defined(USE_AURA) && !defined(OS_CHROMEOS) 99 // TODO(pkasting): Determine why the TestScreen in AuraTestHelper is 100 // insufficient for these tests, then either bolster/replace it or fix the 101 // tests. 102 DCHECK(!display::Screen::GetScreen()); 103 #if defined(USE_X11) 104 if (!features::IsUsingOzonePlatform()) 105 views::test::TestDesktopScreenX11::GetInstance(); 106 #endif // defined(USE_X11) 107 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_OZONE) 108 if (!display::Screen::GetScreen()) 109 display::Screen::SetScreenInstance( 110 views::test::TestDesktopScreenOzone::GetInstance()); 111 #endif 112 if (!display::Screen::GetScreen()) 113 screen_.reset(views::CreateDesktopScreen()); 114 #endif 115 } 116 ~ViewEventTestBase()117ViewEventTestBase::~ViewEventTestBase() { 118 TestingBrowserProcess::DeleteInstance(); 119 } 120 SetUpTestCase()121void ViewEventTestBase::SetUpTestCase() { 122 ChromeUnitTestSuite::InitializeProviders(); 123 ChromeUnitTestSuite::InitializeResourceBundle(); 124 } 125 SetUp()126void ViewEventTestBase::SetUp() { 127 ChromeViewsTestBase::SetUp(); 128 129 test_views_delegate()->set_use_desktop_native_widgets(true); 130 131 window_ = AllocateTestWidget().release(); 132 window_->Init(CreateParams(views::Widget::InitParams::TYPE_WINDOW)); 133 window_->Show(); 134 } 135 TearDown()136void ViewEventTestBase::TearDown() { 137 if (window_) { 138 window_->Close(); 139 base::RunLoop().RunUntilIdle(); 140 } 141 142 ChromeViewsTestBase::TearDown(); 143 } 144 CreateParams(views::Widget::InitParams::Type type)145views::Widget::InitParams ViewEventTestBase::CreateParams( 146 views::Widget::InitParams::Type type) { 147 views::Widget::InitParams params = ChromeViewsTestBase::CreateParams(type); 148 params.delegate = new TestBaseWidgetDelegate(this); // Owns itself. 149 return params; 150 } 151 GetPreferredSizeForContents() const152gfx::Size ViewEventTestBase::GetPreferredSizeForContents() const { 153 return gfx::Size(); 154 } 155 Done()156void ViewEventTestBase::Done() { 157 drag_event_thread_.reset(); 158 run_loop_.Quit(); 159 } 160 StartMessageLoopAndRunTest()161void ViewEventTestBase::StartMessageLoopAndRunTest() { 162 ASSERT_TRUE( 163 ui_test_utils::ShowAndFocusNativeWindow(window_->GetNativeWindow())); 164 165 // Flush any pending events to make sure we start with a clean slate. 166 base::RunLoop().RunUntilIdle(); 167 168 // Schedule a task that starts the test. Need to do this as we're going to 169 // run the message loop. 170 base::ThreadTaskRunnerHandle::Get()->PostTask( 171 FROM_HERE, base::BindOnce(&ViewEventTestBase::DoTestOnMessageLoop, 172 base::Unretained(this))); 173 174 run_loop_.Run(); 175 } 176 177 scoped_refptr<base::SingleThreadTaskRunner> GetDragTaskRunner()178ViewEventTestBase::GetDragTaskRunner() { 179 #if defined(OS_WIN) 180 // Drag events must be posted from a background thread, since starting a drag 181 // triggers a nested message loop that filters messages other than mouse 182 // events, so further tasks on the main message loop will be blocked. 183 if (!drag_event_thread_) { 184 drag_event_thread_ = std::make_unique<base::Thread>("drag-event-thread"); 185 drag_event_thread_->Start(); 186 } 187 return drag_event_thread_->task_runner(); 188 #else 189 // Drag events must be posted from the current thread, since UI events on many 190 // platforms cannot be posted from background threads. The nested drag 191 // message loop on non-Windows does not filter out non-input events, so these 192 // tasks will run. 193 return base::ThreadTaskRunnerHandle::Get(); 194 #endif 195 } 196 RunTestMethod(base::OnceClosure task)197void ViewEventTestBase::RunTestMethod(base::OnceClosure task) { 198 std::move(task).Run(); 199 if (HasFatalFailure()) 200 Done(); 201 } 202