blob: 72d231b2ebb96000db524b40f72af9555b6e23c0 [file] [log] [blame]
// Copyright 2018 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 "ash/metrics/demo_session_metrics_recorder.h"
#include <memory>
#include <string>
#include <utility>
#include "ash/public/cpp/app_types.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/window_util.h"
#include "base/macros.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/timer/mock_timer.h"
#include "extensions/common/constants.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/window_types.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
#include "ui/wm/public/activation_client.h"
namespace ash {
namespace {
// Tests app usage recorded by DemoSessionMetricsRecorder.
// Mocks out the timer to control the sampling cycle. Tests will create and
// activate different window types to test that samples are attributed to the
// correct apps. Tests will also fire the timer continuously without user
// activity to simulate idle time and verify that idle samples are dropped.
class DemoSessionMetricsRecorderTest : public AshTestBase {
public:
DemoSessionMetricsRecorderTest() = default;
~DemoSessionMetricsRecorderTest() override = default;
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
// Create mock timer to be passed into DemoSessionMetricsRecorder.
std::unique_ptr<base::RepeatingTimer> mock_timer =
std::make_unique<base::MockRepeatingTimer>();
// Store a pointer to the timer before moving it.
mock_timer_ = static_cast<base::MockRepeatingTimer*>(mock_timer.get());
metrics_recorder_ =
std::make_unique<DemoSessionMetricsRecorder>(std::move(mock_timer));
histogram_tester_ = std::make_unique<base::HistogramTester>();
}
void TearDown() override {
metrics_recorder_.reset();
AshTestBase::TearDown();
}
// Fires the timer, if it's running. (If it's stopped, we can assume any
// amount of time passes here.)
void FireTimer() {
if (mock_timer_->IsRunning())
mock_timer_->Fire();
}
// Simulates user activity.
void SendUserActivity() { metrics_recorder_->OnUserActivity(nullptr); }
void ClearHistograms() {
histogram_tester_ = std::make_unique<base::HistogramTester>();
}
// Clears the metrics recorder and the timer to simulate the session ending.
void DeleteMetricsRecorder() {
mock_timer_ = nullptr;
metrics_recorder_.reset();
}
// Creates a browser window.
std::unique_ptr<aura::Window> CreateBrowserWindow() {
std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 0,
gfx::Rect(0, 0, 10, 10)));
window->SetProperty(aura::client::kAppType,
static_cast<int>(ash::AppType::BROWSER));
return window;
}
// Creates a browser window associated with a hosted app.
std::unique_ptr<aura::Window> CreateHostedAppBrowserWindow(
const std::string& app_id) {
std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 0,
gfx::Rect(0, 0, 10, 10)));
window->SetProperty(aura::client::kAppType,
static_cast<int>(ash::AppType::BROWSER));
window->SetProperty(
kShelfIDKey,
new std::string(ShelfID(app_id, std::string()).Serialize()));
return window;
}
// Creates a normal Chrome platform app window.
std::unique_ptr<aura::Window> CreateChromeAppWindow(
const std::string& app_id) {
std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 0,
gfx::Rect(0, 0, 10, 10)));
window->SetProperty(aura::client::kAppType,
static_cast<int>(ash::AppType::CHROME_APP));
window->SetProperty(
kShelfIDKey,
new std::string(ShelfID(app_id, std::string()).Serialize()));
return window;
}
// Creates a normal ARC++ app window.
std::unique_ptr<aura::Window> CreateArcWindow(
const std::string& package_name) {
std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 0,
gfx::Rect(0, 0, 10, 10)));
window->SetProperty(aura::client::kAppType,
static_cast<int>(ash::AppType::ARC_APP));
// ARC++ shelf app IDs are hashes of package_name#activity_name formatted as
// extension IDs. The point is that they are opaque to the metrics recorder.
const std::string app_id = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
window->SetProperty(
kShelfIDKey,
new std::string(ShelfID(app_id, std::string()).Serialize()));
if (!package_name.empty())
window->SetProperty(kArcPackageNameKey, new std::string(package_name));
return window;
}
// Creates a popup type window.
std::unique_ptr<aura::Window> CreatePopupWindow() {
std::unique_ptr<aura::Window> window(
CreateTestWindowInShellWithDelegateAndType(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(),
aura::client::WINDOW_TYPE_POPUP, 0, gfx::Rect(0, 0, 10, 10)));
return window;
}
protected:
// Captures histograms.
std::unique_ptr<base::HistogramTester> histogram_tester_;
// The test target.
std::unique_ptr<DemoSessionMetricsRecorder> metrics_recorder_;
// Owned by metics_recorder_.
base::MockRepeatingTimer* mock_timer_ = nullptr;
private:
DISALLOW_COPY_AND_ASSIGN(DemoSessionMetricsRecorderTest);
};
// Verify samples are correct when one app window is active.
TEST_F(DemoSessionMetricsRecorderTest, ActiveApp) {
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow(extension_misc::kHighlightsAppId);
wm::ActivateWindow(chrome_app_window.get());
for (int i = 0; i < 5; i++)
FireTimer();
SendUserActivity();
histogram_tester_->ExpectUniqueSample(
"DemoMode.ActiveApp",
DemoSessionMetricsRecorder::DemoModeApp::kHighlights, 5);
}
// Verify samples are correct when multiple browser windows become active.
TEST_F(DemoSessionMetricsRecorderTest, BrowserWindows) {
std::unique_ptr<aura::Window> browser_window = CreateBrowserWindow();
std::unique_ptr<aura::Window> browser_window2 = CreateBrowserWindow();
// Browser windows should all be treated as the same type.
wm::ActivateWindow(browser_window.get());
FireTimer();
wm::ActivateWindow(browser_window2.get());
FireTimer();
FireTimer();
SendUserActivity();
histogram_tester_->ExpectUniqueSample(
"DemoMode.ActiveApp", DemoSessionMetricsRecorder::DemoModeApp::kBrowser,
3);
}
// Verify samples are correct when multiple windows types become active.
TEST_F(DemoSessionMetricsRecorderTest, AppTypes) {
std::unique_ptr<aura::Window> browser_window = CreateBrowserWindow();
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow(extension_misc::kCameraAppId);
std::unique_ptr<aura::Window> hosted_app_browser_window =
CreateHostedAppBrowserWindow(extension_misc::kYoutubeAppId);
std::unique_ptr<aura::Window> arc_window =
CreateArcWindow("com.google.Photos");
wm::ActivateWindow(browser_window.get());
FireTimer();
SendUserActivity();
histogram_tester_->ExpectBucketCount(
"DemoMode.ActiveApp", DemoSessionMetricsRecorder::DemoModeApp::kBrowser,
1);
wm::ActivateWindow(chrome_app_window.get());
FireTimer();
FireTimer();
SendUserActivity();
histogram_tester_->ExpectBucketCount(
"DemoMode.ActiveApp", DemoSessionMetricsRecorder::DemoModeApp::kCamera,
2);
wm::ActivateWindow(hosted_app_browser_window.get());
FireTimer();
FireTimer();
FireTimer();
SendUserActivity();
histogram_tester_->ExpectBucketCount(
"DemoMode.ActiveApp", DemoSessionMetricsRecorder::DemoModeApp::kYouTube,
3);
wm::ActivateWindow(arc_window.get());
FireTimer();
FireTimer();
FireTimer();
FireTimer();
SendUserActivity();
histogram_tester_->ExpectBucketCount(
"DemoMode.ActiveApp",
DemoSessionMetricsRecorder::DemoModeApp::kGooglePhotos, 4);
histogram_tester_->ExpectTotalCount("DemoMode.ActiveApp", 10);
}
// Verify popup windows are categorized as kOtherWindow.
TEST_F(DemoSessionMetricsRecorderTest, PopupWindows) {
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow(extension_misc::kCameraAppId);
std::unique_ptr<aura::Window> popup_window = CreatePopupWindow();
wm::ActivateWindow(chrome_app_window.get());
for (int i = 0; i < 5; i++)
FireTimer();
wm::ActivateWindow(popup_window.get());
for (int i = 0; i < 3; i++)
FireTimer();
SendUserActivity();
histogram_tester_->ExpectBucketCount(
"DemoMode.ActiveApp", DemoSessionMetricsRecorder::DemoModeApp::kCamera,
5);
histogram_tester_->ExpectBucketCount(
"DemoMode.ActiveApp",
DemoSessionMetricsRecorder::DemoModeApp::kOtherWindow, 3);
histogram_tester_->ExpectTotalCount("DemoMode.ActiveApp", 8);
}
// Verify unknown apps are categorized as "other" Chrome/ARC apps.
TEST_F(DemoSessionMetricsRecorderTest, OtherApps) {
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
std::unique_ptr<aura::Window> arc_window = CreateArcWindow("com.foo.bar");
wm::ActivateWindow(chrome_app_window.get());
FireTimer();
wm::ActivateWindow(arc_window.get());
FireTimer();
FireTimer();
SendUserActivity();
histogram_tester_->ExpectBucketCount(
"DemoMode.ActiveApp",
DemoSessionMetricsRecorder::DemoModeApp::kOtherChromeApp, 1);
histogram_tester_->ExpectBucketCount(
"DemoMode.ActiveApp",
DemoSessionMetricsRecorder::DemoModeApp::kOtherArcApp, 2);
histogram_tester_->ExpectTotalCount("DemoMode.ActiveApp", 3);
}
// Verify samples are discarded after no user activity.
TEST_F(DemoSessionMetricsRecorderTest, DiscardAfterInactivity) {
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow(extension_misc::kCameraAppId);
std::unique_ptr<aura::Window> arc_window =
CreateChromeAppWindow("com.google.Photos");
wm::ActivateWindow(chrome_app_window.get());
for (int i = 0; i < 5; i++)
FireTimer();
SendUserActivity();
histogram_tester_->ExpectUniqueSample(
"DemoMode.ActiveApp", DemoSessionMetricsRecorder::DemoModeApp::kCamera,
5);
ClearHistograms();
// Have no user activity for 20 seconds.
for (int i = 0; i < 20; i++)
FireTimer();
// After user activity, the active window from the idle time isn't reported.
SendUserActivity();
histogram_tester_->ExpectTotalCount("DemoMode.ActiveApp", 0);
}
// Verify sample collection resumes after user activity.
TEST_F(DemoSessionMetricsRecorderTest, ResumeAfterActivity) {
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow(extension_misc::kCameraAppId);
wm::ActivateWindow(chrome_app_window.get());
// Have no user activity for 20 seconds.
for (int i = 0; i < 20; i++)
FireTimer();
// Now send user activity.
SendUserActivity();
histogram_tester_->ExpectTotalCount("DemoMode.ActiveApp", 0);
// Sample collection should resume.
for (int i = 0; i < 5; i++)
FireTimer();
SendUserActivity();
histogram_tester_->ExpectUniqueSample(
"DemoMode.ActiveApp", DemoSessionMetricsRecorder::DemoModeApp::kCamera,
5);
}
// Verify window activation during idle time doesn't trigger reporting.
TEST_F(DemoSessionMetricsRecorderTest, ActivateWindowWhenIdle) {
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow(extension_misc::kCameraAppId);
std::unique_ptr<aura::Window> chrome_app_window2 =
CreateChromeAppWindow(extension_misc::kGoogleKeepAppId);
wm::ActivateWindow(chrome_app_window.get());
// Even if the active window changes, which can happen automatically, these
// samples shouldn't be reported.
for (int i = 0; i < 10; i++)
FireTimer();
wm::ActivateWindow(chrome_app_window2.get());
for (int i = 0; i < 10; i++)
FireTimer();
SendUserActivity();
histogram_tester_->ExpectTotalCount("DemoMode.ActiveApp", 0);
}
TEST_F(DemoSessionMetricsRecorderTest, RepeatedUserActivity) {
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow(extension_misc::kCameraAppId);
std::unique_ptr<aura::Window> arc_window =
CreateArcWindow("com.google.Photos");
wm::ActivateWindow(chrome_app_window.get());
FireTimer();
SendUserActivity();
SendUserActivity();
// Switching between windows in between samples isn't recorded, even with user
// action.
FireTimer();
SendUserActivity();
wm::ActivateWindow(arc_window.get());
SendUserActivity();
wm::ActivateWindow(chrome_app_window.get());
SendUserActivity();
FireTimer();
wm::ActivateWindow(arc_window.get());
SendUserActivity();
SendUserActivity();
SendUserActivity();
histogram_tester_->ExpectUniqueSample(
"DemoMode.ActiveApp", DemoSessionMetricsRecorder::DemoModeApp::kCamera,
3);
}
// Verify remaining samples are recorded on exit.
TEST_F(DemoSessionMetricsRecorderTest, RecordOnExit) {
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow(extension_misc::kGoogleKeepAppId);
std::unique_ptr<aura::Window> arc_window =
CreateArcWindow("com.google.Photos");
wm::ActivateWindow(chrome_app_window.get());
for (int i = 0; i < 2; i++)
FireTimer();
wm::ActivateWindow(arc_window.get());
for (int i = 0; i < 4; i++)
FireTimer();
DeleteMetricsRecorder();
histogram_tester_->ExpectBucketCount(
"DemoMode.ActiveApp",
DemoSessionMetricsRecorder::DemoModeApp::kGoogleKeep, 2);
histogram_tester_->ExpectBucketCount(
"DemoMode.ActiveApp",
DemoSessionMetricsRecorder::DemoModeApp::kGooglePhotos, 4);
histogram_tester_->ExpectTotalCount("DemoMode.ActiveApp", 6);
}
// Verify remaining samples are not recorded on exit because the user became
// idle.
TEST_F(DemoSessionMetricsRecorderTest, IgnoreOnIdleExit) {
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow(extension_misc::kFilesManagerAppId);
wm::ActivateWindow(chrome_app_window.get());
for (int i = 0; i < 10; i++)
FireTimer();
SendUserActivity();
histogram_tester_->ExpectBucketCount(
"DemoMode.ActiveApp", DemoSessionMetricsRecorder::DemoModeApp::kFiles,
10);
ClearHistograms();
for (int i = 0; i < 20; i++)
FireTimer();
DeleteMetricsRecorder();
histogram_tester_->ExpectTotalCount("DemoMode.ActiveApp", 0);
}
// Verify remaining samples are not recorded on exit when the user was idle the
// whole time.
TEST_F(DemoSessionMetricsRecorderTest, IgnoreOnIdleSession) {
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow(extension_misc::kHighlightsAppId);
wm::ActivateWindow(chrome_app_window.get());
for (int i = 0; i < 20; i++)
FireTimer();
DeleteMetricsRecorder();
histogram_tester_->ExpectTotalCount("DemoMode.ActiveApp", 0);
}
TEST_F(DemoSessionMetricsRecorderTest, UniqueAppsLaunchedOnDeletion) {
// Activate each window twice. Despite activating each twice,
// the count should only be incremented once per unique app.
std::unique_ptr<aura::Window> chrome_app_window =
CreateChromeAppWindow(extension_misc::kCameraAppId);
wm::ActivateWindow(chrome_app_window.get());
wm::DeactivateWindow(chrome_app_window.get());
wm::ActivateWindow(chrome_app_window.get());
std::unique_ptr<aura::Window> chrome_browser_window =
CreateChromeAppWindow(extension_misc::kChromeAppId);
wm::ActivateWindow(chrome_browser_window.get());
wm::DeactivateWindow(chrome_browser_window.get());
wm::ActivateWindow(chrome_browser_window.get());
std::unique_ptr<aura::Window> arc_window_1 =
CreateArcWindow("com.google.Photos");
wm::ActivateWindow(arc_window_1.get());
wm::DeactivateWindow(arc_window_1.get());
wm::ActivateWindow(arc_window_1.get());
std::unique_ptr<aura::Window> arc_window_2 =
CreateArcWindow("com.google.Maps");
wm::ActivateWindow(arc_window_2.get());
wm::DeactivateWindow(arc_window_2.get());
wm::ActivateWindow(arc_window_2.get());
// Popup windows shouldn't be counted at all.
std::unique_ptr<aura::Window> popup_window = CreatePopupWindow();
wm::ActivateWindow(popup_window.get());
wm::DeactivateWindow(popup_window.get());
wm::ActivateWindow(popup_window.get());
DeleteMetricsRecorder();
histogram_tester_->ExpectUniqueSample("DemoMode.UniqueAppsLaunched", 4, 1);
}
TEST_F(DemoSessionMetricsRecorderTest,
NoUniqueAppsLaunchedOnMissingArcPackageName) {
// Create an ARC window with no package name set yet
std::unique_ptr<aura::Window> arc_window_1 = CreateArcWindow("");
wm::ActivateWindow(arc_window_1.get());
DeleteMetricsRecorder();
// There shuld be no unique apps reported if there was no package name.
histogram_tester_->ExpectUniqueSample("DemoMode.UniqueAppsLaunched", 0, 1);
}
TEST_F(DemoSessionMetricsRecorderTest,
UniqueAppsLaunchedOnDelayedArcPackageName) {
// Create an ARC window with no package name set yet.
std::unique_ptr<aura::Window> arc_window_1 = CreateArcWindow("");
wm::ActivateWindow(arc_window_1.get());
// Set the package name after window creation/activation.
arc_window_1->SetProperty(kArcPackageNameKey,
new std::string("com.google.Photos"));
std::unique_ptr<aura::Window> arc_window_2 =
CreateArcWindow("com.google.Maps");
wm::ActivateWindow(arc_window_2.get());
DeleteMetricsRecorder();
// There should be 2 unique apps reported.
histogram_tester_->ExpectUniqueSample("DemoMode.UniqueAppsLaunched", 2, 1);
}
TEST_F(DemoSessionMetricsRecorderTest, NoUniqueAppsLaunchedOnDeletion) {
DeleteMetricsRecorder();
// There should be no samples if the recorder is deleted with 0 unique apps
// launched.
histogram_tester_->ExpectUniqueSample("DemoMode.UniqueAppsLaunched", 0, 1);
}
} // namespace
} // namespace ash