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()88 ViewEventTestBase::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()117 ViewEventTestBase::~ViewEventTestBase() {
118   TestingBrowserProcess::DeleteInstance();
119 }
120 
SetUpTestCase()121 void ViewEventTestBase::SetUpTestCase() {
122   ChromeUnitTestSuite::InitializeProviders();
123   ChromeUnitTestSuite::InitializeResourceBundle();
124 }
125 
SetUp()126 void 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()136 void 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)145 views::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() const152 gfx::Size ViewEventTestBase::GetPreferredSizeForContents() const {
153   return gfx::Size();
154 }
155 
Done()156 void ViewEventTestBase::Done() {
157   drag_event_thread_.reset();
158   run_loop_.Quit();
159 }
160 
StartMessageLoopAndRunTest()161 void 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()178 ViewEventTestBase::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)197 void ViewEventTestBase::RunTestMethod(base::OnceClosure task) {
198   std::move(task).Run();
199   if (HasFatalFailure())
200     Done();
201 }
202