#include "ash/app_list/app_list_metrics.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "ash/accessibility/accessibility_controller.h"
#include "ash/app_list/app_list_bubble_presenter.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/app_list/app_list_model_provider.h"
#include "ash/app_list/app_list_presenter_impl.h"
#include "ash/app_list/test/app_list_test_helper.h"
#include "ash/app_list/views/app_list_item_view.h"
#include "ash/app_list/views/app_list_main_view.h"
#include "ash/app_list/views/app_list_view.h"
#include "ash/app_list/views/apps_container_view.h"
#include "ash/app_list/views/contents_view.h"
#include "ash/app_list/views/paged_apps_grid_view.h"
#include "ash/app_list/views/recent_apps_view.h"
#include "ash/app_list/views/search_result_container_view.h"
#include "ash/app_list/views/search_result_page_view.h"
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/app_list/app_list_types.h"
#include "ash/public/cpp/shelf_item_delegate.h"
#include "ash/public/cpp/shelf_model.h"
#include "ash/shelf/home_button.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_navigation_widget.h"
#include "ash/shelf/shelf_view.h"
#include "ash/shelf/shelf_view_test_api.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "ui/display/display_observer.h"
#include "ui/display/screen.h"
#include "ui/display/tablet_state.h"
#include "ui/gfx/scoped_animation_duration_scale_mode.h"
namespace ash {
namespace {
constexpr int kBrowserAppIndexOnShelf = 0;
class TestShelfItemDelegate : public ShelfItemDelegate,
display::DisplayObserver {
public:
explicit TestShelfItemDelegate(const ShelfID& shelf_id)
: ShelfItemDelegate(shelf_id) {}
TestShelfItemDelegate(const ShelfID& shelf_id, bool wait_for_tablet_mode)
: ShelfItemDelegate(shelf_id),
wait_for_tablet_mode_(wait_for_tablet_mode) {
if (wait_for_tablet_mode_) {
display::Screen::Get()->AddObserver(this);
}
}
~TestShelfItemDelegate() override {
display::Screen::Get()->RemoveObserver(this);
}
void ItemSelected(std::unique_ptr<ui::Event> event,
int64_t display_id,
ash::ShelfLaunchSource source,
ItemSelectedCallback callback,
const ItemFilterPredicate& filter_predicate) override {
if (wait_for_tablet_mode_) {
callback_ = std::move(callback);
return;
}
std::move(callback).Run(SHELF_ACTION_WINDOW_ACTIVATED, {});
}
void ExecuteCommand(bool from_context_menu,
int64_t command_id,
int32_t event_flags,
int64_t display_id) override {}
void Close() override {}
void OnDisplayTabletStateChanged(display::TabletState state) override {
if (!callback_ || state != display::TabletState::kEnteringTabletMode) {
return;
}
std::move(callback_).Run(SHELF_ACTION_WINDOW_ACTIVATED, {});
}
private:
bool wait_for_tablet_mode_ = false;
ItemSelectedCallback callback_;
};
}
int64_t GetPrimaryDisplayId() {
return display::Screen::Get()->GetPrimaryDisplay().id();
}
class AppListMetricsTest : public AshTestBase {
public:
AppListMetricsTest() = default;
AppListMetricsTest(const AppListMetricsTest&) = delete;
AppListMetricsTest& operator=(const AppListMetricsTest&) = delete;
~AppListMetricsTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
shelf_test_api_ = std::make_unique<ShelfViewTestAPI>(
GetPrimaryShelf()->GetShelfViewForTesting());
}
void TearDown() override {
shelf_test_api_.reset();
AshTestBase::TearDown();
}
protected:
void CreateShelfItem(bool wait_for_tablet_mode = false) {
ShelfItem shelf_item;
shelf_item.id = ShelfID("app_id");
shelf_item.type = TYPE_BROWSER_SHORTCUT;
ShelfModel::Get()->Add(shelf_item,
std::make_unique<TestShelfItemDelegate>(
shelf_item.id, wait_for_tablet_mode));
shelf_test_api_->RunMessageLoopUntilAnimationsDone();
}
void CreateAndClickShelfItem() {
CreateShelfItem();
ClickShelfItem();
}
void ClickShelfItem() {
const views::ViewModel* view_model =
GetPrimaryShelf()->GetShelfViewForTesting()->view_model_for_test();
LeftClickOn(view_model->view_at(kBrowserAppIndexOnShelf));
}
void PopulateAndLaunchAppInGrid(int num = 4) {
AppListModel* model = AppListModelProvider::Get()->model();
for (int i = 0; i < num; i++) {
AppListItem* item = model->AddItem(
std::make_unique<AppListItem>(base::StringPrintf("item %d", i)));
model->SetItemName(item, item->id());
}
AppListView::TestApi test_api(
Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView());
test_api.GetRootAppsGridView()->GetItemViewAt(0)->RequestFocus();
PressAndReleaseKey(ui::KeyboardCode::VKEY_RETURN);
}
private:
std::unique_ptr<ShelfViewTestAPI> shelf_test_api_;
};
class AppListMetricsTabletTest : public AppListMetricsTest {
public:
AppListMetricsTabletTest() = default;
~AppListMetricsTabletTest() override = default;
void SetUp() override {
AppListMetricsTest::SetUp();
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
}
};
TEST_F(AppListMetricsTabletTest, HomecherAllAppsLaunchFromShelf) {
base::HistogramTester histogram_tester;
CreateAndClickShelfItem();
histogram_tester.ExpectBucketCount(
"Apps.AppListAppLaunchedV2.HomecherAllApps",
AppListLaunchedFrom::kLaunchedFromShelf,
1 );
}
TEST_F(AppListMetricsTest, TapOnItemDuringTabletModeAnimation) {
base::HistogramTester histogram_tester;
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
CreateShelfItem(true);
std::unique_ptr<views::Widget> widget =
CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
gfx::ScopedAnimationDurationScaleMode non_zero_duration_mode(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
ClickShelfItem();
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
GetPrimaryShelf()->shelf_widget()->LayoutRootViewIfNecessary();
GetAppListTestHelper()->CheckVisibility(false);
histogram_tester.ExpectBucketCount(
"Apps.AppListAppLaunchedV2.Closed",
AppListLaunchedFrom::kLaunchedFromShelf,
1 );
}
TEST_F(AppListMetricsTabletTest, HomecherAllAppsLaunchFromGrid) {
base::HistogramTester histogram_tester;
PopulateAndLaunchAppInGrid();
histogram_tester.ExpectBucketCount(
"Apps.AppListAppLaunchedV2.HomecherAllApps",
AppListLaunchedFrom::kLaunchedFromGrid,
1 );
}
TEST_F(AppListMetricsTabletTest, HomecherSearchLaunchFromShelf) {
base::HistogramTester histogram_tester;
PressAndReleaseKey(ui::KeyboardCode::VKEY_H);
GetAppListTestHelper()->WaitUntilIdle();
GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenSearch);
CreateAndClickShelfItem();
histogram_tester.ExpectBucketCount(
"Apps.AppListAppLaunchedV2.HomecherSearch",
AppListLaunchedFrom::kLaunchedFromShelf,
1 );
}
TEST_F(AppListMetricsTest, BubbleAllAppsLaunchFromRecentApps) {
base::HistogramTester histogram_tester;
auto* helper = GetAppListTestHelper();
helper->WaitUntilIdle();
helper->AddRecentApps(5);
helper->AddAppItems(5);
helper->ShowAppList();
helper->WaitUntilIdle();
views::View* recent_apps = helper->GetBubbleRecentAppsView();
recent_apps->children().front()->RequestFocus();
helper->WaitUntilIdle();
PressAndReleaseKey(ui::KeyboardCode::VKEY_RETURN);
helper->WaitUntilIdle();
histogram_tester.ExpectBucketCount(
"Apps.AppListAppLaunchedV2.BubbleAllApps",
AppListLaunchedFrom::kLaunchedFromRecentApps,
1 );
}
TEST_F(AppListMetricsTest, HideContinueSectionMetricInClamshellMode) {
base::HistogramTester histograms;
auto* helper = GetAppListTestHelper();
helper->AddContinueSuggestionResults(4);
helper->AddRecentApps(5);
helper->AddAppItems(5);
helper->ShowAppList();
const int false_bucket = 0;
histograms.ExpectBucketCount(
"Apps.AppList.ContinueSectionHiddenByUser.ClamshellMode", false_bucket,
1);
helper->Dismiss();
Shell::Get()->app_list_controller()->SetHideContinueSection(true);
helper->ShowAppList();
const int true_bucket = 1;
histograms.ExpectBucketCount(
"Apps.AppList.ContinueSectionHiddenByUser.ClamshellMode", true_bucket, 1);
helper->Dismiss();
}
TEST_F(AppListMetricsTest, HideContinueSectionMetricInTabletMode) {
base::HistogramTester histograms;
auto* helper = GetAppListTestHelper();
helper->AddContinueSuggestionResults(4);
helper->AddRecentApps(5);
helper->AddAppItems(5);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
const int false_bucket = 0;
histograms.ExpectBucketCount(
"Apps.AppList.ContinueSectionHiddenByUser.TabletMode", false_bucket, 1);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
Shell::Get()->app_list_controller()->SetHideContinueSection(true);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
const int true_bucket = 1;
histograms.ExpectBucketCount(
"Apps.AppList.ContinueSectionHiddenByUser.TabletMode", true_bucket, 1);
}
TEST_F(AppListMetricsTest, HomecherLaunchFromRecentApps) {
base::HistogramTester histogram_tester;
auto* helper = GetAppListTestHelper();
helper->WaitUntilIdle();
helper->AddRecentApps(5);
helper->AddAppItems(5);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
helper->WaitUntilIdle();
views::View* recent_apps = helper->GetFullscreenRecentAppsView();
recent_apps->children().front()->RequestFocus();
helper->WaitUntilIdle();
PressAndReleaseKey(ui::KeyboardCode::VKEY_RETURN);
helper->WaitUntilIdle();
histogram_tester.ExpectBucketCount(
"Apps.AppListAppLaunchedV2.HomecherAllApps",
AppListLaunchedFrom::kLaunchedFromRecentApps,
1 );
}
class AppListShowSourceMetricTest : public AshTestBase {
public:
AppListShowSourceMetricTest() = default;
AppListShowSourceMetricTest(const AppListShowSourceMetricTest&) = delete;
AppListShowSourceMetricTest& operator=(const AppListShowSourceMetricTest&) =
delete;
~AppListShowSourceMetricTest() override = default;
protected:
void ClickHomeButton() {
LeftClickOn(GetPrimaryShelf()->navigation_widget()->GetHomeButton());
}
};
TEST_F(AppListShowSourceMetricTest, TabletInAppToHome) {
base::HistogramTester histogram_tester;
Shell::Get()
->accessibility_controller()
->SetTabletModeShelfNavigationButtonsEnabled(true);
std::unique_ptr<views::Widget> widget =
CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
ClickHomeButton();
histogram_tester.ExpectBucketCount(
"Apps.AppListShowSource", AppListShowSource::kShelfButton,
1 );
histogram_tester.ExpectBucketCount(
"Apps.AppListShowSource", AppListShowSource::kTabletMode,
0 );
GetAppListTestHelper()->CheckVisibility(true);
ClickHomeButton();
histogram_tester.ExpectBucketCount(
"Apps.AppListShowSource", AppListShowSource::kShelfButton,
1 );
histogram_tester.ExpectTotalCount("Apps.AppListShowSource", 1);
}
TEST_F(AppListShowSourceMetricTest, TabletModeWithWindowOpen) {
base::HistogramTester histogram_tester;
std::unique_ptr<views::Widget> widget =
CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
GetAppListTestHelper()->CheckVisibility(false);
histogram_tester.ExpectTotalCount("Apps.AppListShowSource", 0);
}
TEST_F(AppListShowSourceMetricTest, TabletModeWithNoWindowOpen) {
base::HistogramTester histogram_tester;
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
GetAppListTestHelper()->CheckVisibility(true);
histogram_tester.ExpectBucketCount(
"Apps.AppListShowSource", AppListShowSource::kTabletMode,
1 );
}
TEST_F(AppListShowSourceMetricTest, ClamshellModeHomeButton) {
base::HistogramTester histogram_tester;
auto* app_list_bubble_presenter =
Shell::Get()->app_list_controller()->bubble_presenter_for_test();
GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
EXPECT_TRUE(app_list_bubble_presenter->IsShowing());
histogram_tester.ExpectTotalCount("Apps.AppListBubbleShowSource", 1);
GetAppListTestHelper()->Dismiss();
EXPECT_FALSE(app_list_bubble_presenter->IsShowing());
histogram_tester.ExpectTotalCount("Apps.AppListBubbleShowSource", 1);
GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
EXPECT_TRUE(app_list_bubble_presenter->IsShowing());
histogram_tester.ExpectTotalCount("Apps.AppListBubbleShowSource", 2);
histogram_tester.ExpectTotalCount("Apps.AppListShowSource", 0);
}
TEST_F(AppListShowSourceMetricTest, TabletModeDoesNotRecordAppListBubbleShow) {
base::HistogramTester histogram_tester;
Shell::Get()
->accessibility_controller()
->SetTabletModeShelfNavigationButtonsEnabled(true);
std::unique_ptr<views::Widget> widget =
CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
auto* app_list_bubble_presenter =
Shell::Get()->app_list_controller()->bubble_presenter_for_test();
EXPECT_FALSE(app_list_bubble_presenter->IsShowing());
histogram_tester.ExpectTotalCount("Apps.AppListBubbleShowSource", 0);
ClickHomeButton();
EXPECT_FALSE(app_list_bubble_presenter->IsShowing());
histogram_tester.ExpectTotalCount("Apps.AppListBubbleShowSource", 0);
}
TEST_F(AppListShowSourceMetricTest, ToggleDoesNotRecordOnHide) {
base::HistogramTester histogram_tester;
auto* app_list_controller = Shell::Get()->app_list_controller();
app_list_controller->ToggleAppList(GetPrimaryDisplayId(),
AppListShowSource::kSearchKey,
base::TimeTicks::Now());
auto* app_list_bubble_presenter =
Shell::Get()->app_list_controller()->bubble_presenter_for_test();
ASSERT_TRUE(app_list_bubble_presenter->IsShowing());
app_list_controller->ToggleAppList(GetPrimaryDisplayId(),
AppListShowSource::kSearchKey,
base::TimeTicks::Now());
ASSERT_FALSE(app_list_bubble_presenter->IsShowing());
histogram_tester.ExpectTotalCount("Apps.AppListBubbleShowSource", 1);
}
using AppListPeriodicMetricsTest = AshTestBase;
TEST_F(AppListPeriodicMetricsTest, PeriodicAppListMetrics_NumberOfApps) {
base::HistogramTester histogram;
histogram.ExpectTotalCount("Apps.AppList.NumberOfApps", 0);
histogram.ExpectTotalCount("Apps.AppList.NumberOfRootLevelItems", 0);
AppListModel* model = AppListModelProvider::Get()->model();
for (int i = 0; i < 5; i++) {
model->AddItem(
std::make_unique<AppListItem>(base::StringPrintf("app_id_%d", i)));
}
RecordPeriodicAppListMetrics();
histogram.ExpectBucketCount("Apps.AppList.NumberOfApps", 5, 1);
histogram.ExpectBucketCount("Apps.AppList.NumberOfRootLevelItems", 5, 1);
histogram.ExpectTotalCount("Apps.AppList.NumberOfApps", 1);
histogram.ExpectTotalCount("Apps.AppList.NumberOfRootLevelItems", 1);
const std::string folder_id = "folder_id";
model->CreateFolderItem(folder_id);
for (int i = 0; i < 3; i++) {
auto item =
std::make_unique<AppListItem>(base::StringPrintf("id_in_folder_%d", i));
model->AddItemToFolder(std::move(item), folder_id);
}
RecordPeriodicAppListMetrics();
histogram.ExpectBucketCount("Apps.AppList.NumberOfApps", 8, 1);
histogram.ExpectBucketCount("Apps.AppList.NumberOfRootLevelItems", 6, 1);
}
TEST_F(AppListPeriodicMetricsTest, RecordFolderMetrics_ZeroFolders) {
base::HistogramTester histogram;
GetAppListTestHelper()->model()->PopulateApps(2);
RecordPeriodicAppListMetrics();
EXPECT_EQ(1, histogram.GetBucketCount("Apps.AppList.NumberOfFolders", 0));
EXPECT_EQ(
1, histogram.GetBucketCount("Apps.AppList.NumberOfNonSystemFolders", 0));
EXPECT_EQ(1, histogram.GetBucketCount(
"Apps.AppList.NumberOfAppsInNonSystemFolders", 0));
}
TEST_F(AppListPeriodicMetricsTest, RecordFolderMetrics_OneRegularFolder) {
base::HistogramTester histogram;
GetAppListTestHelper()->model()->CreateAndPopulateFolderWithApps(2);
RecordPeriodicAppListMetrics();
EXPECT_EQ(1, histogram.GetBucketCount("Apps.AppList.NumberOfFolders", 1));
EXPECT_EQ(
1, histogram.GetBucketCount("Apps.AppList.NumberOfNonSystemFolders", 1));
EXPECT_EQ(1, histogram.GetBucketCount(
"Apps.AppList.NumberOfAppsInNonSystemFolders", 2));
}
TEST_F(AppListPeriodicMetricsTest, RecordFolderMetrics_SystemFolder) {
base::HistogramTester histogram;
AppListFolderItem* folder =
GetAppListTestHelper()->model()->CreateSingleItemFolder("folder_id",
"item_id");
folder->SetIsSystemFolder(true);
RecordPeriodicAppListMetrics();
EXPECT_EQ(1, histogram.GetBucketCount("Apps.AppList.NumberOfFolders", 1));
EXPECT_EQ(
1, histogram.GetBucketCount("Apps.AppList.NumberOfNonSystemFolders", 0));
EXPECT_EQ(1, histogram.GetBucketCount(
"Apps.AppList.NumberOfAppsInNonSystemFolders", 0));
}
}