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/test/base/view_event_test_base.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/strings/string_number_conversions.h" 11 #include "chrome/test/base/interactive_test_utils.h" 12 #include "chrome/test/base/ui_test_utils.h" 13 #include "content/public/browser/browser_thread.h" 14 #include "ui/base/ime/input_method_initializer.h" 15 #include "ui/base/test/ui_controls.h" 16 #include "ui/message_center/message_center.h" 17 #include "ui/views/view.h" 18 #include "ui/views/widget/desktop_aura/desktop_screen.h" 19 #include "ui/views/widget/widget.h" 20 21 #if defined(USE_ASH) 22 #include "ash/shell.h" 23 #include "ash/test/test_session_state_delegate.h" 24 #include "ash/test/test_shell_delegate.h" 25 #if defined(OS_WIN) 26 #include "ui/compositor/compositor.h" 27 #endif 28 #endif 29 30 #if defined(USE_AURA) 31 #include "ui/aura/client/event_client.h" 32 #include "ui/aura/env.h" 33 #include "ui/aura/root_window.h" 34 #include "ui/aura/test/aura_test_helper.h" 35 #endif 36 37 #if defined(OS_CHROMEOS) 38 #include "chromeos/audio/cras_audio_handler.h" 39 #endif 40 41 namespace { 42 43 // View subclass that allows you to specify the preferred size. 44 class TestView : public views::View { 45 public: 46 TestView() {} 47 48 void SetPreferredSize(const gfx::Size& size) { 49 preferred_size_ = size; 50 PreferredSizeChanged(); 51 } 52 53 virtual gfx::Size GetPreferredSize() OVERRIDE { 54 if (!preferred_size_.IsEmpty()) 55 return preferred_size_; 56 return View::GetPreferredSize(); 57 } 58 59 virtual void Layout() OVERRIDE { 60 View* child_view = child_at(0); 61 child_view->SetBounds(0, 0, width(), height()); 62 } 63 64 private: 65 gfx::Size preferred_size_; 66 67 DISALLOW_COPY_AND_ASSIGN(TestView); 68 }; 69 70 // Delay in background thread before posting mouse move. 71 const int kMouseMoveDelayMS = 200; 72 73 } // namespace 74 75 ViewEventTestBase::ViewEventTestBase() 76 : window_(NULL), 77 content_view_(NULL) { 78 } 79 80 void ViewEventTestBase::Done() { 81 base::MessageLoop::current()->Quit(); 82 83 #if defined(OS_WIN) && !defined(USE_AURA) 84 // We need to post a message to tickle the Dispatcher getting called and 85 // exiting out of the nested loop. Without this the quit never runs. 86 if (window_) 87 PostMessage(window_->GetNativeWindow(), WM_USER, 0, 0); 88 #endif 89 90 // If we're in a nested message loop, as is the case with menus, we 91 // need to quit twice. The second quit does that for us. Finish all 92 // pending UI events before posting closure because events it may be 93 // executed before UI events are executed. 94 ui_controls::RunClosureAfterAllPendingUIEvents( 95 base::MessageLoop::QuitClosure()); 96 } 97 98 void ViewEventTestBase::SetUp() { 99 ui::InitializeInputMethodForTesting(); 100 gfx::NativeView context = NULL; 101 #if defined(USE_ASH) 102 #if defined(OS_WIN) 103 // http://crbug.com/154081 use ash::Shell code path below on win_ash bots when 104 // interactive_ui_tests is brought up on that platform. 105 gfx::Screen::SetScreenInstance( 106 gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen()); 107 108 // The ContextFactory must exist before any Compositors are created. The 109 // ash::Shell code path below handles this, but since we skip it we must 110 // do this here. 111 bool allow_test_contexts = true; 112 ui::Compositor::InitializeContextFactoryForTests(allow_test_contexts); 113 #else // !OS_WIN 114 // Ash Shell can't just live on its own without a browser process, we need to 115 // also create the message center. 116 message_center::MessageCenter::Initialize(); 117 #if defined(OS_CHROMEOS) 118 chromeos::CrasAudioHandler::InitializeForTesting(); 119 #endif // OS_CHROMEOS 120 ash::test::TestShellDelegate* shell_delegate = 121 new ash::test::TestShellDelegate(); 122 ash::Shell::CreateInstance(shell_delegate); 123 shell_delegate->test_session_state_delegate() 124 ->SetActiveUserSessionStarted(true); 125 context = ash::Shell::GetPrimaryRootWindow(); 126 #endif // !OS_WIN 127 #elif defined(USE_AURA) 128 // Instead of using the ash shell, use an AuraTestHelper to create and manage 129 // the test screen. 130 aura_test_helper_.reset( 131 new aura::test::AuraTestHelper(base::MessageLoopForUI::current())); 132 aura_test_helper_->SetUp(); 133 context = aura_test_helper_->root_window(); 134 #endif // USE_AURA 135 window_ = views::Widget::CreateWindowWithContext(this, context); 136 } 137 138 void ViewEventTestBase::TearDown() { 139 if (window_) { 140 #if defined(OS_WIN) && !defined(USE_AURA) 141 DestroyWindow(window_->GetNativeWindow()); 142 #else 143 window_->Close(); 144 content::RunAllPendingInMessageLoop(); 145 #endif 146 window_ = NULL; 147 } 148 #if defined(USE_ASH) 149 #if defined(OS_WIN) 150 #else 151 ash::Shell::DeleteInstance(); 152 #if defined(OS_CHROMEOS) 153 chromeos::CrasAudioHandler::Shutdown(); 154 #endif 155 // Ash Shell can't just live on its own without a browser process, we need to 156 // also shut down the message center. 157 message_center::MessageCenter::Shutdown(); 158 aura::Env::DeleteInstance(); 159 #endif 160 #elif defined(USE_AURA) 161 aura_test_helper_->TearDown(); 162 #endif 163 ui::ShutdownInputMethodForTesting(); 164 } 165 166 bool ViewEventTestBase::CanResize() const { 167 return true; 168 } 169 170 views::View* ViewEventTestBase::GetContentsView() { 171 if (!content_view_) { 172 // Wrap the real view (as returned by CreateContentsView) in a View so 173 // that we can customize the preferred size. 174 TestView* test_view = new TestView(); 175 test_view->SetPreferredSize(GetPreferredSize()); 176 test_view->AddChildView(CreateContentsView()); 177 content_view_ = test_view; 178 } 179 return content_view_; 180 } 181 182 const views::Widget* ViewEventTestBase::GetWidget() const { 183 return content_view_->GetWidget(); 184 } 185 186 views::Widget* ViewEventTestBase::GetWidget() { 187 return content_view_->GetWidget(); 188 } 189 190 ViewEventTestBase::~ViewEventTestBase() { 191 } 192 193 void ViewEventTestBase::StartMessageLoopAndRunTest() { 194 ASSERT_TRUE( 195 ui_test_utils::ShowAndFocusNativeWindow(window_->GetNativeWindow())); 196 197 // Flush any pending events to make sure we start with a clean slate. 198 content::RunAllPendingInMessageLoop(); 199 200 // Schedule a task that starts the test. Need to do this as we're going to 201 // run the message loop. 202 base::MessageLoop::current()->PostTask( 203 FROM_HERE, base::Bind(&ViewEventTestBase::DoTestOnMessageLoop, this)); 204 205 content::RunMessageLoop(); 206 } 207 208 gfx::Size ViewEventTestBase::GetPreferredSize() { 209 return gfx::Size(); 210 } 211 212 void ViewEventTestBase::ScheduleMouseMoveInBackground(int x, int y) { 213 if (!dnd_thread_.get()) { 214 dnd_thread_.reset(new base::Thread("mouse-move-thread")); 215 dnd_thread_->Start(); 216 } 217 dnd_thread_->message_loop()->PostDelayedTask( 218 FROM_HERE, 219 base::Bind(base::IgnoreResult(&ui_controls::SendMouseMove), x, y), 220 base::TimeDelta::FromMilliseconds(kMouseMoveDelayMS)); 221 } 222 223 void ViewEventTestBase::StopBackgroundThread() { 224 dnd_thread_.reset(NULL); 225 } 226 227 void ViewEventTestBase::RunTestMethod(const base::Closure& task) { 228 StopBackgroundThread(); 229 230 task.Run(); 231 if (HasFatalFailure()) 232 Done(); 233 } 234