// 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 "content/browser/media/audible_metrics.h" #include "base/metrics/histogram_samples.h" #include "base/test/histogram_tester.h" #include "base/test/simple_test_tick_clock.h" #include "base/test/user_action_tester.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { namespace { static const WebContents* WEB_CONTENTS_0 = reinterpret_cast(0x00); static const WebContents* WEB_CONTENTS_1 = reinterpret_cast(0x01); static const WebContents* WEB_CONTENTS_2 = reinterpret_cast(0x10); static const WebContents* WEB_CONTENTS_3 = reinterpret_cast(0x11); static const char* CONCURRENT_TAB_WHEN_STARTING_HISTOGRAM = "Media.Audible.ConcurrentTabsWhenStarting"; static const char* MAX_CONCURRENT_TAB_IN_SESSION_HISTOGRAM = "Media.Audible.MaxConcurrentTabsInSession"; static const char* CONCURRENT_TABS_TIME_HISTOGRAM = "Media.Audible.ConcurrentTabsTime"; static const char* ADD_TAB_USER_ACTION = "Media.Audible.AddTab"; static const char* REMOVE_TAB_USER_ACTION = "Media.Audible.RemoveTab"; class AudibleMetricsTest : public testing::Test { public: AudibleMetricsTest() = default; void SetUp() override { clock_ = new base::SimpleTestTickClock(); // Set the clock to a value different than 0 so the time it gives is // recognized as initialized. clock_->Advance(base::TimeDelta::FromMilliseconds(1)); audible_metrics_.SetClockForTest( scoped_ptr(clock_)); } void TearDown() override { clock_ = nullptr; } base::SimpleTestTickClock* clock() { return clock_; } AudibleMetrics* audible_metrics() { return &audible_metrics_; }; const base::UserActionTester& user_action_tester() const { return user_action_tester_; } scoped_ptr GetHistogramSamplesSinceTestStart( const std::string& name) { return histogram_tester_.GetHistogramSamplesSinceCreation(name); } private: base::SimpleTestTickClock* clock_ = nullptr; AudibleMetrics audible_metrics_; base::HistogramTester histogram_tester_; base::UserActionTester user_action_tester_; DISALLOW_COPY_AND_ASSIGN(AudibleMetricsTest); }; } // anonymous namespace TEST_F(AudibleMetricsTest, CreateAndKillDoesNothing) { { scoped_ptr audible_metrics(new AudibleMetrics()); } { scoped_ptr samples( GetHistogramSamplesSinceTestStart( CONCURRENT_TAB_WHEN_STARTING_HISTOGRAM)); EXPECT_EQ(0, samples->TotalCount()); } { scoped_ptr samples( GetHistogramSamplesSinceTestStart( MAX_CONCURRENT_TAB_IN_SESSION_HISTOGRAM)); EXPECT_EQ(0, samples->TotalCount()); } { scoped_ptr samples( GetHistogramSamplesSinceTestStart(CONCURRENT_TABS_TIME_HISTOGRAM)); EXPECT_EQ(0, samples->TotalCount()); } EXPECT_EQ(0, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(0, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); } TEST_F(AudibleMetricsTest, AudibleStart) { audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); { scoped_ptr samples( GetHistogramSamplesSinceTestStart( CONCURRENT_TAB_WHEN_STARTING_HISTOGRAM)); EXPECT_EQ(1, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(0)); } { scoped_ptr samples( GetHistogramSamplesSinceTestStart( MAX_CONCURRENT_TAB_IN_SESSION_HISTOGRAM)); EXPECT_EQ(1, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(1)); } { scoped_ptr samples( GetHistogramSamplesSinceTestStart(CONCURRENT_TABS_TIME_HISTOGRAM)); EXPECT_EQ(0, samples->TotalCount()); } EXPECT_EQ(1, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(0, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); } TEST_F(AudibleMetricsTest, AudibleStartAndStop) { audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, false); { scoped_ptr samples( GetHistogramSamplesSinceTestStart( CONCURRENT_TAB_WHEN_STARTING_HISTOGRAM)); EXPECT_EQ(1, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(0)); } { scoped_ptr samples( GetHistogramSamplesSinceTestStart( MAX_CONCURRENT_TAB_IN_SESSION_HISTOGRAM)); EXPECT_EQ(1, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(1)); } { scoped_ptr samples( GetHistogramSamplesSinceTestStart(CONCURRENT_TABS_TIME_HISTOGRAM)); EXPECT_EQ(0, samples->TotalCount()); } EXPECT_EQ(1, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(1, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); } TEST_F(AudibleMetricsTest, AddSameTabIsNoOp) { audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); { scoped_ptr samples( GetHistogramSamplesSinceTestStart( CONCURRENT_TAB_WHEN_STARTING_HISTOGRAM)); EXPECT_EQ(1, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(0)); } { scoped_ptr samples( GetHistogramSamplesSinceTestStart( MAX_CONCURRENT_TAB_IN_SESSION_HISTOGRAM)); EXPECT_EQ(1, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(1)); } { scoped_ptr samples( GetHistogramSamplesSinceTestStart(CONCURRENT_TABS_TIME_HISTOGRAM)); EXPECT_EQ(0, samples->TotalCount()); } EXPECT_EQ(1, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(0, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); } TEST_F(AudibleMetricsTest, RemoveUnknownTabIsNoOp) { audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, false); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, false); EXPECT_EQ(0, GetHistogramSamplesSinceTestStart( CONCURRENT_TAB_WHEN_STARTING_HISTOGRAM)->TotalCount()); EXPECT_EQ(0, GetHistogramSamplesSinceTestStart( MAX_CONCURRENT_TAB_IN_SESSION_HISTOGRAM)->TotalCount()); EXPECT_EQ(0, GetHistogramSamplesSinceTestStart( CONCURRENT_TABS_TIME_HISTOGRAM)->TotalCount()); EXPECT_EQ(0, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(0, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); } TEST_F(AudibleMetricsTest, ConcurrentTabsInSessionIsIncremental) { audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_2, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_3, true); scoped_ptr samples(GetHistogramSamplesSinceTestStart( MAX_CONCURRENT_TAB_IN_SESSION_HISTOGRAM)); EXPECT_EQ(4, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(1)); EXPECT_EQ(1, samples->GetCount(2)); EXPECT_EQ(1, samples->GetCount(3)); EXPECT_EQ(1, samples->GetCount(4)); EXPECT_EQ(4, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(0, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); } TEST_F(AudibleMetricsTest, ConcurrentTabsInSessionKeepTrackOfRemovedTabs) { audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, false); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_2, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, false); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_2, false); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_3, true); scoped_ptr samples(GetHistogramSamplesSinceTestStart( MAX_CONCURRENT_TAB_IN_SESSION_HISTOGRAM)); EXPECT_EQ(2, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(1)); EXPECT_EQ(1, samples->GetCount(2)); EXPECT_EQ(4, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(3, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); } TEST_F(AudibleMetricsTest, ConcurrentTabsInSessionIsNotCountedTwice) { audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_2, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_3, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, false); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, false); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_2, false); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_3, false); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_2, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_3, true); scoped_ptr samples(GetHistogramSamplesSinceTestStart( MAX_CONCURRENT_TAB_IN_SESSION_HISTOGRAM)); EXPECT_EQ(4, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(1)); EXPECT_EQ(1, samples->GetCount(2)); EXPECT_EQ(1, samples->GetCount(3)); EXPECT_EQ(1, samples->GetCount(4)); EXPECT_EQ(8, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(4, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); } TEST_F(AudibleMetricsTest, ConcurrentTabsWhenStartingAddedPerTab) { audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, true); { scoped_ptr samples( GetHistogramSamplesSinceTestStart( CONCURRENT_TAB_WHEN_STARTING_HISTOGRAM)); EXPECT_EQ(2, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(0)); EXPECT_EQ(1, samples->GetCount(1)); } EXPECT_EQ(2, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(0, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); // Added again: ignored. audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, true); { scoped_ptr samples( GetHistogramSamplesSinceTestStart( CONCURRENT_TAB_WHEN_STARTING_HISTOGRAM)); EXPECT_EQ(2, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(0)); EXPECT_EQ(1, samples->GetCount(1)); } EXPECT_EQ(2, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(0, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); // Removing both. audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, false); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, false); { scoped_ptr samples( GetHistogramSamplesSinceTestStart( CONCURRENT_TAB_WHEN_STARTING_HISTOGRAM)); EXPECT_EQ(2, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(0)); EXPECT_EQ(1, samples->GetCount(1)); } EXPECT_EQ(2, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(2, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); // Adding them after removed, it is counted. audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, true); { scoped_ptr samples( GetHistogramSamplesSinceTestStart( CONCURRENT_TAB_WHEN_STARTING_HISTOGRAM)); EXPECT_EQ(4, samples->TotalCount()); EXPECT_EQ(2, samples->GetCount(0)); EXPECT_EQ(2, samples->GetCount(1)); } EXPECT_EQ(4, user_action_tester().GetActionCount(ADD_TAB_USER_ACTION)); EXPECT_EQ(2, user_action_tester().GetActionCount(REMOVE_TAB_USER_ACTION)); } TEST_F(AudibleMetricsTest, ConcurrentTabsTimeRequiresTwoAudibleTabs) { audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, true); clock()->Advance(base::TimeDelta::FromMilliseconds(1000)); // No record because concurrent audible tabs still running. EXPECT_EQ(0, GetHistogramSamplesSinceTestStart( CONCURRENT_TABS_TIME_HISTOGRAM)->TotalCount()); // No longer concurrent. audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, false); { scoped_ptr samples( GetHistogramSamplesSinceTestStart(CONCURRENT_TABS_TIME_HISTOGRAM)); EXPECT_EQ(1, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(1000)); } // Stopping the second tab is a no-op. audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, false); { scoped_ptr samples( GetHistogramSamplesSinceTestStart(CONCURRENT_TABS_TIME_HISTOGRAM)); EXPECT_EQ(1, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(1000)); } } TEST_F(AudibleMetricsTest, ConcurrentTabsTimeRunsAsLongAsTwoAudibleTabs) { audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, true); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, true); clock()->Advance(base::TimeDelta::FromMilliseconds(1000)); audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_2, true); clock()->Advance(base::TimeDelta::FromMilliseconds(500)); // Mutes one of the three audible tabs. audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_1, false); // No record because concurrent audible tabs still running. EXPECT_EQ(0, GetHistogramSamplesSinceTestStart( CONCURRENT_TABS_TIME_HISTOGRAM)->TotalCount()); // Mutes the first audible tab. audible_metrics()->UpdateAudibleWebContentsState(WEB_CONTENTS_0, false); { scoped_ptr samples( GetHistogramSamplesSinceTestStart(CONCURRENT_TABS_TIME_HISTOGRAM)); EXPECT_EQ(1, samples->TotalCount()); EXPECT_EQ(1, samples->GetCount(1500)); } } } // namespace content