Skip to content

Commit

Permalink
[App-Discovery] Add initial logging to launcher.
Browse files Browse the repository at this point in the history
Bug: b/267218014
Change-Id: I7b8e33cd110fc363254de7af4e90c0e82175f4b2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4228480
Reviewed-by: Tony Yeoman <tby@chromium.org>
Commit-Queue: Jong Ahn <jongahn@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1108206}
  • Loading branch information
Jong Ahn authored and Chromium LUCI CQ committed Feb 22, 2023
1 parent a9f3a2e commit 3842f9a
Show file tree
Hide file tree
Showing 9 changed files with 389 additions and 8 deletions.
4 changes: 4 additions & 0 deletions chrome/browser/ash/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -4752,6 +4752,7 @@ source_set("unit_tests") {
"app_list/md_icon_normalizer_unittest.cc",
"app_list/reorder/app_list_reorder_core_unittest.cc",
"app_list/reorder/app_list_reorder_util_unittest.cc",
"app_list/search/app_discovery_metrics_manager_unittest.cc",
"app_list/search/app_search_provider_test_base.cc",
"app_list/search/app_search_provider_test_base.h",
"app_list/search/app_search_provider_unittest.cc",
Expand Down Expand Up @@ -5985,6 +5986,9 @@ source_set("unit_tests") {
"//components/metrics",
"//components/metrics:serialization",
"//components/metrics:test_support",
"//components/metrics/structured",
"//components/metrics/structured:structured_events",
"//components/metrics/structured:test_support",
"//components/omnibox/browser",
"//components/omnibox/browser:location_bar",
"//components/omnibox/browser:test_support",
Expand Down
5 changes: 2 additions & 3 deletions chrome/browser/ash/app_list/app_list_client_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -382,9 +382,8 @@ void AppListClientImpl::GetContextMenuModel(
}

void AppListClientImpl::OnAppListVisibilityWillChange(bool visible) {
app_list_target_visibility_ = visible;
if (!visible && search_controller_) {
search_controller_->AppListClosing();
if (search_controller_) {
search_controller_->AppListViewChanging(visible);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/app_list/search/app_discovery_metrics_manager.h"

#include "ash/public/cpp/app_list/app_list_metrics.h"
#include "chrome/browser/ash/app_list/search/common/types_util.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chromeos/ash/components/string_matching/fuzzy_tokenized_string_match.h"
#include "chromeos/ash/components/string_matching/tokenized_string.h"
#include "components/metrics/structured/structured_events.h"
#include "components/sync/base/model_type.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/driver/sync_service_utils.h"

namespace app_list {

namespace {

namespace cros_events = metrics::structured::events::v2::cr_os_events;

bool IsResultAppInstallRelated(ash::SearchResultType result_type) {
return result_type == ash::SearchResultType::PLAY_STORE_UNINSTALLED_APP ||
result_type == ash::SearchResultType::GAME_SEARCH ||
result_type == ash::SearchResultType::PLAY_STORE_INSTANT_APP;
}

} // namespace

AppDiscoveryMetricsManager::AppDiscoveryMetricsManager(Profile* profile)
: profile_(profile) {}
AppDiscoveryMetricsManager::~AppDiscoveryMetricsManager() = default;

void AppDiscoveryMetricsManager::OnOpenResult(ChromeSearchResult* result,
const std::u16string& query) {
// If the result is not app-install related, then ignore result as it does not
// pertain to app-discovery.
if (!IsResultAppInstallRelated(result->metrics_type())) {
return;
}

auto app_name = ash::string_matching::TokenizedString(result->title());
auto tokenized_query = ash::string_matching::TokenizedString(query);

double string_match_score =
ash::string_matching::FuzzyTokenizedStringMatch::WeightedRatio(
app_name, tokenized_query);

// If app-sync is disabled, then do not send app_id.
std::string app_id = IsAppSyncEnabled() ? result->id() : "";
std::u16string app_title = IsAppSyncEnabled() ? result->title() : u"";

cros_events::AppDiscovery_AppLauncherResultOpened()
.SetAppId(app_id)
.SetAppName(std::string(app_title.begin(), app_title.end()))
.SetFuzzyStringMatch(string_match_score)
.Record();
}

void AppDiscoveryMetricsManager::OnLauncherOpen() {
cros_events::AppDiscovery_LauncherOpen().Record();
}

bool AppDiscoveryMetricsManager::IsAppSyncEnabled() {
switch (syncer::GetUploadToGoogleState(
SyncServiceFactory::GetForProfile(profile_), syncer::ModelType::APPS)) {
case syncer::UploadState::NOT_ACTIVE:
return false;
case syncer::UploadState::INITIALIZING:
// Note that INITIALIZING is considered good enough, because syncing apps
// is known to be enabled, and transient errors don't really matter here.
case syncer::UploadState::ACTIVE:
return true;
}
}

} // namespace app_list
41 changes: 41 additions & 0 deletions chrome/browser/ash/app_list/search/app_discovery_metrics_manager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_APP_LIST_SEARCH_APP_DISCOVERY_METRICS_MANAGER_H_
#define CHROME_BROWSER_ASH_APP_LIST_SEARCH_APP_DISCOVERY_METRICS_MANAGER_H_

#include "base/scoped_observation.h"
#include "chrome/browser/ash/app_list/search/chrome_search_result.h"
#include "chrome/browser/profiles/profile.h"

namespace app_list {

// Class that manages recording metrics related to app discovery from the
// launcher.
class AppDiscoveryMetricsManager {
public:
explicit AppDiscoveryMetricsManager(Profile* profile);
~AppDiscoveryMetricsManager();

AppDiscoveryMetricsManager(const AppDiscoveryMetricsManager&) = delete;
AppDiscoveryMetricsManager& operator=(const AppDiscoveryMetricsManager&) =
delete;

// Called when the launcher is opened.
void OnLauncherOpen();

// Called when a result is launched from the launcher.
void OnOpenResult(ChromeSearchResult* result, const std::u16string& query);

private:
// Returns whether app sync is enabled for |profile_| or not.
bool IsAppSyncEnabled();

// Profile of the current user.
Profile* profile_;
};

} // namespace app_list

#endif // CHROME_BROWSER_ASH_APP_LIST_SEARCH_APP_DISCOVERY_METRICS_MANAGER_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/app_list/search/app_discovery_metrics_manager.h"

#include "ash/public/cpp/app_list/app_list_metrics.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "chrome/browser/ash/app_list/search/chrome_search_result.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/test/base/testing_profile.h"
#include "components/metrics/structured/recorder.h"
#include "components/metrics/structured/structured_events.h"
#include "components/metrics/structured/structured_metrics_features.h"
#include "components/metrics/structured/test/test_structured_metrics_provider.h"
#include "components/sync/test/test_sync_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace app_list {
namespace {

namespace cros_events = metrics::structured::events::v2::cr_os_events;

std::unique_ptr<KeyedService> TestingSyncFactoryFunction(
content::BrowserContext* context) {
return std::make_unique<syncer::TestSyncService>();
}

// Impl for testing structured metrics that forwards all writes to the recorder
// directly.
class TestRecorder
: public metrics::structured::StructuredMetricsClient::RecordingDelegate {
public:
TestRecorder() {
metrics::structured::StructuredMetricsClient::Get()->SetDelegate(this);
}

bool IsReadyToRecord() const override { return true; }

void RecordEvent(metrics::structured::Event&& event) override {
metrics::structured::Recorder::GetInstance()->RecordEvent(std::move(event));
}
};

class TestSearchResult : public ChromeSearchResult {
public:
TestSearchResult(const std::string& id,
const std::u16string& title,
MetricsType metrics_type) {
set_id(id);
SetTitle(title);
SetMetricsType(metrics_type);
}

TestSearchResult(const TestSearchResult&) = delete;
TestSearchResult& operator=(const TestSearchResult&) = delete;

~TestSearchResult() override {}

// ChromeSearchResult overrides:
void Open(int event_flags) override {}
};

} // namespace

class AppDiscoveryMetricsManagerTest : public testing::Test {
public:
void SetUp() override {
test_recorder_ = std::make_unique<TestRecorder>();
test_structured_metrics_provider_ =
std::make_unique<metrics::structured::TestStructuredMetricsProvider>();
test_structured_metrics_provider_->EnableRecording();
metrics::structured::Recorder::GetInstance()->SetUiTaskRunner(
task_environment_.GetMainThreadTaskRunner());

TestingProfile::Builder builder;
builder.AddTestingFactory(SyncServiceFactory::GetInstance(),
SyncServiceFactory::GetDefaultFactory());
testing_profile_ = builder.Build();

sync_service_ = static_cast<syncer::TestSyncService*>(
SyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
testing_profile_.get(),
base::BindRepeating(&TestingSyncFactoryFunction)));

app_discovery_metrics_ =
std::make_unique<AppDiscoveryMetricsManager>(testing_profile_.get());
}

metrics::structured::TestStructuredMetricsProvider*
test_structured_metrics_provider() {
return test_structured_metrics_provider_.get();
}

void ValidateAppLaunchEvent(const metrics::structured::Event& event,
const std::string& app_id,
const std::u16string& app_title,
double match_score) {
cros_events::AppDiscovery_AppLauncherResultOpened expected_event;

EXPECT_EQ(expected_event.project_name(), event.project_name());
EXPECT_EQ(expected_event.event_name(), event.event_name());

expected_event.SetAppId(app_id)
.SetAppName(std::string(app_title.begin(), app_title.end()))
.SetFuzzyStringMatch(match_score);

EXPECT_EQ(expected_event.metric_values(), event.metric_values());
}

void ValidateLauncherOpenEvent(const metrics::structured::Event& event) {
cros_events::AppDiscovery_LauncherOpen expected_event;

EXPECT_EQ(expected_event.project_name(), event.project_name());
EXPECT_EQ(expected_event.event_name(), event.event_name());
}

TestingProfile* profile() { return testing_profile_.get(); }

syncer::TestSyncService* sync_service() { return sync_service_; }

AppDiscoveryMetricsManager* app_discovery_metrics() {
return app_discovery_metrics_.get();
}

private:
content::BrowserTaskEnvironment task_environment_;

std::unique_ptr<TestingProfile> testing_profile_;
raw_ptr<syncer::TestSyncService> sync_service_ = nullptr;
std::unique_ptr<AppDiscoveryMetricsManager> app_discovery_metrics_;

std::unique_ptr<TestRecorder> test_recorder_;
std::unique_ptr<metrics::structured::TestStructuredMetricsProvider>
test_structured_metrics_provider_;
};

TEST_F(AppDiscoveryMetricsManagerTest, OnOpenAppResult) {
const std::u16string app_name = u"app_name";
const std::u16string query = u"app_name";
const std::string app_id = "id";

TestSearchResult search_result(
app_id, app_name, ash::SearchResultType::PLAY_STORE_UNINSTALLED_APP);

base::RunLoop run_loop;
auto record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppLaunchEvent(event, app_id, app_name, /*match_score=*/1.0);
run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(record_callback);

app_discovery_metrics()->OnOpenResult(&search_result, query);
run_loop.Run();
}

TEST_F(AppDiscoveryMetricsManagerTest, OnOpenAppResultAppSyncDisabled) {
const std::u16string app_name = u"app_name";
const std::u16string query = u"app_name";
const std::string app_id = "id";

// Disable app-sync.
sync_service()->SetDisableReasons(
syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY);

TestSearchResult search_result(
app_id, app_name, ash::SearchResultType::PLAY_STORE_UNINSTALLED_APP);

base::RunLoop run_loop;
auto record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
// App ID and app name will be stripped if app sync is disabled.
ValidateAppLaunchEvent(event, /*app_id=*/"", /*app_title=*/u"",
/*match_score=*/1.0);
run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(record_callback);

app_discovery_metrics()->OnOpenResult(&search_result, query);
run_loop.Run();
}

TEST_F(AppDiscoveryMetricsManagerTest, OnLauncherOpen) {
base::RunLoop run_loop;
auto record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateLauncherOpenEvent(event);
run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(record_callback);

app_discovery_metrics()->OnLauncherOpen();
run_loop.Run();
}

} // namespace app_list

0 comments on commit 3842f9a

Please sign in to comment.