blob: aae6b28f21779ad952eedc19786b7b470a6b6b69 [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_data_store.h"
#include "chrome/browser/metrics/tab_stats_tracker.h"
#include "chrome/browser/ui/recently_audible_helper.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
namespace {
using TabsStats = TabStatsDataStore::TabsStats;
class TabStatsDataStoreTest : public ChromeRenderViewHostTestHarness {
protected:
TabStatsDataStoreTest() {
TabStatsTracker::RegisterPrefs(pref_service_.registry());
data_store_ = std::make_unique<TabStatsDataStore>(&pref_service_);
}
std::unique_ptr<content::WebContents> CreateTestWebContents() {
std::unique_ptr<content::WebContents> contents =
ChromeRenderViewHostTestHarness::CreateTestWebContents();
RecentlyAudibleHelper::CreateForWebContents(contents.get());
return contents;
}
TestingPrefServiceSimple pref_service_;
std::unique_ptr<TabStatsDataStore> data_store_;
};
} // namespace
TEST_F(TabStatsDataStoreTest, TabStatsGetsReloadedFromLocalState) {
// This tests creates add some tabs/windows to a data store instance and then
// reinitializes it (and so the count of active tabs/windows drops to zero).
// As the TabStatsTracker constructor restores its state from the pref service
// the maximums should be restored.
size_t expected_tab_count = 12;
std::vector<std::unique_ptr<content::WebContents>> test_web_contents_vec;
for (size_t i = 0; i < expected_tab_count; ++i) {
test_web_contents_vec.emplace_back(CreateTestWebContents());
data_store_->OnTabAdded(test_web_contents_vec.back().get());
}
size_t expected_window_count = 5;
for (size_t i = 0; i < expected_window_count; ++i)
data_store_->OnWindowAdded();
size_t expected_max_tab_per_window = expected_tab_count - 1;
data_store_->UpdateMaxTabsPerWindowIfNeeded(expected_max_tab_per_window);
TabsStats stats = 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);
// Reset the |tab_stats_tracker_| and ensure that the maximums are restored.
data_store_.reset(new TabStatsDataStore(&pref_service_));
TabsStats stats2 = data_store_->tab_stats();
EXPECT_EQ(stats.total_tab_count_max, stats2.total_tab_count_max);
EXPECT_EQ(stats.max_tab_per_window, stats2.max_tab_per_window);
EXPECT_EQ(stats.window_count_max, stats2.window_count_max);
// The actual number of tabs/window should be 0 as it's not stored in the
// pref service.
EXPECT_EQ(0U, stats2.window_count);
EXPECT_EQ(0U, stats2.total_tab_count);
}
TEST_F(TabStatsDataStoreTest, TrackTabUsageDuringInterval) {
std::vector<std::unique_ptr<content::WebContents>> web_contents_vec;
for (size_t i = 0; i < 3; ++i) {
web_contents_vec.emplace_back(CreateTestWebContents());
// Make sure that these WebContents are initially not visible.
web_contents_vec.back()->WasHidden();
}
// Creates a test interval.
TabStatsDataStore::TabsStateDuringIntervalMap* interval_map =
data_store_->AddInterval();
EXPECT_TRUE(interval_map->empty());
std::vector<TabStatsDataStore::TabID> web_contents_id_vec;
// Adds the tabs to the data store.
for (auto& iter : web_contents_vec) {
data_store_->OnTabAdded(iter.get());
web_contents_id_vec.push_back(
data_store_->GetTabIDForTesting(iter.get()).value());
}
EXPECT_EQ(web_contents_vec.size(), interval_map->size());
for (const auto& iter : web_contents_id_vec) {
auto map_iter = interval_map->find(iter);
EXPECT_TRUE(map_iter != interval_map->end());
// The tabs have been inserted after the creation of the interval.
EXPECT_FALSE(map_iter->second.existed_before_interval);
EXPECT_FALSE(map_iter->second.visible_or_audible_during_interval);
EXPECT_FALSE(map_iter->second.interacted_during_interval);
EXPECT_TRUE(map_iter->second.exists_currently);
}
// Interact with a tab.
data_store_->OnTabInteraction(web_contents_vec[1].get());
EXPECT_FALSE(interval_map->find(web_contents_id_vec[0])
->second.interacted_during_interval);
EXPECT_TRUE(interval_map->find(web_contents_id_vec[1])
->second.interacted_during_interval);
EXPECT_FALSE(interval_map->find(web_contents_id_vec[2])
->second.interacted_during_interval);
data_store_->ResetIntervalData(interval_map);
EXPECT_FALSE(interval_map->find(web_contents_id_vec[0])
->second.interacted_during_interval);
EXPECT_FALSE(interval_map->find(web_contents_id_vec[1])
->second.interacted_during_interval);
EXPECT_FALSE(interval_map->find(web_contents_id_vec[2])
->second.interacted_during_interval);
// Make the first WebContents visible.
web_contents_vec[0].get()->WasShown();
data_store_->OnTabVisible(web_contents_vec[0].get());
EXPECT_TRUE(interval_map->find(web_contents_id_vec[0])
->second.visible_or_audible_during_interval);
EXPECT_FALSE(interval_map->find(web_contents_id_vec[1])
->second.visible_or_audible_during_interval);
EXPECT_FALSE(interval_map->find(web_contents_id_vec[2])
->second.visible_or_audible_during_interval);
// Make one of the WebContents audible.
auto* audible_helper_2 =
RecentlyAudibleHelper::FromWebContents(web_contents_vec[2].get());
audible_helper_2->SetRecentlyAudibleForTesting();
data_store_->ResetIntervalData(interval_map);
data_store_->OnTabAudible(web_contents_vec[2].get());
EXPECT_TRUE(interval_map->find(web_contents_id_vec[0])
->second.visible_or_audible_during_interval);
EXPECT_FALSE(interval_map->find(web_contents_id_vec[1])
->second.visible_or_audible_during_interval);
EXPECT_TRUE(interval_map->find(web_contents_id_vec[2])
->second.visible_or_audible_during_interval);
// Make sure that the tab stats get properly copied when a tab is replaced.
TabStatsDataStore::TabStateDuringInterval tab_stats_copy =
interval_map->find(web_contents_id_vec[1])->second;
std::unique_ptr<content::WebContents> new_contents = CreateTestWebContents();
content::WebContents* old_contents = web_contents_vec[1].get();
data_store_->OnTabReplaced(old_contents, new_contents.get());
EXPECT_EQ(data_store_->GetTabIDForTesting(new_contents.get()).value(),
web_contents_id_vec[1]);
web_contents_vec[1] = std::move(new_contents);
// |old_contents| is invalid starting from here.
EXPECT_FALSE(data_store_->GetTabIDForTesting(old_contents));
auto iter = interval_map->find(web_contents_id_vec[1]);
EXPECT_TRUE(iter != interval_map->end());
EXPECT_EQ(tab_stats_copy.existed_before_interval,
iter->second.existed_before_interval);
EXPECT_EQ(tab_stats_copy.exists_currently, iter->second.exists_currently);
EXPECT_EQ(tab_stats_copy.visible_or_audible_during_interval,
iter->second.visible_or_audible_during_interval);
EXPECT_EQ(tab_stats_copy.interacted_during_interval,
iter->second.interacted_during_interval);
}
TEST_F(TabStatsDataStoreTest, OnTabReplaced) {
// Creates a tab and make sure that it's not visible.
std::unique_ptr<content::WebContents> web_contents_1(CreateTestWebContents());
web_contents_1->WasHidden();
// Creates a test interval.
TabStatsDataStore::TabsStateDuringIntervalMap* interval_map =
data_store_->AddInterval();
data_store_->OnTabAdded(web_contents_1.get());
TabStatsDataStore::TabID tab_id =
data_store_->GetTabIDForTesting(web_contents_1.get()).value();
// Interact with the tab.
data_store_->OnTabInteraction(web_contents_1.get());
EXPECT_TRUE(interval_map->find(tab_id)->second.interacted_during_interval);
EXPECT_FALSE(
interval_map->find(tab_id)->second.visible_or_audible_during_interval);
std::unique_ptr<content::WebContents> web_contents_2(CreateTestWebContents());
web_contents_2->WasHidden();
// Replace the tab, make sure that the |visible_or_audible_during_interval|
// bit is still set.
data_store_->OnTabReplaced(web_contents_1.get(), web_contents_2.get());
EXPECT_FALSE(data_store_->GetTabIDForTesting(web_contents_1.get()));
EXPECT_EQ(tab_id, data_store_->GetTabIDForTesting(web_contents_2.get()));
EXPECT_EQ(1U, interval_map->size());
EXPECT_TRUE(interval_map->find(tab_id)->second.interacted_during_interval);
// Mark the tab as audible and verify that the corresponding bit is set.
auto* audible_helper_2 =
RecentlyAudibleHelper::FromWebContents(web_contents_2.get());
audible_helper_2->SetNotRecentlyAudibleForTesting();
data_store_->OnTabAudible(web_contents_2.get());
EXPECT_TRUE(
interval_map->find(tab_id)->second.visible_or_audible_during_interval);
// Close the tab and make sure that the |exists_currently| bit gets cleared.
data_store_->OnTabRemoved(web_contents_2.get());
EXPECT_FALSE(interval_map->find(tab_id)->second.exists_currently);
// Add a new tab with a WebContents pointer that has already been used in the
// past, make sure that this gets treated as a new tab.
data_store_->OnTabAdded(web_contents_1.get());
EXPECT_EQ(2U, interval_map->size());
EXPECT_NE(tab_id, data_store_->GetTabIDForTesting(web_contents_1.get()));
}
} // namespace metrics