blob: dda24a6d61c6ed41b7a61c2a5d6b59a5705280f5 [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.
#include "chrome/browser/ui/views/test/view_event_test_base.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/ui/views/test/view_event_test_platform_part.h"
#include "chrome/test/base/chrome_unit_test_suite.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/testing_browser_process.h"
#include "mojo/core/embedder/embedder.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/ime/input_method_initializer.h"
#include "ui/compositor/test/context_factories_for_test.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#if defined(OS_CHROMEOS)
#include "ash/test/ui_controls_factory_ash.h"
#include "ui/aura/test/ui_controls_factory_aura.h"
#include "ui/aura/window.h"
#include "ui/base/test/ui_controls_aura.h"
#include "ui/base/ui_base_features.h"
#endif
namespace {
// View subclass that allows you to specify the preferred size.
class TestView : public views::View {
public:
explicit TestView(ViewEventTestBase* harness) : harness_(harness) {}
gfx::Size CalculatePreferredSize() const override {
return harness_->GetPreferredSizeForContents();
}
void Layout() override {
// Permit a test to remove the view being tested from the hierarchy, then
// still handle a _NET_WM_STATE event on Linux during teardown that triggers
// layout.
if (children().empty())
return;
View* child_view = child_at(0);
child_view->SetBounds(0, 0, width(), height());
}
private:
ViewEventTestBase* harness_;
DISALLOW_COPY_AND_ASSIGN(TestView);
};
} // namespace
ViewEventTestBase::ViewEventTestBase() : window_(NULL), content_view_(NULL) {
// The TestingBrowserProcess must be created in the constructor because there
// are tests that require it before SetUp() is called.
TestingBrowserProcess::CreateInstance();
}
void ViewEventTestBase::Done() {
drag_event_thread_.reset();
run_loop_.Quit();
}
void ViewEventTestBase::SetUpTestCase() {
ChromeUnitTestSuite::InitializeProviders();
ChromeUnitTestSuite::InitializeResourceBundle();
}
void ViewEventTestBase::SetUp() {
// Mojo is initialized here similar to how each browser test case initializes
// Mojo when starting. This only works because each interactive_ui_test runs
// in a new process.
mojo::core::Init();
ui::InitializeInputMethodForTesting();
// The ContextFactory must exist before any Compositors are created.
bool enable_pixel_output = false;
ui::ContextFactory* context_factory = nullptr;
ui::ContextFactoryPrivate* context_factory_private = nullptr;
ui::InitializeContextFactoryForTests(enable_pixel_output, &context_factory,
&context_factory_private);
views_delegate_.set_context_factory(context_factory);
views_delegate_.set_context_factory_private(context_factory_private);
views_delegate_.set_use_desktop_native_widgets(true);
platform_part_.reset(ViewEventTestPlatformPart::Create(
context_factory, context_factory_private));
gfx::NativeWindow context = platform_part_->GetContext();
window_ = views::Widget::CreateWindowWithContext(this, context);
window_->Show();
#if defined(OS_CHROMEOS)
ui_controls::InstallUIControlsAura(
features::IsUsingWindowService()
? aura::test::CreateUIControlsAura(
window_->GetNativeWindow()->GetHost())
: ash::test::CreateAshUIControls());
#endif
}
void ViewEventTestBase::TearDown() {
if (window_) {
window_->Close();
base::RunLoop().RunUntilIdle();
window_ = NULL;
}
ui::Clipboard::DestroyClipboardForCurrentThread();
platform_part_.reset();
ui::TerminateContextFactoryForTests();
ui::ShutdownInputMethodForTesting();
}
gfx::Size ViewEventTestBase::GetPreferredSizeForContents() const {
return gfx::Size();
}
bool ViewEventTestBase::CanResize() const {
return true;
}
views::View* ViewEventTestBase::GetContentsView() {
if (!content_view_) {
// Wrap the real view (as returned by CreateContentsView) in a View so
// that we can customize the preferred size.
TestView* test_view = new TestView(this);
test_view->AddChildView(CreateContentsView());
content_view_ = test_view;
}
return content_view_;
}
const views::Widget* ViewEventTestBase::GetWidget() const {
return content_view_->GetWidget();
}
views::Widget* ViewEventTestBase::GetWidget() {
return content_view_->GetWidget();
}
ViewEventTestBase::~ViewEventTestBase() {
TestingBrowserProcess::DeleteInstance();
}
void ViewEventTestBase::StartMessageLoopAndRunTest() {
ASSERT_TRUE(
ui_test_utils::ShowAndFocusNativeWindow(window_->GetNativeWindow()));
// Flush any pending events to make sure we start with a clean slate.
base::RunLoop().RunUntilIdle();
// Schedule a task that starts the test. Need to do this as we're going to
// run the message loop.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&ViewEventTestBase::DoTestOnMessageLoop,
base::Unretained(this)));
run_loop_.Run();
}
scoped_refptr<base::SingleThreadTaskRunner>
ViewEventTestBase::GetDragTaskRunner() {
#if defined(OS_WIN)
// Drag events must be posted from a background thread, since starting a drag
// triggers a nested message loop that filters messages other than mouse
// events, so further tasks on the main message loop will be blocked.
if (!drag_event_thread_) {
drag_event_thread_ = std::make_unique<base::Thread>("drag-event-thread");
drag_event_thread_->Start();
}
return drag_event_thread_->task_runner();
#else
// Drag events must be posted from the current thread, since UI events on many
// platforms cannot be posted from background threads. The nested drag
// message loop on non-Windows does not filter out non-input events, so these
// tasks will run.
return base::ThreadTaskRunnerHandle::Get();
#endif
}
void ViewEventTestBase::RunTestMethod(base::OnceClosure task) {
std::move(task).Run();
if (HasFatalFailure())
Done();
}