Skip to content

Commit

Permalink
Add a whole-app-list unit test.
Browse files Browse the repository at this point in the history
This establishes a test harness for doing tests that touch on multiple
parts of the app launcher UI, or that need coverage for the various app
launcher configurations: Ash or Desktop; regular or experimental
"landscape" mode.

It currently just tests displaying and closing the app launcher in its
default state with a flat (no folders), 2- or 3-page model.

BUG=169114
TEST=Added unit tests AppListViewTest{Aura,Desktop}.*

Review URL: https://codereview.chromium.org/210083009

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@260762 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
tapted@chromium.org committed Apr 1, 2014
1 parent 50dd59b commit 7e5b41a
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ void AppListServiceWin::OnLoadProfileForWarmup(Profile* initial_profile) {
}

void AppListServiceWin::SetAppListNextPaintCallback(void (*callback)()) {
app_list::AppListView::SetNextPaintCallback(callback);
app_list::AppListView::SetNextPaintCallback(base::Bind(callback));
}

void AppListServiceWin::HandleFirstRun() {
Expand Down
1 change: 1 addition & 0 deletions ui/app_list/app_list.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@
'cocoa/test/apps_grid_controller_test_helper.mm',
'test/run_all_unittests.cc',
'views/app_list_main_view_unittest.cc',
'views/app_list_view_unittest.cc',
'views/apps_grid_view_unittest.cc',
'views/folder_header_view_unittest.cc',
'views/search_box_view_unittest.cc',
Expand Down
11 changes: 6 additions & 5 deletions ui/app_list/views/app_list_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ namespace app_list {

namespace {

void (*g_next_paint_callback)();
// For UMA and testing. If non-null, triggered when the app list is painted.
base::Closure g_next_paint_callback;

// The margin from the edge to the speech UI.
const int kSpeechUIMargin = 12;
Expand Down Expand Up @@ -199,9 +200,9 @@ gfx::Size AppListView::GetPreferredSize() {

void AppListView::Paint(gfx::Canvas* canvas) {
views::BubbleDelegateView::Paint(canvas);
if (g_next_paint_callback) {
g_next_paint_callback();
g_next_paint_callback = NULL;
if (!g_next_paint_callback.is_null()) {
g_next_paint_callback.Run();
g_next_paint_callback.Reset();
}
}

Expand Down Expand Up @@ -243,7 +244,7 @@ void AppListView::RemoveObserver(AppListViewObserver* observer) {
}

// static
void AppListView::SetNextPaintCallback(void (*callback)()) {
void AppListView::SetNextPaintCallback(const base::Closure& callback) {
g_next_paint_callback = callback;
}

Expand Down
3 changes: 2 additions & 1 deletion ui/app_list/views/app_list_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef UI_APP_LIST_VIEWS_APP_LIST_VIEW_H_
#define UI_APP_LIST_VIEWS_APP_LIST_VIEW_H_

#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
#include "ui/app_list/app_list_export.h"
Expand Down Expand Up @@ -95,7 +96,7 @@ class APP_LIST_EXPORT AppListView : public views::BubbleDelegateView,
void RemoveObserver(AppListViewObserver* observer);

// Set a callback to be called the next time any app list paints.
static void SetNextPaintCallback(void (*callback)());
static void SetNextPaintCallback(const base::Closure& callback);

#if defined(OS_WIN)
HWND GetHWND() const;
Expand Down
261 changes: 261 additions & 0 deletions ui/app_list/views/app_list_view_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
// Copyright 2014 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 "ui/app_list/views/app_list_view.h"

#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h."
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/app_list/app_list_switches.h"
#include "ui/app_list/pagination_model.h"
#include "ui/app_list/test/app_list_test_model.h"
#include "ui/app_list/test/app_list_test_view_delegate.h"
#include "ui/app_list/views/app_list_main_view.h"
#include "ui/app_list/views/contents_view.h"
#include "ui/app_list/views/search_box_view.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/window.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"

namespace app_list {
namespace test {

namespace {

// Choose a set that is 3 regular app list pages and 2 landscape app list pages.
const int kInitialItems = 34;

const char* TestName() {
return ::testing::UnitTest::GetInstance()->current_test_info()->name();
}

// Allows the same tests to run with different contexts: either an Ash-style
// root window or a desktop window tree host.
class AppListViewTestContext {
public:
AppListViewTestContext(aura::Window* parent);
~AppListViewTestContext();

// Test displaying the app list and performs a standard set of checks on its
// top level views. Then closes the window.
void RunDisplayTest();

// A standard set of checks on a view, e.g., ensuring it is drawn and visible.
static void CheckView(views::View* subview);

// Invoked when the Widget is closing, and the view it contains is about to
// be torn down. This only occurs in a run loop and will be used as a signal
// to quit.
void NativeWidgetClosing() {
view_ = NULL;
run_loop_->Quit();
}

// Whether the experimental "landscape" app launcher UI is being tested.
bool is_landscape() const { return is_landscape_; }

private:
const bool is_landscape_;
scoped_ptr<base::RunLoop> run_loop_;
PaginationModel pagination_model_;
app_list::AppListView* view_; // Owned by native widget.
app_list::test::AppListTestViewDelegate* delegate_; // Owned by |view_|;

DISALLOW_COPY_AND_ASSIGN(AppListViewTestContext);
};

// Extend the regular AppListTestViewDelegate to communicate back to the test
// context. Note the test context doesn't simply inherit this, because the
// delegate is owned by the view.
class UnitTestViewDelegate : public app_list::test::AppListTestViewDelegate {
public:
UnitTestViewDelegate(AppListViewTestContext* parent) : parent_(parent) {}

// Overridden from app_list::test::AppListTestViewDelegate:
virtual void ViewClosing() OVERRIDE { parent_->NativeWidgetClosing(); }

private:
AppListViewTestContext* parent_;

DISALLOW_COPY_AND_ASSIGN(UnitTestViewDelegate);
};

AppListViewTestContext::AppListViewTestContext(aura::Window* parent)
: is_landscape_(EndsWith(TestName(), "Landscape", true)) {
if (is_landscape_) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableExperimentalAppListPosition);
}

delegate_ = new UnitTestViewDelegate(this);
view_ = new app_list::AppListView(delegate_);

// Initialize centered around a point that ensures the window is wholly shown.
view_->InitAsBubbleAtFixedLocation(parent,
&pagination_model_,
gfx::Point(300, 300),
views::BubbleBorder::FLOAT,
false /* border_accepts_events */);
}

AppListViewTestContext::~AppListViewTestContext() {
// The view observes the PaginationModel which is about to get destroyed, so
// if the view is not already deleted by the time this destructor is called,
// there will be problems.
EXPECT_FALSE(view_);
}

// static
void AppListViewTestContext::CheckView(views::View* subview) {
ASSERT_TRUE(subview);
EXPECT_TRUE(subview->parent());
EXPECT_TRUE(subview->visible());
EXPECT_TRUE(subview->IsDrawn());
}

void AppListViewTestContext::RunDisplayTest() {
EXPECT_FALSE(view_->GetWidget()->IsVisible());
EXPECT_EQ(-1, pagination_model_.total_pages());
view_->GetWidget()->Show();
delegate_->GetTestModel()->PopulateApps(kInitialItems);

run_loop_.reset(new base::RunLoop);
AppListView::SetNextPaintCallback(run_loop_->QuitClosure());
run_loop_->Run();

EXPECT_TRUE(view_->GetWidget()->IsVisible());

if (is_landscape_)
EXPECT_EQ(2, pagination_model_.total_pages());
else
EXPECT_EQ(3, pagination_model_.total_pages());
EXPECT_EQ(0, pagination_model_.selected_page());

// Checks on the main view.
AppListMainView* main_view = view_->app_list_main_view();
EXPECT_NO_FATAL_FAILURE(CheckView(main_view));
EXPECT_NO_FATAL_FAILURE(CheckView(main_view->search_box_view()));
EXPECT_NO_FATAL_FAILURE(CheckView(main_view->contents_view()));

view_->GetWidget()->Close();
run_loop_.reset(new base::RunLoop);
run_loop_->Run();

// |view_| should have been deleted and set to NULL via ViewClosing().
EXPECT_FALSE(view_);
}

class AppListViewTestAura : public views::ViewsTestBase {
public:
AppListViewTestAura() {}
virtual ~AppListViewTestAura() {}

// testing::Test overrides:
virtual void SetUp() OVERRIDE {
views::ViewsTestBase::SetUp();
test_context_.reset(new AppListViewTestContext(GetContext()));
}

virtual void TearDown() OVERRIDE {
test_context_.reset();
views::ViewsTestBase::TearDown();
}

protected:
scoped_ptr<AppListViewTestContext> test_context_;

private:
DISALLOW_COPY_AND_ASSIGN(AppListViewTestAura);
};

class AppListViewTestDesktop : public views::ViewsTestBase {
public:
AppListViewTestDesktop() {}
virtual ~AppListViewTestDesktop() {}

// testing::Test overrides:
virtual void SetUp() OVERRIDE {
set_views_delegate(new AppListViewTestViewsDelegate(this));
views::ViewsTestBase::SetUp();
test_context_.reset(new AppListViewTestContext(NULL));
}

virtual void TearDown() OVERRIDE {
test_context_.reset();
views::ViewsTestBase::TearDown();
}

protected:
scoped_ptr<AppListViewTestContext> test_context_;

private:
class AppListViewTestViewsDelegate : public views::TestViewsDelegate {
public:
AppListViewTestViewsDelegate(AppListViewTestDesktop* parent)
: parent_(parent) {}

// Overridden from views::ViewsDelegate:
virtual void OnBeforeWidgetInit(
views::Widget::InitParams* params,
views::internal::NativeWidgetDelegate* delegate) OVERRIDE;

private:
AppListViewTestDesktop* parent_;

DISALLOW_COPY_AND_ASSIGN(AppListViewTestViewsDelegate);
};

DISALLOW_COPY_AND_ASSIGN(AppListViewTestDesktop);
};

void AppListViewTestDesktop::AppListViewTestViewsDelegate::OnBeforeWidgetInit(
views::Widget::InitParams* params,
views::internal::NativeWidgetDelegate* delegate) {
// Mimic the logic in ChromeViewsDelegate::OnBeforeWidgetInit(). Except, for
// ChromeOS, use the root window from the AuraTestHelper rather than depending
// on ash::Shell:GetPrimaryRootWindow(). Also assume non-ChromeOS is never the
// Ash desktop, as that is covered by AppListViewTestAura.
#if defined(OS_CHROMEOS)
if (!params->parent && !params->context)
params->context = parent_->GetContext();
#elif defined(USE_AURA)
if (params->parent == NULL && params->context == NULL && params->top_level)
params->native_widget = new views::DesktopNativeWidgetAura(delegate);
#endif
}

} // namespace

// Tests showing the app list with basic test model in an ash-style root window.
TEST_F(AppListViewTestAura, Display) {
EXPECT_FALSE(test_context_->is_landscape()); // Sanity check.
host()->Show();
EXPECT_NO_FATAL_FAILURE(test_context_->RunDisplayTest());
}

// Tests showing the app list on the desktop. Note on ChromeOS, this will still
// use the regular root window.
TEST_F(AppListViewTestDesktop, Display) {
EXPECT_FALSE(test_context_->is_landscape());
EXPECT_NO_FATAL_FAILURE(test_context_->RunDisplayTest());
}

// Landscape versions of the above.

TEST_F(AppListViewTestAura, DisplayLandscape) {
EXPECT_TRUE(test_context_->is_landscape());
host()->Show();
EXPECT_NO_FATAL_FAILURE(test_context_->RunDisplayTest());
}

TEST_F(AppListViewTestDesktop, DisplayLandscape) {
EXPECT_TRUE(test_context_->is_landscape());
EXPECT_NO_FATAL_FAILURE(test_context_->RunDisplayTest());
}

} // namespace test
} // namespace app_list

0 comments on commit 7e5b41a

Please sign in to comment.