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#ifndef CHROME_TEST_BASE_VIEW_EVENT_TEST_BASE_H_ 6#define CHROME_TEST_BASE_VIEW_EVENT_TEST_BASE_H_ 7 8// We only want to use ViewEventTestBase in test targets which properly 9// isolate each test case by running each test in a separate process. 10// This way if a test hangs the test launcher can reliably terminate it. 11#if defined(HAS_OUT_OF_PROC_TEST_RUNNER) 12 13#include "base/bind.h" 14#include "base/callback.h" 15#include "base/compiler_specific.h" 16#include "base/message_loop/message_loop.h" 17#include "base/threading/thread.h" 18#include "chrome/browser/ui/views/chrome_views_delegate.h" 19#include "content/public/test/test_browser_thread_bundle.h" 20#include "testing/gtest/include/gtest/gtest.h" 21#include "ui/views/widget/widget_delegate.h" 22 23#if defined(OS_WIN) 24#include "ui/base/win/scoped_ole_initializer.h" 25#endif 26 27namespace gfx { 28class Size; 29} 30 31class ViewEventTestPlatformPart; 32 33// Base class for Views based tests that dispatch events. 34// 35// As views based event test involves waiting for events to be processed, 36// writing a views based test is slightly different than that of writing 37// other unit tests. In particular when the test fails or is done you need 38// to stop the message loop. This can be done by way of invoking the Done 39// method. 40// 41// Any delayed callbacks should be done by way of CreateEventTask. 42// CreateEventTask checks to see if ASSERT_XXX has been invoked after invoking 43// the task. If there was a failure Done is invoked and the test stops. 44// 45// ViewEventTestBase creates a Window with the View returned from 46// CreateContentsView. The preferred size for the view can be customized by 47// overriding GetPreferredSize. If you do not override GetPreferredSize the 48// preferred size of the view returned from CreateContentsView is used. 49// 50// Subclasses of ViewEventTestBase must implement two methods: 51// . DoTestOnMessageLoop: invoked when the message loop is running. Run your 52// test here, invoke Done when done. 53// . CreateContentsView: returns the view to place in the window. 54// 55// Once you have created a ViewEventTestBase use the macro VIEW_TEST to define 56// the fixture. 57// 58// I encountered weird timing problems in initiating dragging and drop that 59// necessitated ugly hacks. In particular when the hook installed by 60// ui_controls received the mouse event and posted a task that task was not 61// processed. To work around this use the following pattern when initiating 62// dnd: 63// // Schedule the mouse move at a location slightly different from where 64// // you really want to move to. 65// ui_controls::SendMouseMoveNotifyWhenDone(loc.x + 10, loc.y, 66// base::Bind(&YYY, this)); 67// // Then use this to schedule another mouse move. 68// ScheduleMouseMoveInBackground(loc.x, loc.y); 69 70class ViewEventTestBase : public views::WidgetDelegate, 71 public testing::Test { 72 public: 73 ViewEventTestBase(); 74 75 // Invoke when done either because of failure or success. Quits the message 76 // loop. 77 void Done(); 78 79 static void SetUpTestCase(); 80 81 // Creates a window. 82 virtual void SetUp() OVERRIDE; 83 84 // Destroys the window. 85 virtual void TearDown() OVERRIDE; 86 87 // Overridden from views::WidgetDelegate: 88 virtual bool CanResize() const OVERRIDE; 89 virtual views::View* GetContentsView() OVERRIDE; 90 virtual const views::Widget* GetWidget() const OVERRIDE; 91 virtual views::Widget* GetWidget() OVERRIDE; 92 93 // Overridden to do nothing so that this class can be used in runnable tasks. 94 void AddRef() {} 95 void Release() {} 96 97 protected: 98 virtual ~ViewEventTestBase(); 99 100 // Returns the view that is added to the window. 101 virtual views::View* CreateContentsView() = 0; 102 103 // Called once the message loop is running. 104 virtual void DoTestOnMessageLoop() = 0; 105 106 // Invoke from test main. Shows the window, starts the message loop and 107 // schedules a task that invokes DoTestOnMessageLoop. 108 void StartMessageLoopAndRunTest(); 109 110 // Returns an empty Size. Subclasses that want a preferred size other than 111 // that of the View returned by CreateContentsView should override this 112 // appropriately. 113 virtual gfx::Size GetPreferredSize() const; 114 115 // Creates a task that calls the specified method back. The specified 116 // method is called in such a way that if there are any test failures 117 // Done is invoked. 118 template <class T, class Method> 119 base::Closure CreateEventTask(T* target, Method method) { 120 return base::Bind(&ViewEventTestBase::RunTestMethod, this, 121 base::Bind(method, target)); 122 } 123 124 // Spawns a new thread posts a MouseMove in the background. 125 void ScheduleMouseMoveInBackground(int x, int y); 126 127 views::Widget* window_; 128 129 private: 130 // Stops the thread started by ScheduleMouseMoveInBackground. 131 void StopBackgroundThread(); 132 133 // Callback from CreateEventTask. Stops the background thread, runs the 134 // supplied task and if there are failures invokes Done. 135 void RunTestMethod(const base::Closure& task); 136 137 // The content of the Window. 138 views::View* content_view_; 139 140 // Thread for posting background MouseMoves. 141 scoped_ptr<base::Thread> dnd_thread_; 142 143 content::TestBrowserThreadBundle thread_bundle_; 144 145#if defined(OS_WIN) 146 ui::ScopedOleInitializer ole_initializer_; 147#endif 148 149 scoped_ptr<ViewEventTestPlatformPart> platform_part_; 150 151 ChromeViewsDelegate views_delegate_; 152 153 DISALLOW_COPY_AND_ASSIGN(ViewEventTestBase); 154}; 155 156// Convenience macro for defining a ViewEventTestBase. See class description 157// of ViewEventTestBase for details. 158#define VIEW_TEST(test_class, name) \ 159 TEST_F(test_class, name) {\ 160 StartMessageLoopAndRunTest();\ 161 } 162 163#endif // defined(HAS_OUT_OF_PROC_TEST_RUNNER) 164 165#endif // CHROME_TEST_BASE_VIEW_EVENT_TEST_BASE_H_ 166