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/message_loop/message_loop.h"
9#include "chrome/test/base/chrome_unit_test_suite.h"
10#include "chrome/test/base/interactive_test_utils.h"
11#include "chrome/test/base/testing_browser_process.h"
12#include "chrome/test/base/view_event_test_platform_part.h"
13#include "ui/base/ime/input_method_initializer.h"
14#include "ui/base/test/ui_controls.h"
15#include "ui/compositor/test/context_factories_for_test.h"
16#include "ui/views/view.h"
17#include "ui/views/widget/widget.h"
18
19namespace {
20
21// View subclass that allows you to specify the preferred size.
22class TestView : public views::View {
23 public:
24  TestView() {}
25
26  void SetPreferredSize(const gfx::Size& size) {
27    preferred_size_ = size;
28    PreferredSizeChanged();
29  }
30
31  virtual gfx::Size GetPreferredSize() const OVERRIDE {
32    if (!preferred_size_.IsEmpty())
33      return preferred_size_;
34    return View::GetPreferredSize();
35  }
36
37  virtual void Layout() OVERRIDE {
38    View* child_view = child_at(0);
39    child_view->SetBounds(0, 0, width(), height());
40  }
41
42 private:
43  gfx::Size preferred_size_;
44
45  DISALLOW_COPY_AND_ASSIGN(TestView);
46};
47
48// Delay in background thread before posting mouse move.
49const int kMouseMoveDelayMS = 200;
50
51}  // namespace
52
53ViewEventTestBase::ViewEventTestBase()
54  : window_(NULL),
55    content_view_(NULL) {
56  // The TestingBrowserProcess must be created in the constructor because there
57  // are tests that require it before SetUp() is called.
58  TestingBrowserProcess::CreateInstance();
59}
60
61void ViewEventTestBase::Done() {
62  base::MessageLoop::current()->Quit();
63
64  // If we're in a nested message loop, as is the case with menus, we
65  // need to quit twice. The second quit does that for us. Finish all
66  // pending UI events before posting closure because events it may be
67  // executed before UI events are executed.
68  ui_controls::RunClosureAfterAllPendingUIEvents(
69      base::MessageLoop::QuitClosure());
70}
71
72void ViewEventTestBase::SetUpTestCase() {
73  ChromeUnitTestSuite::InitializeProviders();
74  ChromeUnitTestSuite::InitializeResourceBundle();
75}
76
77void ViewEventTestBase::SetUp() {
78  views::ViewsDelegate::views_delegate = &views_delegate_;
79  ui::InitializeInputMethodForTesting();
80
81  // The ContextFactory must exist before any Compositors are created.
82  bool enable_pixel_output = false;
83  ui::ContextFactory* context_factory =
84      ui::InitializeContextFactoryForTests(enable_pixel_output);
85
86  platform_part_.reset(ViewEventTestPlatformPart::Create(context_factory));
87  gfx::NativeWindow context = platform_part_->GetContext();
88  window_ = views::Widget::CreateWindowWithContext(this, context);
89}
90
91void ViewEventTestBase::TearDown() {
92  if (window_) {
93    window_->Close();
94    content::RunAllPendingInMessageLoop();
95    window_ = NULL;
96  }
97
98  ui::Clipboard::DestroyClipboardForCurrentThread();
99  platform_part_.reset();
100
101  ui::TerminateContextFactoryForTests();
102
103  ui::ShutdownInputMethodForTesting();
104  views::ViewsDelegate::views_delegate = NULL;
105}
106
107bool ViewEventTestBase::CanResize() const {
108  return true;
109}
110
111views::View* ViewEventTestBase::GetContentsView() {
112  if (!content_view_) {
113    // Wrap the real view (as returned by CreateContentsView) in a View so
114    // that we can customize the preferred size.
115    TestView* test_view = new TestView();
116    test_view->SetPreferredSize(GetPreferredSize());
117    test_view->AddChildView(CreateContentsView());
118    content_view_ = test_view;
119  }
120  return content_view_;
121}
122
123const views::Widget* ViewEventTestBase::GetWidget() const {
124  return content_view_->GetWidget();
125}
126
127views::Widget* ViewEventTestBase::GetWidget() {
128  return content_view_->GetWidget();
129}
130
131ViewEventTestBase::~ViewEventTestBase() {
132  TestingBrowserProcess::DeleteInstance();
133}
134
135void ViewEventTestBase::StartMessageLoopAndRunTest() {
136  ASSERT_TRUE(
137      ui_test_utils::ShowAndFocusNativeWindow(window_->GetNativeWindow()));
138
139  // Flush any pending events to make sure we start with a clean slate.
140  content::RunAllPendingInMessageLoop();
141
142  // Schedule a task that starts the test. Need to do this as we're going to
143  // run the message loop.
144  base::MessageLoop::current()->PostTask(
145      FROM_HERE, base::Bind(&ViewEventTestBase::DoTestOnMessageLoop, this));
146
147  content::RunMessageLoop();
148}
149
150gfx::Size ViewEventTestBase::GetPreferredSize() const {
151  return gfx::Size();
152}
153
154void ViewEventTestBase::ScheduleMouseMoveInBackground(int x, int y) {
155  if (!dnd_thread_.get()) {
156    dnd_thread_.reset(new base::Thread("mouse-move-thread"));
157    dnd_thread_->Start();
158  }
159  dnd_thread_->message_loop()->PostDelayedTask(
160      FROM_HERE,
161      base::Bind(base::IgnoreResult(&ui_controls::SendMouseMove), x, y),
162      base::TimeDelta::FromMilliseconds(kMouseMoveDelayMS));
163}
164
165void ViewEventTestBase::StopBackgroundThread() {
166  dnd_thread_.reset(NULL);
167}
168
169void ViewEventTestBase::RunTestMethod(const base::Closure& task) {
170  StopBackgroundThread();
171
172  task.Run();
173  if (HasFatalFailure())
174    Done();
175}
176