blob: 73811bf3f8927ad18e2148cba17e15d8b9f561d6 [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/containers/flat_map.h"
#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
namespace {
using TabsStats = TabStatsDataStore::TabsStats;
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
class MockTabStatsTrackerDelegate : public TabStatsTrackerDelegate {
public:
MockTabStatsTrackerDelegate() = default;
~MockTabStatsTrackerDelegate() override = default;
#if defined(OS_WIN)
OcclusionStatusMap CallComputeNativeWindowOcclusionStatus(
std::vector<aura::WindowTreeHost*> hosts) override {
// Checking that the hosts are not nullptr, because of a bug where nullptr
// was being passed in addition to the desired aura::WindowTreeHost
// pointers, causing a crash when dereferenced. Crash bug found at:
// crbug.com/837541
for (aura::WindowTreeHost* host : hosts)
DCHECK(host);
return mock_occlusion_results_;
}
void SetMockOcclusionResults(OcclusionStatusMap mock_occlusion_results) {
mock_occlusion_results_ = mock_occlusion_results;
}
private:
OcclusionStatusMap mock_occlusion_results_;
#endif
};
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:
// Used to make sure that the metrics are reported properly.
base::HistogramTester histogram_tester_;
TabStatsTracker* tab_stats_tracker_;
DISALLOW_COPY_AND_ASSIGN(TabStatsTrackerBrowserTest);
};
IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest,
TabsAndWindowsAreCountedAccurately) {
// Assert that the |TabStatsTracker| instance is initialized during the
// creation of the main browser.
ASSERT_TRUE(tab_stats_tracker_ != nullptr);
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());
}
IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest,
TabDeletionGetsHandledProperly) {
// Assert that the |TabStatsTracker| instance is initialized during the
// creation of the main browser.
ASSERT_TRUE(tab_stats_tracker_ != nullptr);
constexpr base::TimeDelta kValidLongInterval = base::TimeDelta::FromHours(12);
TabStatsDataStore* data_store = tab_stats_tracker_->tab_stats_data_store();
TabStatsDataStore::TabsStateDuringIntervalMap* interval_map =
data_store->AddInterval();
AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED);
EXPECT_EQ(2U, interval_map->size());
content::WebContents* web_contents =
data_store->existing_tabs_for_testing()->begin()->first;
// Delete one of the WebContents without calling the |OnTabRemoved| function,
// the WebContentsObserver owned by |tab_stats_tracker_| should be notified
// and this should be handled correctly.
TabStatsDataStore::TabID tab_id =
data_store->GetTabIDForTesting(web_contents).value();
browser()->tab_strip_model()->DetachWebContentsAt(
browser()->tab_strip_model()->GetIndexOfWebContents(web_contents));
EXPECT_TRUE(base::ContainsKey(*interval_map, tab_id));
tab_stats_tracker_->OnInterval(kValidLongInterval, interval_map);
EXPECT_EQ(1U, interval_map->size());
EXPECT_FALSE(base::ContainsKey(*interval_map, tab_id));
web_contents = data_store->existing_tabs_for_testing()->begin()->first;
// Do this a second time, ensures that the situation where there's no existing
// tabs is handled properly.
tab_id = data_store->GetTabIDForTesting(web_contents).value();
browser()->tab_strip_model()->DetachWebContentsAt(
browser()->tab_strip_model()->GetIndexOfWebContents(web_contents));
EXPECT_TRUE(base::ContainsKey(*interval_map, tab_id));
tab_stats_tracker_->OnInterval(kValidLongInterval, interval_map);
EXPECT_EQ(0U, interval_map->size());
}
#if defined(OS_WIN)
IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest,
TestCalculateAndRecordNativeWindowVisibilities) {
std::unique_ptr<MockTabStatsTrackerDelegate> mock_delegate =
std::make_unique<MockTabStatsTrackerDelegate>();
// Maintaining this reference to |mock_delegate| is safe because the
// TabStatsTracker will outlive this test class.
MockTabStatsTrackerDelegate* mock_delegate_raw = mock_delegate.get();
tab_stats_tracker_->SetDelegateForTesting(std::move(mock_delegate));
TabStatsTrackerDelegate::OcclusionStatusMap mock_occlusion_results;
mock_delegate_raw->SetMockOcclusionResults(mock_occlusion_results);
tab_stats_tracker_->CalculateAndRecordNativeWindowVisibilities();
// There should be 1 entry for each zero window bucket.
histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Occluded",
0, 1);
histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Visible",
0, 1);
histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Hidden",
0, 1);
// There should be no entries in the 1 window bucket.
histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Occluded",
1, 0);
histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Visible",
1, 0);
histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Hidden",
1, 0);
// Create a browser for each aura::Window::OcclusionState.
mock_occlusion_results[CreateBrowser(ProfileManager::GetActiveUserProfile())
->window()
->GetNativeWindow()
->GetHost()] =
aura::Window::OcclusionState::HIDDEN;
mock_occlusion_results[CreateBrowser(ProfileManager::GetActiveUserProfile())
->window()
->GetNativeWindow()
->GetHost()] =
aura::Window::OcclusionState::VISIBLE;
mock_occlusion_results[CreateBrowser(ProfileManager::GetActiveUserProfile())
->window()
->GetNativeWindow()
->GetHost()] =
aura::Window::OcclusionState::OCCLUDED;
mock_delegate_raw->SetMockOcclusionResults(mock_occlusion_results);
// There should now be 1 entry for each 1 window bucket.
tab_stats_tracker_->CalculateAndRecordNativeWindowVisibilities();
histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Occluded",
1, 1);
histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Visible",
1, 1);
histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Hidden",
1, 1);
mock_occlusion_results.clear();
// Create 5 aura::Window::OcclusionState browsers.
for (int count = 0; count < 5; count++) {
mock_occlusion_results[CreateBrowser(ProfileManager::GetActiveUserProfile())
->window()
->GetNativeWindow()
->GetHost()] =
aura::Window::OcclusionState::OCCLUDED;
}
mock_delegate_raw->SetMockOcclusionResults(mock_occlusion_results);
tab_stats_tracker_->CalculateAndRecordNativeWindowVisibilities();
// There should be 1 entry in the 5 window occluded bucket.
histogram_tester_.ExpectBucketCount("Windows.NativeWindowVisibility.Occluded",
5, 1);
}
#endif // defined(OS_WIN)
} // namespace metrics