blob: deeb45af1956a82b484c8e2114d94be5cda61a64 [file] [log] [blame]
// Copyright 2017 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/metrics/tab_stats_tracker.h"
#include "base/test/histogram_tester.h"
#include "base/test/power_monitor_test_base.h"
#include "base/test/scoped_task_environment.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/metrics_service.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
namespace {
using TabsStats = TabStatsDataStore::TabsStats;
class TabStatsTrackerBrowserTest : public InProcessBrowserTest {
public:
TabStatsTrackerBrowserTest() : tab_stats_tracker_(nullptr) {}
void SetUpOnMainThread() override {
tab_stats_tracker_ = TabStatsTracker::GetInstance();
ASSERT_TRUE(tab_stats_tracker_ != nullptr);
}
protected:
TabStatsTracker* tab_stats_tracker_;
DISALLOW_COPY_AND_ASSIGN(TabStatsTrackerBrowserTest);
};
class TestTabStatsTracker : public TabStatsTracker {
public:
using UmaStatsReportingDelegate = TabStatsTracker::UmaStatsReportingDelegate;
explicit TestTabStatsTracker(PrefService* pref_service)
: TabStatsTracker(pref_service), pref_service_(pref_service) {
// Stop the timer to ensure that the stats don't get reported (and reset)
// while running the tests.
EXPECT_TRUE(timer()->IsRunning());
timer()->Stop();
}
~TestTabStatsTracker() override {}
// Helper functions to update the number of tabs/windows.
size_t AddTabs(size_t tab_count) {
tab_stats_data_store()->OnTabsAdded(tab_count);
return tab_stats_data_store()->tab_stats().total_tab_count;
}
size_t RemoveTabs(size_t tab_count) {
EXPECT_LE(tab_count, tab_stats_data_store()->tab_stats().total_tab_count);
tab_stats_data_store()->OnTabsRemoved(tab_count);
return tab_stats_data_store()->tab_stats().total_tab_count;
}
size_t AddWindows(size_t window_count) {
for (size_t i = 0; i < window_count; ++i)
tab_stats_data_store()->OnWindowAdded();
return tab_stats_data_store()->tab_stats().window_count;
}
size_t RemoveWindows(size_t window_count) {
EXPECT_LE(window_count, tab_stats_data_store()->tab_stats().window_count);
for (size_t i = 0; i < window_count; ++i)
tab_stats_data_store()->OnWindowRemoved();
return tab_stats_data_store()->tab_stats().window_count;
}
void CheckDailyEventInterval() { daily_event()->CheckInterval(); }
void TriggerDailyEvent() {
// Reset the daily event to allow triggering the DailyEvent::OnInterval
// manually several times in the same test.
reset_daily_event(new DailyEvent(pref_service_, prefs::kTabStatsDailySample,
kTabStatsDailyEventHistogramName));
daily_event()->AddObserver(base::MakeUnique<TabStatsDailyObserver>(
reporting_delegate(), tab_stats_data_store()));
// Update the daily event registry to the previous day and trigger it.
base::Time last_time = base::Time::Now() - base::TimeDelta::FromHours(25);
pref_service_->SetInt64(prefs::kTabStatsDailySample,
last_time.since_origin().InMicroseconds());
CheckDailyEventInterval();
// The daily event registry should have been updated.
EXPECT_NE(last_time.since_origin().InMicroseconds(),
pref_service_->GetInt64(prefs::kTabStatsDailySample));
}
TabStatsDataStore* data_store() { return tab_stats_data_store(); }
private:
PrefService* pref_service_;
DISALLOW_COPY_AND_ASSIGN(TestTabStatsTracker);
};
class TabStatsTrackerTest : public testing::Test {
public:
using UmaStatsReportingDelegate =
TestTabStatsTracker::UmaStatsReportingDelegate;
TabStatsTrackerTest() {
power_monitor_source_ = new base::PowerMonitorTestSource();
power_monitor_.reset(new base::PowerMonitor(
std::unique_ptr<base::PowerMonitorSource>(power_monitor_source_)));
MetricsService::RegisterPrefs(pref_service_.registry());
// The tab stats tracker has to be created after the power monitor as it's
// using it.
tab_stats_tracker_.reset(new TestTabStatsTracker(&pref_service_));
}
void TearDown() override { tab_stats_tracker_.reset(nullptr); }
// The Power Monitor requires a task environment.
base::test::ScopedTaskEnvironment scoped_task_environment_;
// The tabs stat tracker instance, it should be created in the SetUp
std::unique_ptr<TestTabStatsTracker> tab_stats_tracker_;
// Used to simulate power events.
base::PowerMonitorTestSource* power_monitor_source_;
std::unique_ptr<base::PowerMonitor> power_monitor_;
// Used to make sure that the metrics are reported properly.
base::HistogramTester histogram_tester_;
TestingPrefServiceSimple pref_service_;
private:
DISALLOW_COPY_AND_ASSIGN(TabStatsTrackerTest);
};
// Comparator for base::Bucket values.
bool CompareHistogramBucket(const base::Bucket& l, const base::Bucket& r) {
return l.min < r.min;
}
void EnsureTabStatsMatchExpectations(const TabsStats& expected,
const TabsStats& actual) {
EXPECT_EQ(expected.total_tab_count, actual.total_tab_count);
EXPECT_EQ(expected.total_tab_count_max, actual.total_tab_count_max);
EXPECT_EQ(expected.max_tab_per_window, actual.max_tab_per_window);
EXPECT_EQ(expected.window_count, actual.window_count);
EXPECT_EQ(expected.window_count_max, actual.window_count_max);
}
} // namespace
IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest,
TabsAndWindowsAreCountedAccurately) {
// Assert that the |TabStatsTracker| instance is initialized during the
// creation of the main browser.
ASSERT_NE(static_cast<TabStatsTracker*>(nullptr), tab_stats_tracker_);
TabsStats expected_stats = {};
// There should be only one window with one tab at startup.
expected_stats.total_tab_count = 1;
expected_stats.total_tab_count_max = 1;
expected_stats.max_tab_per_window = 1;
expected_stats.window_count = 1;
expected_stats.window_count_max = 1;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
// Add a tab and make sure that the counters get updated.
AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED);
++expected_stats.total_tab_count;
++expected_stats.total_tab_count_max;
++expected_stats.max_tab_per_window;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
browser()->tab_strip_model()->CloseWebContentsAt(1, 0);
--expected_stats.total_tab_count;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
Browser* browser = CreateBrowser(ProfileManager::GetActiveUserProfile());
++expected_stats.total_tab_count;
++expected_stats.window_count;
++expected_stats.window_count_max;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
AddTabAtIndexToBrowser(browser, 1, GURL("about:blank"),
ui::PAGE_TRANSITION_TYPED, true);
++expected_stats.total_tab_count;
++expected_stats.total_tab_count_max;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
CloseBrowserSynchronously(browser);
expected_stats.total_tab_count = 1;
expected_stats.window_count = 1;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
}
TEST_F(TabStatsTrackerTest, OnResume) {
// Makes sure that there's no sample initially.
histogram_tester_.ExpectTotalCount(
UmaStatsReportingDelegate::kNumberOfTabsOnResumeHistogramName, 0);
// Creates some tabs.
size_t expected_tab_count = tab_stats_tracker_->AddTabs(12);
std::vector<base::Bucket> count_buckets;
count_buckets.push_back(base::Bucket(expected_tab_count, 1));
// Generates a resume event that should end up calling the
// |ReportTabCountOnResume| method of the reporting delegate.
power_monitor_source_->GenerateSuspendEvent();
power_monitor_source_->GenerateResumeEvent();
// There should be only one sample for the |kNumberOfTabsOnResume| histogram.
histogram_tester_.ExpectTotalCount(
UmaStatsReportingDelegate::kNumberOfTabsOnResumeHistogramName,
count_buckets.size());
EXPECT_EQ(histogram_tester_.GetAllSamples(
UmaStatsReportingDelegate::kNumberOfTabsOnResumeHistogramName),
count_buckets);
// Removes some tabs and update the expectations.
expected_tab_count = tab_stats_tracker_->RemoveTabs(5);
count_buckets.push_back(base::Bucket(expected_tab_count, 1));
std::sort(count_buckets.begin(), count_buckets.end(), CompareHistogramBucket);
// Generates another resume event.
power_monitor_source_->GenerateSuspendEvent();
power_monitor_source_->GenerateResumeEvent();
// There should be 2 samples for this metric now.
histogram_tester_.ExpectTotalCount(
UmaStatsReportingDelegate::kNumberOfTabsOnResumeHistogramName,
count_buckets.size());
EXPECT_EQ(histogram_tester_.GetAllSamples(
UmaStatsReportingDelegate::kNumberOfTabsOnResumeHistogramName),
count_buckets);
}
TEST_F(TabStatsTrackerTest, StatsGetReportedDaily) {
// This test ensures that the stats get reported accurately when the daily
// event triggers.
// Adds some tabs and windows, then remove some so the maximums are not equal
// to the current state.
size_t expected_tab_count = tab_stats_tracker_->AddTabs(12);
size_t expected_window_count = tab_stats_tracker_->AddWindows(5);
size_t expected_max_tab_per_window = expected_tab_count - 1;
tab_stats_tracker_->data_store()->UpdateMaxTabsPerWindowIfNeeded(
expected_max_tab_per_window);
expected_tab_count = tab_stats_tracker_->RemoveTabs(5);
expected_window_count = tab_stats_tracker_->RemoveWindows(2);
expected_max_tab_per_window = expected_tab_count - 1;
TabsStats stats = tab_stats_tracker_->data_store()->tab_stats();
// Trigger the daily event.
tab_stats_tracker_->TriggerDailyEvent();
// Ensures that the histograms have been properly updated.
histogram_tester_.ExpectBucketCount(
UmaStatsReportingDelegate::kMaxTabsInADayHistogramName,
stats.total_tab_count_max, 1);
histogram_tester_.ExpectBucketCount(
UmaStatsReportingDelegate::kMaxTabsPerWindowInADayHistogramName,
stats.max_tab_per_window, 1);
histogram_tester_.ExpectBucketCount(
UmaStatsReportingDelegate::kMaxWindowsInADayHistogramName,
stats.window_count_max, 1);
// Manually call the function to update the maximum number of tabs in a single
// window. This is normally done automatically in the reporting function by
// scanning the list of existing windows, but doesn't work here as this isn't
// a window test.
tab_stats_tracker_->data_store()->UpdateMaxTabsPerWindowIfNeeded(
expected_max_tab_per_window);
// Make sure that the maximum values have been updated to the current state.
stats = tab_stats_tracker_->data_store()->tab_stats();
EXPECT_EQ(expected_tab_count, stats.total_tab_count_max);
EXPECT_EQ(expected_max_tab_per_window, stats.max_tab_per_window);
EXPECT_EQ(expected_window_count, stats.window_count_max);
EXPECT_EQ(expected_tab_count, static_cast<size_t>(pref_service_.GetInteger(
prefs::kTabStatsTotalTabCountMax)));
EXPECT_EQ(expected_max_tab_per_window,
static_cast<size_t>(
pref_service_.GetInteger(prefs::kTabStatsMaxTabsPerWindow)));
EXPECT_EQ(expected_window_count, static_cast<size_t>(pref_service_.GetInteger(
prefs::kTabStatsWindowCountMax)));
// Trigger the daily event.
tab_stats_tracker_->TriggerDailyEvent();
// The values in the histograms should now be equal to the current state.
histogram_tester_.ExpectBucketCount(
UmaStatsReportingDelegate::kMaxTabsInADayHistogramName,
stats.total_tab_count_max, 1);
histogram_tester_.ExpectBucketCount(
UmaStatsReportingDelegate::kMaxTabsPerWindowInADayHistogramName,
stats.max_tab_per_window, 1);
histogram_tester_.ExpectBucketCount(
UmaStatsReportingDelegate::kMaxWindowsInADayHistogramName,
stats.window_count_max, 1);
}
} // namespace metrics