blob: 566c1c6d70b86a0a31442a097875e44b523f22c9 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_TEST_BASE_VIEW_EVENT_TEST_BASE_H_
#define CHROME_TEST_BASE_VIEW_EVENT_TEST_BASE_H_
// We only want to use ViewEventTestBase in test targets which properly
// isolate each test case by running each test in a separate process.
// This way if a test hangs the test launcher can reliably terminate it.
#if defined(HAS_OUT_OF_PROC_TEST_RUNNER)
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "chrome/test/views/chrome_test_views_delegate.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/widget/widget_delegate.h"
#if defined(OS_WIN)
#include "ui/base/win/scoped_ole_initializer.h"
#endif
namespace gfx {
class Size;
}
class ViewEventTestPlatformPart;
// Base class for Views based tests that dispatch events.
//
// As views based event test involves waiting for events to be processed,
// writing a views based test is slightly different than that of writing
// other unit tests. In particular when the test fails or is done you need
// to stop the message loop. This can be done by way of invoking the Done
// method.
//
// Any delayed callbacks should be done by way of CreateEventTask.
// CreateEventTask checks to see if ASSERT_XXX has been invoked after invoking
// the task. If there was a failure Done is invoked and the test stops.
//
// ViewEventTestBase creates a Window with the View returned from
// CreateContentsView. The preferred size for the view can be customized by
// overriding GetPreferredSizeForContents. If you do not override
// GetPreferredSizeForContents the preferred size of the view returned from
// CreateContentsView is used.
//
// Subclasses of ViewEventTestBase must implement two methods:
// . DoTestOnMessageLoop: invoked when the message loop is running. Run your
// test here, invoke Done when done.
// . CreateContentsView: returns the view to place in the window.
//
// Once you have created a ViewEventTestBase use the macro VIEW_TEST to define
// the fixture.
//
// Testing drag and drop is tricky because the mouse move that initiates drag
// and drop may trigger a nested native event loop that waits for more mouse
// messages. Thus code needs an initial mouse move to start the drag, then a
// mouse move enqueued from a background thread to finish the drag (since tasks
// on the main thread may be stalled waiting for the nested loop to terminate).
// You can do this with a pattern like:
// // Schedule the mouse move at a location slightly different from where
// // you really want to move to to start the drag.
// ui_controls::SendMouseMoveNotifyWhenDone(loc.x + 10, loc.y,
// base::BindOnce(&YYY, this));
// // Then schedule another mouse move to finish it.
// ScheduleMouseMoveInBackground(loc.x, loc.y);
class ViewEventTestBase : public views::WidgetDelegate,
public testing::Test {
public:
ViewEventTestBase();
// Invoke when done either because of failure or success. Quits the message
// loop.
void Done();
static void SetUpTestCase();
// Creates a window.
void SetUp() override;
// Destroys the window.
void TearDown() override;
// Returns an empty Size. Subclasses that want a preferred size other than
// that of the View returned by CreateContentsView should override this
// appropriately.
virtual gfx::Size GetPreferredSizeForContents() const;
// Overridden from views::WidgetDelegate:
bool CanResize() const override;
views::View* GetContentsView() override;
const views::Widget* GetWidget() const override;
views::Widget* GetWidget() override;
protected:
~ViewEventTestBase() override;
// Returns the view that is added to the window.
virtual views::View* CreateContentsView() = 0;
// Called once the message loop is running.
virtual void DoTestOnMessageLoop() = 0;
// Invoke from test main. Shows the window, starts the message loop and
// schedules a task that invokes DoTestOnMessageLoop.
void StartMessageLoopAndRunTest();
// Creates a task that calls the specified method back. The specified
// method is called in such a way that if there are any test failures
// Done is invoked.
template <class T, class Method>
base::Closure CreateEventTask(T* target, Method method) {
return base::Bind(&ViewEventTestBase::RunTestMethod, base::Unretained(this),
base::Bind(method, base::Unretained(target)));
}
// Spawns a new thread posts a MouseMove in the background.
void ScheduleMouseMoveInBackground(int x, int y);
views::Widget* window_;
private:
// Stops the thread started by ScheduleMouseMoveInBackground.
void StopBackgroundThread();
// Callback from CreateEventTask. Stops the background thread, runs the
// supplied task and if there are failures invokes Done.
void RunTestMethod(const base::Closure& task);
// The content of the Window.
views::View* content_view_;
// Thread for posting background MouseMoves.
std::unique_ptr<base::Thread> dnd_thread_;
content::TestBrowserThreadBundle thread_bundle_;
#if defined(OS_WIN)
ui::ScopedOleInitializer ole_initializer_;
#endif
std::unique_ptr<ViewEventTestPlatformPart> platform_part_;
ChromeTestViewsDelegate views_delegate_;
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(ViewEventTestBase);
};
// Convenience macro for defining a ViewEventTestBase. See class description
// of ViewEventTestBase for details.
#define VIEW_TEST(test_class, name) \
TEST_F(test_class, name) {\
StartMessageLoopAndRunTest();\
}
#endif // defined(HAS_OUT_OF_PROC_TEST_RUNNER)
#endif // CHROME_TEST_BASE_VIEW_EVENT_TEST_BASE_H_