blob: 8a7fb2bbfa7d5c894c7207a9f2b9662c8dd80ff7 [file] [log] [blame]
// 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 "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/platform_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
namespace {
const base::TimeDelta kZeroTime = base::TimeDelta::FromSeconds(0);
const base::TimeDelta kInactivityTimeoutForTesting =
base::TimeDelta::FromSeconds(1);
// Mock class for |DesktopSessionDurationTracker| for testing.
class MockDesktopSessionDurationTracker : public DesktopSessionDurationTracker {
public:
MockDesktopSessionDurationTracker() {}
bool is_timeout() const { return time_out_; }
using DesktopSessionDurationTracker::OnAudioStart;
using DesktopSessionDurationTracker::OnAudioEnd;
protected:
void OnTimerFired() override {
DesktopSessionDurationTracker::OnTimerFired();
time_out_ = true;
}
private:
bool time_out_ = false;
DISALLOW_COPY_AND_ASSIGN(MockDesktopSessionDurationTracker);
};
// Mock class for |DesktopSessionDurationTracker::Observer| for testing.
class MockDesktopSessionObserver
: public metrics::DesktopSessionDurationTracker::Observer {
public:
MockDesktopSessionObserver() {}
int session_started_count() const { return session_started_count_; }
int session_ended_count() const { return session_ended_count_; }
protected:
// metrics::DesktopSessionDurationTracker::Observer:
void OnSessionStarted(base::TimeTicks session_start) override {
session_started_count_ = true;
}
void OnSessionEnded(base::TimeDelta session_length) override {
session_ended_count_ = true;
}
private:
int session_started_count_ = false;
int session_ended_count_ = false;
DISALLOW_COPY_AND_ASSIGN(MockDesktopSessionObserver);
};
class DesktopSessionDurationTrackerTest : public testing::Test {
public:
DesktopSessionDurationTrackerTest() {}
void SetUp() override {
metrics::DesktopSessionDurationTracker::Initialize();
instance_.SetInactivityTimeoutForTesting(kInactivityTimeoutForTesting);
}
void TearDown() override {
metrics::DesktopSessionDurationTracker::CleanupForTesting();
}
void ExpectTotalDuration(base::TimeDelta duration) {
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 1);
base::Bucket bucket =
histogram_tester_.GetAllSamples("Session.TotalDuration")[0];
int max_expected_value = duration.InMilliseconds();
EXPECT_LE(bucket.min, max_expected_value);
}
base::HistogramTester histogram_tester_;
MockDesktopSessionDurationTracker instance_;
MockDesktopSessionObserver observer_;
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
DISALLOW_COPY_AND_ASSIGN(DesktopSessionDurationTrackerTest);
};
} // namespace
TEST_F(DesktopSessionDurationTrackerTest, TestVisibility) {
// The browser becomes visible but it shouldn't start the session.
instance_.OnVisibilityChanged(true, kZeroTime);
EXPECT_FALSE(instance_.in_session());
EXPECT_TRUE(instance_.is_visible());
EXPECT_FALSE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
instance_.OnUserEvent();
EXPECT_TRUE(instance_.in_session());
EXPECT_TRUE(instance_.is_visible());
EXPECT_FALSE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
// Even if there is a recent user event visibility change should end the
// session.
instance_.OnUserEvent();
instance_.OnUserEvent();
instance_.OnVisibilityChanged(false, kZeroTime);
EXPECT_FALSE(instance_.in_session());
EXPECT_FALSE(instance_.is_visible());
EXPECT_FALSE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 1);
// For the second time only visibility change should start the session.
instance_.OnVisibilityChanged(true, kZeroTime);
EXPECT_TRUE(instance_.in_session());
EXPECT_TRUE(instance_.is_visible());
EXPECT_FALSE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 1);
instance_.OnVisibilityChanged(false, kZeroTime);
EXPECT_FALSE(instance_.in_session());
EXPECT_FALSE(instance_.is_visible());
EXPECT_FALSE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 2);
}
TEST_F(DesktopSessionDurationTrackerTest, TestUserEvent) {
EXPECT_FALSE(instance_.in_session());
EXPECT_FALSE(instance_.is_visible());
EXPECT_FALSE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
// User event doesn't go through if nothing is visible.
instance_.OnUserEvent();
EXPECT_FALSE(instance_.in_session());
EXPECT_FALSE(instance_.is_visible());
EXPECT_FALSE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
instance_.OnVisibilityChanged(true, kZeroTime);
instance_.OnUserEvent();
EXPECT_TRUE(instance_.in_session());
EXPECT_TRUE(instance_.is_visible());
EXPECT_FALSE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
// Wait until the session expires.
while (!instance_.is_timeout()) {
base::RunLoop().RunUntilIdle();
}
EXPECT_FALSE(instance_.in_session());
EXPECT_TRUE(instance_.is_visible());
EXPECT_FALSE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 1);
}
TEST_F(DesktopSessionDurationTrackerTest, TestAudioEvent) {
instance_.OnVisibilityChanged(true, kZeroTime);
instance_.OnAudioStart();
EXPECT_TRUE(instance_.in_session());
EXPECT_TRUE(instance_.is_visible());
EXPECT_TRUE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
instance_.OnVisibilityChanged(false, kZeroTime);
EXPECT_TRUE(instance_.in_session());
EXPECT_FALSE(instance_.is_visible());
EXPECT_TRUE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
instance_.OnAudioEnd();
EXPECT_TRUE(instance_.in_session());
EXPECT_FALSE(instance_.is_visible());
EXPECT_FALSE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
// Wait until the session expires.
while (!instance_.is_timeout()) {
base::RunLoop().RunUntilIdle();
}
EXPECT_FALSE(instance_.in_session());
EXPECT_FALSE(instance_.is_visible());
EXPECT_FALSE(instance_.is_audio_playing());
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 1);
}
TEST_F(DesktopSessionDurationTrackerTest, TestInputTimeoutDiscount) {
instance_.OnVisibilityChanged(true, kZeroTime);
base::TimeTicks before_session_start = base::TimeTicks::Now();
instance_.OnUserEvent(); // This should start the session
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
// Wait until the session expires.
while (!instance_.is_timeout()) {
base::RunLoop().RunUntilIdle();
}
base::TimeTicks after_session_end = base::TimeTicks::Now();
ExpectTotalDuration(after_session_end - before_session_start -
kInactivityTimeoutForTesting);
}
TEST_F(DesktopSessionDurationTrackerTest, TestVisibilityTimeoutDiscount) {
instance_.OnVisibilityChanged(true, kZeroTime);
base::TimeTicks before_session_start = base::TimeTicks::Now();
instance_.OnUserEvent(); // This should start the session
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
// Sleep a little while.
base::TimeDelta kDelay = base::TimeDelta::FromSeconds(2);
while (true) {
base::TimeDelta elapsed = base::TimeTicks::Now() - before_session_start;
if (elapsed >= kDelay)
break;
base::PlatformThread::Sleep(kDelay);
}
// End the session via visibility change.
instance_.OnVisibilityChanged(false, kDelay);
base::TimeTicks after_session_end = base::TimeTicks::Now();
ExpectTotalDuration(after_session_end - before_session_start - kDelay);
}
TEST_F(DesktopSessionDurationTrackerTest, TestObserver) {
instance_.AddObserver(&observer_);
EXPECT_FALSE(instance_.in_session());
EXPECT_FALSE(instance_.is_visible());
EXPECT_EQ(observer_.session_started_count(), 0);
EXPECT_EQ(observer_.session_ended_count(), 0);
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
instance_.OnVisibilityChanged(true, kZeroTime);
instance_.OnUserEvent();
EXPECT_TRUE(instance_.in_session());
EXPECT_TRUE(instance_.is_visible());
EXPECT_EQ(observer_.session_ended_count(), 0);
EXPECT_EQ(observer_.session_started_count(), 1);
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
// Wait until the session expires.
while (!instance_.is_timeout()) {
base::RunLoop().RunUntilIdle();
}
EXPECT_FALSE(instance_.in_session());
EXPECT_TRUE(instance_.is_visible());
EXPECT_EQ(observer_.session_started_count(), 1);
EXPECT_EQ(observer_.session_ended_count(), 1);
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 1);
}
TEST_F(DesktopSessionDurationTrackerTest, TestNoDoubleEndSession) {
instance_.AddObserver(&observer_);
EXPECT_FALSE(instance_.in_session());
EXPECT_FALSE(instance_.is_visible());
EXPECT_EQ(observer_.session_started_count(), 0);
EXPECT_EQ(observer_.session_ended_count(), 0);
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
instance_.OnVisibilityChanged(true, kZeroTime);
instance_.OnUserEvent();
EXPECT_TRUE(instance_.in_session());
EXPECT_TRUE(instance_.is_visible());
EXPECT_EQ(observer_.session_ended_count(), 0);
EXPECT_EQ(observer_.session_started_count(), 1);
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 0);
// Simulate Chrome being hidden. This should end the session.
instance_.OnVisibilityChanged(false, kZeroTime);
EXPECT_FALSE(instance_.in_session());
EXPECT_FALSE(instance_.is_visible());
EXPECT_EQ(observer_.session_ended_count(), 1);
EXPECT_EQ(observer_.session_started_count(), 1);
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 1);
// When the timeout expires, observers shouldn't be notified again and the
// histogram shouldn't be recorded again.
base::PlatformThread::Sleep(kInactivityTimeoutForTesting * 2);
EXPECT_EQ(observer_.session_ended_count(), 1);
EXPECT_EQ(observer_.session_started_count(), 1);
histogram_tester_.ExpectTotalCount("Session.TotalDuration", 1);
}
} // namespace metrics