| // Copyright 2017 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/metrics/tab_stats/tab_stats_tracker.h" |
| |
| #include <algorithm> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/strings/strcat.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/power_monitor_test.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/tabs/tab_enums.h" |
| #include "chrome/browser/ui/tabs/tab_group.h" |
| #include "chrome/browser/ui/tabs/tab_group_model.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/chrome_render_view_host_test_harness.h" |
| #include "chrome/test/base/test_browser_window.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "components/tab_groups/tab_group_color.h" |
| #include "components/tab_groups/tab_group_id.h" |
| #include "components/tab_groups/tab_group_visual_data.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/navigation_simulator.h" |
| #include "content/public/test/web_contents_tester.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace metrics { |
| |
| namespace { |
| |
| using TabsStats = TabStatsDataStore::TabsStats; |
| |
| std::string GetHistogramNameWithBatteryStateSuffix(const char* histogram_name) { |
| const char* suffix = |
| base::PowerMonitor::IsOnBatteryPower() ? ".OnBattery" : ".PluggedIn"; |
| |
| return base::StrCat({histogram_name, suffix}); |
| } |
| |
| class TestTabStatsObserver : public TabStatsObserver { |
| public: |
| // Functions used to update the counts. |
| void OnPrimaryMainFrameNavigationCommitted( |
| content::WebContents* web_contents) override { |
| ++main_frame_committed_navigations_count_; |
| } |
| |
| size_t main_frame_committed_navigations_count() { |
| return main_frame_committed_navigations_count_; |
| } |
| |
| private: |
| size_t main_frame_committed_navigations_count_ = 0; |
| }; |
| |
| class TestTabStatsTracker : public TabStatsTracker { |
| public: |
| using TabStatsTracker::OnHeartbeatEvent; |
| using TabStatsTracker::OnInitialOrInsertedTab; |
| using TabStatsTracker::OnInterval; |
| using TabStatsTracker::TabChangedAt; |
| using UmaStatsReportingDelegate = TabStatsTracker::UmaStatsReportingDelegate; |
| |
| explicit TestTabStatsTracker(PrefService* pref_service); |
| |
| TestTabStatsTracker(const TestTabStatsTracker&) = delete; |
| TestTabStatsTracker& operator=(const TestTabStatsTracker&) = delete; |
| |
| ~TestTabStatsTracker() override {} |
| |
| // Helper functions to update the number of tabs/windows. |
| |
| size_t AddTabs(size_t tab_count, |
| ChromeRenderViewHostTestHarness* test_harness, |
| TabStripModel* tab_strip_model) { |
| EXPECT_TRUE(test_harness); |
| for (size_t i = 0; i < tab_count; ++i) { |
| std::unique_ptr<content::WebContents> tab = |
| test_harness->CreateTestWebContents(); |
| tab_strip_model->InsertWebContentsAt( |
| tab_strip_model->count(), std::move(tab), AddTabTypes::ADD_ACTIVE); |
| } |
| EXPECT_EQ(tab_stats_data_store()->tab_stats().total_tab_count, |
| static_cast<size_t>(tab_strip_model->count())); |
| return tab_stats_data_store()->tab_stats().total_tab_count; |
| } |
| |
| size_t RemoveTabs(size_t tab_count, TabStripModel* tab_strip_model) { |
| EXPECT_LE(tab_count, tab_stats_data_store()->tab_stats().total_tab_count); |
| EXPECT_LE(tab_count, static_cast<size_t>(tab_strip_model->count())); |
| for (size_t i = 0; i < tab_count; ++i) { |
| tab_strip_model->CloseWebContentsAt(tab_strip_model->count() - 1, |
| TabCloseTypes::CLOSE_USER_GESTURE); |
| } |
| 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 DiscardedStateChange(ChromeRenderViewHostTestHarness* test_harness, |
| ::mojom::LifecycleUnitDiscardReason reason, |
| bool is_discarded) { |
| std::unique_ptr<content::WebContents> tab = |
| test_harness->CreateTestWebContents(); |
| OnDiscardedStateChange(tab.get(), reason, is_discarded); |
| } |
| |
| void CheckDailyEventInterval() { daily_event_for_testing()->CheckInterval(); } |
| |
| void TriggerDailyEvent() { |
| // Reset the daily event to allow triggering the DailyEvent::OnInterval |
| // manually several times in the same test. |
| reset_daily_event_for_testing( |
| new DailyEvent(pref_service_, prefs::kTabStatsDailySample, |
| kTabStatsDailyEventHistogramName)); |
| daily_event_for_testing()->AddObserver( |
| std::make_unique<TabStatsDailyObserver>( |
| reporting_delegate_for_testing(), tab_stats_data_store())); |
| |
| // Update the daily event registry to the previous day and trigger it. |
| base::Time last_time = base::Time::Now() - base::Hours(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: |
| raw_ptr<PrefService> pref_service_; |
| }; |
| |
| class TestUmaStatsReportingDelegate |
| : public TestTabStatsTracker::UmaStatsReportingDelegate { |
| public: |
| using TestTabStatsTracker::UmaStatsReportingDelegate:: |
| GetIntervalHistogramName; |
| TestUmaStatsReportingDelegate() {} |
| |
| TestUmaStatsReportingDelegate(const TestUmaStatsReportingDelegate&) = delete; |
| TestUmaStatsReportingDelegate& operator=( |
| const TestUmaStatsReportingDelegate&) = delete; |
| |
| protected: |
| // Skip the check that ensures that there's at least one visible window as |
| // there's no window in the context of these tests. |
| bool IsChromeBackgroundedWithoutWindows() override { return false; } |
| }; |
| |
| class TabStatsTrackerTest : public ChromeRenderViewHostTestHarness { |
| public: |
| using UmaStatsReportingDelegate = |
| TestTabStatsTracker::UmaStatsReportingDelegate; |
| |
| TabStatsTrackerTest() { |
| TabStatsTracker::RegisterPrefs(pref_service_.registry()); |
| |
| // The tab stats tracker has to be created after the power monitor as it's |
| // using it. |
| tab_stats_tracker_ = std::make_unique<TestTabStatsTracker>(&pref_service_); |
| } |
| |
| TabStatsTrackerTest(const TabStatsTrackerTest&) = delete; |
| TabStatsTrackerTest& operator=(const TabStatsTrackerTest&) = delete; |
| |
| void SetUp() override { |
| ChromeRenderViewHostTestHarness::SetUp(); |
| browser_ = CreateBrowserWithTestWindowForParams( |
| Browser::CreateParams(profile(), true)); |
| tab_strip_model_ = browser_->tab_strip_model(); |
| } |
| |
| void TearDown() override { |
| tab_stats_tracker_->RemoveTabs(tab_strip_model_->count(), tab_strip_model_); |
| |
| tab_stats_tracker_.reset(nullptr); |
| tab_strip_model_ = nullptr; |
| browser_.reset(nullptr); |
| ChromeRenderViewHostTestHarness::TearDown(); |
| } |
| |
| // 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::test::ScopedPowerMonitorTestSource power_monitor_source_; |
| |
| // Used to make sure that the metrics are reported properly. |
| base::HistogramTester histogram_tester_; |
| |
| TestingPrefServiceSimple pref_service_; |
| |
| std::unique_ptr<Browser> browser_; |
| raw_ptr<TabStripModel> tab_strip_model_; |
| }; |
| |
| TestTabStatsTracker::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(daily_event_timer_for_testing()->IsRunning()); |
| daily_event_timer_for_testing()->Stop(); |
| |
| // Stop the usage interval timers so they don't trigger while running the |
| // tests. |
| usage_interval_timers_for_testing()->clear(); |
| |
| reset_reporting_delegate_for_testing(new TestUmaStatsReportingDelegate()); |
| |
| // Stop the heartbeat timer to ensure that it doesn't interfere with the |
| // tests. |
| heartbeat_timer_for_testing()->Stop(); |
| } |
| |
| // Comparator for base::Bucket values. |
| bool CompareHistogramBucket(const base::Bucket& l, const base::Bucket& r) { |
| return l.min < r.min; |
| } |
| |
| } // namespace |
| |
| TEST_F(TabStatsTrackerTest, MainFrameCommittedNavigationTriggersUpdate) { |
| constexpr const char kFirstUrl[] = "https://parent.com/"; |
| |
| TestTabStatsObserver tab_stats_observer; |
| tab_stats_tracker_->AddObserverAndSetInitialState(&tab_stats_observer); |
| // Number of navigations starts of at zero. |
| ASSERT_EQ(tab_stats_observer.main_frame_committed_navigations_count(), 0u); |
| |
| // Insert a new tab. |
| std::unique_ptr<content::WebContents> web_contents = CreateTestWebContents(); |
| tab_stats_tracker_->OnInitialOrInsertedTab(web_contents.get()); |
| |
| // Commit a main frame navigation on the observed tab. |
| auto* parent = content::NavigationSimulator::NavigateAndCommitFromBrowser( |
| web_contents.get(), GURL(kFirstUrl)); |
| DCHECK(parent); |
| |
| // Navigation registered. |
| ASSERT_EQ(tab_stats_observer.main_frame_committed_navigations_count(), 1u); |
| } |
| |
| 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, this, tab_strip_model_); |
| |
| std::vector<base::Bucket> count_buckets; |
| count_buckets.emplace_back(base::Bucket(expected_tab_count, 1)); |
| |
| EXPECT_FALSE(power_monitor_source_.IsOnBatteryPower()); |
| |
| // 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()); |
| histogram_tester_.ExpectTotalCount( |
| GetHistogramNameWithBatteryStateSuffix( |
| UmaStatsReportingDelegate::kNumberOfTabsOnResumeHistogramName), |
| count_buckets.size()); |
| EXPECT_EQ(histogram_tester_.GetAllSamples( |
| UmaStatsReportingDelegate::kNumberOfTabsOnResumeHistogramName), |
| count_buckets); |
| EXPECT_EQ( |
| histogram_tester_.GetAllSamples(GetHistogramNameWithBatteryStateSuffix( |
| UmaStatsReportingDelegate::kNumberOfTabsOnResumeHistogramName)), |
| count_buckets); |
| |
| // Removes some tabs and update the expectations. |
| expected_tab_count = tab_stats_tracker_->RemoveTabs(5, tab_strip_model_); |
| count_buckets.emplace_back(base::Bucket(expected_tab_count, 1)); |
| std::sort(count_buckets.begin(), count_buckets.end(), CompareHistogramBucket); |
| |
| power_monitor_source_.GeneratePowerStateEvent(true); |
| EXPECT_TRUE(power_monitor_source_.IsOnBatteryPower()); |
| // 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()); |
| histogram_tester_.ExpectTotalCount( |
| GetHistogramNameWithBatteryStateSuffix( |
| UmaStatsReportingDelegate::kNumberOfTabsOnResumeHistogramName), |
| 1); |
| EXPECT_EQ(histogram_tester_.GetAllSamples( |
| UmaStatsReportingDelegate::kNumberOfTabsOnResumeHistogramName), |
| count_buckets); |
| histogram_tester_.ExpectUniqueSample( |
| GetHistogramNameWithBatteryStateSuffix( |
| UmaStatsReportingDelegate::kNumberOfTabsOnResumeHistogramName), |
| expected_tab_count, 1); |
| } |
| |
| 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, this, tab_strip_model_); |
| size_t expected_window_count = tab_stats_tracker_->AddWindows(5); |
| size_t expected_max_tab_per_window = expected_tab_count; |
| tab_stats_tracker_->data_store()->UpdateMaxTabsPerWindowIfNeeded( |
| expected_max_tab_per_window); |
| expected_tab_count = tab_stats_tracker_->RemoveTabs(5, tab_strip_model_); |
| expected_window_count = tab_stats_tracker_->RemoveWindows(2); |
| expected_max_tab_per_window = expected_tab_count; |
| |
| TabsStats stats = tab_stats_tracker_->data_store()->tab_stats(); |
| |
| EXPECT_FALSE(power_monitor_source_.IsOnBatteryPower()); |
| // Trigger the daily event. |
| tab_stats_tracker_->TriggerDailyEvent(); |
| |
| // Ensures that the histograms have been properly updated. |
| histogram_tester_.ExpectUniqueSample( |
| UmaStatsReportingDelegate::kMaxTabsInADayHistogramName, |
| stats.total_tab_count_max, 1); |
| histogram_tester_.ExpectUniqueSample( |
| GetHistogramNameWithBatteryStateSuffix( |
| UmaStatsReportingDelegate::kMaxTabsInADayHistogramName), |
| stats.total_tab_count_max, 1); |
| histogram_tester_.ExpectUniqueSample( |
| UmaStatsReportingDelegate::kMaxTabsPerWindowInADayHistogramName, |
| stats.max_tab_per_window, 1); |
| histogram_tester_.ExpectUniqueSample( |
| GetHistogramNameWithBatteryStateSuffix( |
| UmaStatsReportingDelegate::kMaxTabsPerWindowInADayHistogramName), |
| stats.max_tab_per_window, 1); |
| histogram_tester_.ExpectUniqueSample( |
| UmaStatsReportingDelegate::kMaxWindowsInADayHistogramName, |
| stats.window_count_max, 1); |
| histogram_tester_.ExpectUniqueSample( |
| GetHistogramNameWithBatteryStateSuffix( |
| 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))); |
| |
| power_monitor_source_.GeneratePowerStateEvent(true); |
| EXPECT_TRUE(power_monitor_source_.IsOnBatteryPower()); |
| |
| // 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( |
| GetHistogramNameWithBatteryStateSuffix( |
| UmaStatsReportingDelegate::kMaxTabsInADayHistogramName), |
| stats.total_tab_count_max, 1); |
| histogram_tester_.ExpectBucketCount( |
| UmaStatsReportingDelegate::kMaxTabsPerWindowInADayHistogramName, |
| stats.max_tab_per_window, 1); |
| histogram_tester_.ExpectBucketCount( |
| GetHistogramNameWithBatteryStateSuffix( |
| UmaStatsReportingDelegate::kMaxTabsPerWindowInADayHistogramName), |
| stats.max_tab_per_window, 1); |
| histogram_tester_.ExpectBucketCount( |
| UmaStatsReportingDelegate::kMaxWindowsInADayHistogramName, |
| stats.window_count_max, 1); |
| histogram_tester_.ExpectBucketCount( |
| GetHistogramNameWithBatteryStateSuffix( |
| UmaStatsReportingDelegate::kMaxWindowsInADayHistogramName), |
| stats.window_count_max, 1); |
| } |
| |
| TEST_F(TabStatsTrackerTest, DailyDiscards) { |
| // This test checks that the discard/reload counts are reported when the |
| // daily event triggers. |
| |
| // Daily report is skipped when there is no tab. Adds tabs to avoid that. |
| tab_stats_tracker_->AddTabs(1, this, tab_strip_model_); |
| |
| constexpr size_t kExpectedDiscardsExternal = 3; |
| constexpr size_t kExpectedDiscardsUrgent = 5; |
| constexpr size_t kExpectedDiscardsProactive = 11; |
| constexpr size_t kExpectedReloadsExternal = 7; |
| constexpr size_t kExpectedReloadsUrgent = 9; |
| constexpr size_t kExpectedReloadsProactive = 10; |
| for (size_t i = 0; i < kExpectedDiscardsExternal; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::EXTERNAL, /*is_discarded*/ true); |
| } |
| for (size_t i = 0; i < kExpectedDiscardsUrgent; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::URGENT, /*is_discarded*/ true); |
| } |
| for (size_t i = 0; i < kExpectedDiscardsProactive; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::PROACTIVE, /*is_discarded*/ true); |
| } |
| for (size_t i = 0; i < kExpectedReloadsExternal; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::EXTERNAL, /*is_discarded*/ false); |
| } |
| for (size_t i = 0; i < kExpectedReloadsUrgent; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::URGENT, /*is_discarded*/ false); |
| } |
| for (size_t i = 0; i < kExpectedReloadsProactive; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::PROACTIVE, /*is_discarded*/ false); |
| } |
| |
| // Triggers the daily event. |
| tab_stats_tracker_->TriggerDailyEvent(); |
| |
| // Checks that the histograms have been properly updated. |
| histogram_tester_.ExpectUniqueSample( |
| UmaStatsReportingDelegate::kDailyDiscardsExternalHistogramName, |
| kExpectedDiscardsExternal, 1); |
| histogram_tester_.ExpectUniqueSample( |
| UmaStatsReportingDelegate::kDailyDiscardsUrgentHistogramName, |
| kExpectedDiscardsUrgent, 1); |
| histogram_tester_.ExpectUniqueSample( |
| UmaStatsReportingDelegate::kDailyDiscardsProactiveHistogramName, |
| kExpectedDiscardsProactive, 1); |
| histogram_tester_.ExpectUniqueSample( |
| UmaStatsReportingDelegate::kDailyReloadsExternalHistogramName, |
| kExpectedReloadsExternal, 1); |
| histogram_tester_.ExpectUniqueSample( |
| UmaStatsReportingDelegate::kDailyReloadsUrgentHistogramName, |
| kExpectedReloadsUrgent, 1); |
| histogram_tester_.ExpectUniqueSample( |
| UmaStatsReportingDelegate::kDailyReloadsProactiveHistogramName, |
| kExpectedReloadsProactive, 1); |
| |
| // Checks that the second report also updates the histograms properly. |
| constexpr size_t kExpectedDiscardsExternal2 = 15; |
| constexpr size_t kExpectedDiscardsUrgent2 = 25; |
| constexpr size_t kExpectedDiscardsProactive2 = 55; |
| constexpr size_t kExpectedReloadsExternal2 = 35; |
| constexpr size_t kExpectedReloadsUrgent2 = 45; |
| constexpr size_t kExpectedReloadsProactive2 = 40; |
| for (size_t i = 0; i < kExpectedDiscardsExternal2; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::EXTERNAL, /*is_discarded=*/true); |
| } |
| for (size_t i = 0; i < kExpectedDiscardsUrgent2; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::URGENT, /*is_discarded=*/true); |
| } |
| for (size_t i = 0; i < kExpectedDiscardsProactive2; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::PROACTIVE, /*is_discarded=*/true); |
| } |
| for (size_t i = 0; i < kExpectedReloadsExternal2; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::EXTERNAL, /*is_discarded=*/false); |
| } |
| for (size_t i = 0; i < kExpectedReloadsUrgent2; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::URGENT, /*is_discarded=*/false); |
| } |
| for (size_t i = 0; i < kExpectedReloadsProactive2; ++i) { |
| tab_stats_tracker_->DiscardedStateChange( |
| this, LifecycleUnitDiscardReason::PROACTIVE, /*is_discarded=*/false); |
| } |
| |
| // Triggers the daily event again. |
| tab_stats_tracker_->TriggerDailyEvent(); |
| |
| // Checks that the histograms have been properly updated. |
| histogram_tester_.ExpectBucketCount( |
| UmaStatsReportingDelegate::kDailyDiscardsExternalHistogramName, |
| kExpectedDiscardsExternal2, 1); |
| histogram_tester_.ExpectBucketCount( |
| UmaStatsReportingDelegate::kDailyDiscardsUrgentHistogramName, |
| kExpectedDiscardsUrgent2, 1); |
| histogram_tester_.ExpectBucketCount( |
| UmaStatsReportingDelegate::kDailyDiscardsProactiveHistogramName, |
| kExpectedDiscardsProactive2, 1); |
| histogram_tester_.ExpectBucketCount( |
| UmaStatsReportingDelegate::kDailyReloadsExternalHistogramName, |
| kExpectedReloadsExternal2, 1); |
| histogram_tester_.ExpectBucketCount( |
| UmaStatsReportingDelegate::kDailyReloadsUrgentHistogramName, |
| kExpectedReloadsUrgent2, 1); |
| histogram_tester_.ExpectBucketCount( |
| UmaStatsReportingDelegate::kDailyReloadsProactiveHistogramName, |
| kExpectedReloadsProactive2, 1); |
| } |
| |
| TEST_F(TabStatsTrackerTest, TabUsageGetsReported) { |
| constexpr base::TimeDelta kValidLongInterval = base::Hours(12); |
| TabStatsDataStore::TabsStateDuringIntervalMap* interval_map = |
| tab_stats_tracker_->data_store()->AddInterval(); |
| |
| std::vector<std::unique_ptr<content::WebContents>> web_contentses; |
| for (size_t i = 0; i < 4; ++i) { |
| web_contentses.emplace_back(CreateTestWebContents()); |
| // Make sure that these WebContents are initially not visible. |
| web_contentses[i]->WasHidden(); |
| tab_stats_tracker_->OnInitialOrInsertedTab(web_contentses[i].get()); |
| } |
| |
| tab_stats_tracker_->OnInterval(kValidLongInterval, interval_map); |
| |
| histogram_tester_.ExpectUniqueSample( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate:: |
| kUnusedAndClosedInIntervalHistogramNameBase, |
| kValidLongInterval), |
| 0, 1); |
| histogram_tester_.ExpectUniqueSample( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate::kUnusedTabsInIntervalHistogramNameBase, |
| kValidLongInterval), |
| web_contentses.size(), 1); |
| histogram_tester_.ExpectUniqueSample( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate::kUsedAndClosedInIntervalHistogramNameBase, |
| kValidLongInterval), |
| 0, 1); |
| histogram_tester_.ExpectUniqueSample( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate::kUsedTabsInIntervalHistogramNameBase, |
| kValidLongInterval), |
| 0, 1); |
| |
| // Mark one tab as visible and make sure that it get reported properly. |
| web_contentses[0]->WasShown(); |
| tab_stats_tracker_->OnInterval(kValidLongInterval, interval_map); |
| histogram_tester_.ExpectBucketCount( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate:: |
| kUnusedAndClosedInIntervalHistogramNameBase, |
| kValidLongInterval), |
| 0, 2); |
| histogram_tester_.ExpectBucketCount( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate::kUnusedTabsInIntervalHistogramNameBase, |
| kValidLongInterval), |
| web_contentses.size() - 1, 1); |
| histogram_tester_.ExpectBucketCount( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate::kUsedAndClosedInIntervalHistogramNameBase, |
| kValidLongInterval), |
| 0, 2); |
| histogram_tester_.ExpectBucketCount( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate::kUsedTabsInIntervalHistogramNameBase, |
| kValidLongInterval), |
| 1, 1); |
| |
| // Mark a tab as audible and make sure that we now have 2 tabs marked as used. |
| content::WebContentsTester::For(web_contentses[1].get()) |
| ->SetIsCurrentlyAudible(true); |
| tab_stats_tracker_->TabChangedAt(web_contentses[1].get(), 1, |
| TabChangeType::kAll); |
| tab_stats_tracker_->OnInterval(kValidLongInterval, interval_map); |
| histogram_tester_.ExpectBucketCount( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate::kUnusedTabsInIntervalHistogramNameBase, |
| kValidLongInterval), |
| web_contentses.size() - 2, 1); |
| histogram_tester_.ExpectBucketCount( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate::kUsedTabsInIntervalHistogramNameBase, |
| kValidLongInterval), |
| 2, 1); |
| |
| // Simulate an interaction on a tab, we should now see 3 tabs being marked as |
| // used. |
| content::WebContentsTester::For(web_contentses[2].get()) |
| ->TestDidReceiveMouseDownEvent(); |
| tab_stats_tracker_->OnInterval(kValidLongInterval, interval_map); |
| histogram_tester_.ExpectBucketCount( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate::kUnusedTabsInIntervalHistogramNameBase, |
| kValidLongInterval), |
| web_contentses.size() - 3, 1); |
| histogram_tester_.ExpectBucketCount( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate::kUsedTabsInIntervalHistogramNameBase, |
| kValidLongInterval), |
| 3, 1); |
| |
| // Remove the last WebContents, which should be reported as an unused tab. |
| web_contentses.pop_back(); |
| tab_stats_tracker_->OnInterval(kValidLongInterval, interval_map); |
| histogram_tester_.ExpectBucketCount( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate:: |
| kUnusedAndClosedInIntervalHistogramNameBase, |
| kValidLongInterval), |
| 1, 1); |
| |
| // Remove an active WebContents and make sure that this get reported properly. |
| // |
| // We need to re-interact with the WebContents as each call to |OnInterval| |
| // reset the interval and clear the interaction bit. |
| content::WebContentsTester::For(web_contentses.back().get()) |
| ->TestDidReceiveMouseDownEvent(); |
| web_contentses.pop_back(); |
| tab_stats_tracker_->OnInterval(kValidLongInterval, interval_map); |
| histogram_tester_.ExpectBucketCount( |
| TestUmaStatsReportingDelegate::GetIntervalHistogramName( |
| UmaStatsReportingDelegate::kUsedAndClosedInIntervalHistogramNameBase, |
| kValidLongInterval), |
| 1, 1); |
| } |
| |
| TEST_F(TabStatsTrackerTest, HeartbeatMetrics) { |
| size_t expected_tab_count = |
| tab_stats_tracker_->AddTabs(12, this, tab_strip_model_); |
| size_t expected_window_count = tab_stats_tracker_->AddWindows(5); |
| |
| tab_stats_tracker_->OnHeartbeatEvent(); |
| |
| histogram_tester_.ExpectBucketCount( |
| UmaStatsReportingDelegate::kTabCountHistogramName, expected_tab_count, 1); |
| histogram_tester_.ExpectBucketCount( |
| UmaStatsReportingDelegate::kWindowCountHistogramName, |
| expected_window_count, 1); |
| |
| expected_tab_count = tab_stats_tracker_->RemoveTabs(4, tab_strip_model_); |
| expected_window_count = tab_stats_tracker_->RemoveWindows(3); |
| |
| ASSERT_TRUE(tab_strip_model_->SupportsTabGroups()); |
| tab_groups::TabGroupId group_id1 = tab_strip_model_->AddToNewGroup({0, 1}); |
| tab_groups::TabGroupId group_id2 = tab_strip_model_->AddToNewGroup({5}); |
| const tab_groups::TabGroupVisualData visual_data( |
| u"Foo", tab_groups::TabGroupColorId::kCyan, /* is_collapsed = */ true); |
| TabGroup* group1 = tab_strip_model_->group_model()->GetTabGroup(group_id1); |
| TabGroup* group2 = tab_strip_model_->group_model()->GetTabGroup(group_id2); |
| group1->SetVisualData(visual_data); |
| group2->SetVisualData(visual_data); |
| ASSERT_TRUE(tab_strip_model_->IsGroupCollapsed(group_id1)); |
| ASSERT_TRUE(tab_strip_model_->IsGroupCollapsed(group_id2)); |
| |
| tab_stats_tracker_->OnHeartbeatEvent(); |
| |
| histogram_tester_.ExpectBucketCount( |
| UmaStatsReportingDelegate::kWindowCountHistogramName, |
| expected_window_count, 1); |
| } |
| |
| } // namespace metrics |