// Copyright 2016 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 "components/sync_sessions/sync_sessions_metrics.h" #include #include #include "base/test/histogram_tester.h" #include "base/time/time.h" #include "components/sessions/core/session_types.h" #include "components/sync_sessions/fake_sync_sessions_client.h" #include "components/sync_sessions/sessions_sync_manager.h" #include "components/sync_sessions/synced_session.h" #include "testing/gtest/include/gtest/gtest.h" using sessions::SessionWindow; using sessions::SessionTab; using base::Time; using base::TimeDelta; namespace sync_sessions { namespace { class FakeSessionsSyncManager : public SessionsSyncManager { public: FakeSessionsSyncManager(SyncSessionsClient* sessions_client, std::vector>* sessions) : SessionsSyncManager(sessions_client, nullptr, nullptr, nullptr, base::Closure(), base::Closure()), sessions_(sessions) {} bool GetAllForeignSessions( std::vector* sessions) override { for (auto& session : *sessions_) { sessions->push_back(session.get()); } return true; } private: std::vector>* sessions_; }; Time SecondsFromEpoch(int seconds) { return Time::UnixEpoch() + TimeDelta::FromSeconds(seconds); } } // namespace class SyncSessionsMetricsTest : public ::testing::Test { protected: SyncSessionsMetricsTest() : fake_manager_(&fake_client_, &sessions_) {} // Sets the tab/window/session timestamps and creates anything needed. The new // calls in here are safe because the session/window objects are going to // delete all their children when their destructors are invoked. void PushTab(size_t tabIndex, int windowIndex, Time timestamp) { // First add sessions/windows as necessary. while (tabIndex >= sessions_.size()) { sessions_.push_back(std::make_unique()); } if (sessions_[tabIndex]->windows.find(windowIndex) == sessions_[tabIndex]->windows.end()) { sessions_[tabIndex]->windows[windowIndex] = std::make_unique(); } sessions_[tabIndex]->modified_time = std::max(sessions_[tabIndex]->modified_time, timestamp); sessions_[tabIndex]->windows[windowIndex]->wrapped_window.timestamp = std::max( sessions_[tabIndex]->windows[windowIndex]->wrapped_window.timestamp, timestamp); sessions_[tabIndex]->windows[windowIndex]->wrapped_window.tabs.push_back( std::make_unique()); sessions_[tabIndex] ->windows[windowIndex] ->wrapped_window.tabs.back() ->timestamp = timestamp; } // Removes the last tab at the given indexes. The idexes provided should be // valid for existing data, this method does not check their validity. Windows // are not cleaned up/removed if they're left with 0 tabs. void PopTab(size_t tabIndex, int windowIndex, Time timestamp) { sessions_[tabIndex]->modified_time = std::max(sessions_[tabIndex]->modified_time, timestamp); sessions_[tabIndex]->windows[windowIndex]->wrapped_window.timestamp = std::max( sessions_[tabIndex]->windows[windowIndex]->wrapped_window.timestamp, timestamp); sessions_[tabIndex]->windows[windowIndex]->wrapped_window.tabs.pop_back(); } // Runs MaxTabTimestamp on the current sessions data. Time MaxTabTimestamp() { std::vector typed_sessions; for (auto& session : sessions_) { typed_sessions.push_back(session.get()); } return SyncSessionsMetrics::MaxTabTimestamp(typed_sessions); } SessionsSyncManager* get_sessions_sync_manager() { return &fake_manager_; } private: std::vector> sessions_; FakeSyncSessionsClient fake_client_; FakeSessionsSyncManager fake_manager_; }; TEST_F(SyncSessionsMetricsTest, NoWindows) { ASSERT_EQ(Time::UnixEpoch(), MaxTabTimestamp()); } TEST_F(SyncSessionsMetricsTest, NoTabs) { PushTab(0, 0, SecondsFromEpoch(1)); PopTab(0, 0, SecondsFromEpoch(2)); ASSERT_EQ(Time::UnixEpoch(), MaxTabTimestamp()); } TEST_F(SyncSessionsMetricsTest, OneTab) { PushTab(0, 0, SecondsFromEpoch(1)); ASSERT_EQ(SecondsFromEpoch(1), MaxTabTimestamp()); } TEST_F(SyncSessionsMetricsTest, MultipleTabs) { PushTab(0, 0, SecondsFromEpoch(1)); PushTab(0, 0, SecondsFromEpoch(3)); PushTab(0, 0, SecondsFromEpoch(2)); ASSERT_EQ(SecondsFromEpoch(3), MaxTabTimestamp()); } TEST_F(SyncSessionsMetricsTest, MultipleWindows) { PushTab(0, 1, SecondsFromEpoch(1)); PushTab(0, 2, SecondsFromEpoch(3)); PushTab(0, 3, SecondsFromEpoch(2)); ASSERT_EQ(SecondsFromEpoch(3), MaxTabTimestamp()); } TEST_F(SyncSessionsMetricsTest, OrderedSessions) { PushTab(0, 0, SecondsFromEpoch(3)); PushTab(1, 0, SecondsFromEpoch(2)); PushTab(2, 0, SecondsFromEpoch(1)); ASSERT_EQ(SecondsFromEpoch(3), MaxTabTimestamp()); } TEST_F(SyncSessionsMetricsTest, NonOrderedSessions) { // While 2 is not the max, it should give up when it sees the tab at index 1. // This is breaking the assumptions of the logic we're testing, and thus we // expect to return an incorrect ansewr. What this test is really verifying // is that the logic that finds the most recent timestamp is is exiting early // instead of inefficiently examining all foreign tabs. PushTab(0, 0, SecondsFromEpoch(2)); PushTab(1, 0, SecondsFromEpoch(1)); PushTab(2, 0, SecondsFromEpoch(3)); ASSERT_EQ(SecondsFromEpoch(2), MaxTabTimestamp()); } TEST_F(SyncSessionsMetricsTest, OrderedSessionsWithDeletedTab) { // Tab/window with index 0 is going to be the most recent, but the recent tab // was removed. The algorithm should continue on and find the tab in index 1. PushTab(0, 0, SecondsFromEpoch(1)); PushTab(0, 0, SecondsFromEpoch(4)); PopTab(0, 0, SecondsFromEpoch(5)); PushTab(1, 0, SecondsFromEpoch(3)); PushTab(2, 0, SecondsFromEpoch(2)); ASSERT_EQ(SecondsFromEpoch(3), MaxTabTimestamp()); } TEST_F(SyncSessionsMetricsTest, SkipEmitNoManager) { base::HistogramTester histogram_tester; PushTab(0, 0, Time::Now() - TimeDelta::FromHours(1)); SyncSessionsMetrics::RecordYoungestForeignTabAgeOnNTP(nullptr); histogram_tester.ExpectTotalCount("Sync.YoungestForeignTabAgeOnNTP", 0); } TEST_F(SyncSessionsMetricsTest, SkipEmitNoSessions) { base::HistogramTester histogram_tester; SyncSessionsMetrics::RecordYoungestForeignTabAgeOnNTP( get_sessions_sync_manager()); histogram_tester.ExpectTotalCount("Sync.YoungestForeignTabAgeOnNTP", 0); } TEST_F(SyncSessionsMetricsTest, SkipEmitInvalidTimestamp) { base::HistogramTester histogram_tester; // Foreign session is far in the future, it should be ignored. PushTab(0, 0, Time::Now() + TimeDelta::FromHours(1)); SyncSessionsMetrics::RecordYoungestForeignTabAgeOnNTP( get_sessions_sync_manager()); histogram_tester.ExpectTotalCount("Sync.YoungestForeignTabAgeOnNTP", 0); } TEST_F(SyncSessionsMetricsTest, EmitNormalCase) { base::HistogramTester histogram_tester; PushTab(0, 0, Time::Now() - TimeDelta::FromHours(1)); SyncSessionsMetrics::RecordYoungestForeignTabAgeOnNTP( get_sessions_sync_manager()); histogram_tester.ExpectTotalCount("Sync.YoungestForeignTabAgeOnNTP", 1); } } // namespace sync_sessions