| Index: content/browser/media/audio_stream_monitor_unittest.cc
|
| diff --git a/content/browser/media/audio_stream_monitor_unittest.cc b/content/browser/media/audio_stream_monitor_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b8871af65636e896ebe4b84eba3787ca67961b6a
|
| --- /dev/null
|
| +++ b/content/browser/media/audio_stream_monitor_unittest.cc
|
| @@ -0,0 +1,297 @@
|
| +// Copyright 2014 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/audio_stream_monitor.h"
|
| +
|
| +#include <map>
|
| +#include <utility>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/bind_helpers.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/test/simple_test_tick_clock.h"
|
| +#include "content/browser/web_contents/web_contents_impl.h"
|
| +#include "content/public/browser/invalidate_type.h"
|
| +#include "content/public/browser/web_contents_delegate.h"
|
| +#include "content/public/test/test_renderer_host.h"
|
| +#include "media/audio/audio_power_monitor.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +using ::testing::InvokeWithoutArgs;
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +const int kRenderProcessId = 1;
|
| +const int kAnotherRenderProcessId = 2;
|
| +const int kStreamId = 3;
|
| +const int kAnotherStreamId = 6;
|
| +
|
| +// Used to confirm audio indicator state changes occur at the correct times.
|
| +class MockWebContentsDelegate : public WebContentsDelegate {
|
| + public:
|
| + MOCK_METHOD2(NavigationStateChanged,
|
| + void(const WebContents* source, InvalidateTypes changed_flags));
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +class AudioStreamMonitorTest : public RenderViewHostTestHarness {
|
| + public:
|
| + AudioStreamMonitorTest() {
|
| + // Start |clock_| at non-zero.
|
| + clock_.Advance(base::TimeDelta::FromSeconds(1000000));
|
| + }
|
| +
|
| + virtual void SetUp() OVERRIDE {
|
| + RenderViewHostTestHarness::SetUp();
|
| +
|
| + WebContentsImpl* web_contents = reinterpret_cast<WebContentsImpl*>(
|
| + RenderViewHostTestHarness::web_contents());
|
| + web_contents->SetDelegate(&mock_web_contents_delegate_);
|
| + monitor_ = web_contents->audio_stream_monitor();
|
| + const_cast<base::TickClock*&>(monitor_->clock_) = &clock_;
|
| + }
|
| +
|
| + base::TimeTicks GetTestClockTime() { return clock_.NowTicks(); }
|
| +
|
| + void AdvanceClock(const base::TimeDelta& delta) { clock_.Advance(delta); }
|
| +
|
| + AudioStreamMonitor::ReadPowerAndClipCallback CreatePollCallback(
|
| + int stream_id) {
|
| + return base::Bind(
|
| + &AudioStreamMonitorTest::ReadPower, base::Unretained(this), stream_id);
|
| + }
|
| +
|
| + void SetStreamPower(int stream_id, float power) {
|
| + current_power_[stream_id] = power;
|
| + }
|
| +
|
| + void SimulatePollTimerFired() { monitor_->Poll(); }
|
| +
|
| + void SimulateOffTimerFired() { monitor_->MaybeToggle(); }
|
| +
|
| + void ExpectIsPolling(int render_process_id, int stream_id, bool is_polling) {
|
| + const AudioStreamMonitor::StreamID key(render_process_id, stream_id);
|
| + EXPECT_EQ(
|
| + is_polling,
|
| + monitor_->poll_callbacks_.find(key) != monitor_->poll_callbacks_.end());
|
| + EXPECT_EQ(!monitor_->poll_callbacks_.empty(),
|
| + monitor_->poll_timer_.IsRunning());
|
| + }
|
| +
|
| + void ExpectTabWasRecentlyAudible(bool was_audible,
|
| + const base::TimeTicks& last_blurt_time) {
|
| + EXPECT_EQ(was_audible, monitor_->was_recently_audible_);
|
| + EXPECT_EQ(last_blurt_time, monitor_->last_blurt_time_);
|
| + EXPECT_EQ(monitor_->was_recently_audible_,
|
| + monitor_->off_timer_.IsRunning());
|
| + }
|
| +
|
| + void ExpectWebContentsWillBeNotifiedOnce(bool should_be_audible) {
|
| + EXPECT_CALL(
|
| + mock_web_contents_delegate_,
|
| + NavigationStateChanged(RenderViewHostTestHarness::web_contents(),
|
| + INVALIDATE_TYPE_TAB))
|
| + .WillOnce(InvokeWithoutArgs(
|
| + this,
|
| + should_be_audible
|
| + ? &AudioStreamMonitorTest::ExpectIsNotifyingForToggleOn
|
| + : &AudioStreamMonitorTest::ExpectIsNotifyingForToggleOff))
|
| + .RetiresOnSaturation();
|
| + }
|
| +
|
| + static base::TimeDelta one_polling_interval() {
|
| + return base::TimeDelta::FromSeconds(1) /
|
| + AudioStreamMonitor::kPowerMeasurementsPerSecond;
|
| + }
|
| +
|
| + static base::TimeDelta holding_period() {
|
| + return base::TimeDelta::FromMilliseconds(
|
| + AudioStreamMonitor::kHoldOnMilliseconds);
|
| + }
|
| +
|
| + void StartMonitoring(
|
| + int render_process_id,
|
| + int stream_id,
|
| + const AudioStreamMonitor::ReadPowerAndClipCallback& callback) {
|
| + monitor_->StartMonitoringStreamOnUIThread(
|
| + render_process_id, stream_id, callback);
|
| + }
|
| +
|
| + void StopMonitoring(int render_process_id, int stream_id) {
|
| + monitor_->StopMonitoringStreamOnUIThread(render_process_id, stream_id);
|
| + }
|
| +
|
| + protected:
|
| + AudioStreamMonitor* monitor_;
|
| +
|
| + private:
|
| + std::pair<float, bool> ReadPower(int stream_id) {
|
| + return std::make_pair(current_power_[stream_id], false);
|
| + }
|
| +
|
| + void ExpectIsNotifyingForToggleOn() {
|
| + EXPECT_TRUE(monitor_->WasRecentlyAudible());
|
| + }
|
| +
|
| + void ExpectIsNotifyingForToggleOff() {
|
| + EXPECT_FALSE(monitor_->WasRecentlyAudible());
|
| + }
|
| +
|
| + MockWebContentsDelegate mock_web_contents_delegate_;
|
| + base::SimpleTestTickClock clock_;
|
| + std::map<int, float> current_power_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(AudioStreamMonitorTest);
|
| +};
|
| +
|
| +// Tests that AudioStreamMonitor is polling while it has a
|
| +// ReadPowerAndClipCallback, and is not polling at other times.
|
| +TEST_F(AudioStreamMonitorTest, PollsWhenProvidedACallback) {
|
| + EXPECT_FALSE(monitor_->WasRecentlyAudible());
|
| + ExpectIsPolling(kRenderProcessId, kStreamId, false);
|
| +
|
| + StartMonitoring(kRenderProcessId, kStreamId, CreatePollCallback(kStreamId));
|
| + EXPECT_FALSE(monitor_->WasRecentlyAudible());
|
| + ExpectIsPolling(kRenderProcessId, kStreamId, true);
|
| +
|
| + StopMonitoring(kRenderProcessId, kStreamId);
|
| + EXPECT_FALSE(monitor_->WasRecentlyAudible());
|
| + ExpectIsPolling(kRenderProcessId, kStreamId, false);
|
| +}
|
| +
|
| +// Tests that AudioStreamMonitor debounces the power level readings it's taking,
|
| +// which could be fluctuating rapidly between the audible versus silence
|
| +// threshold. See comments in audio_stream_monitor.h for expected behavior.
|
| +TEST_F(AudioStreamMonitorTest,
|
| + ImpulsesKeepIndicatorOnUntilHoldingPeriodHasPassed) {
|
| + StartMonitoring(kRenderProcessId, kStreamId, CreatePollCallback(kStreamId));
|
| +
|
| + // Expect WebContents will get one call form AudioStreamMonitor to toggle the
|
| + // indicator on upon the very first poll.
|
| + ExpectWebContentsWillBeNotifiedOnce(true);
|
| +
|
| + // Loop, each time testing a slightly longer period of polled silence. The
|
| + // indicator should remain on throughout.
|
| + int num_silence_polls = 0;
|
| + base::TimeTicks last_blurt_time;
|
| + do {
|
| + // Poll an audible signal, and expect tab indicator state is on.
|
| + SetStreamPower(kStreamId, media::AudioPowerMonitor::max_power());
|
| + last_blurt_time = GetTestClockTime();
|
| + SimulatePollTimerFired();
|
| + ExpectTabWasRecentlyAudible(true, last_blurt_time);
|
| + AdvanceClock(one_polling_interval());
|
| +
|
| + // Poll a silent signal repeatedly, ensuring that the indicator is being
|
| + // held on during the holding period.
|
| + SetStreamPower(kStreamId, media::AudioPowerMonitor::zero_power());
|
| + for (int i = 0; i < num_silence_polls; ++i) {
|
| + SimulatePollTimerFired();
|
| + ExpectTabWasRecentlyAudible(true, last_blurt_time);
|
| + // Note: Redundant off timer firings should not have any effect.
|
| + SimulateOffTimerFired();
|
| + ExpectTabWasRecentlyAudible(true, last_blurt_time);
|
| + AdvanceClock(one_polling_interval());
|
| + }
|
| +
|
| + ++num_silence_polls;
|
| + } while (GetTestClockTime() < last_blurt_time + holding_period());
|
| +
|
| + // At this point, the clock has just advanced to beyond the holding period, so
|
| + // the next firing of the off timer should turn off the tab indicator. Also,
|
| + // make sure it stays off for several cycles thereafter.
|
| + ExpectWebContentsWillBeNotifiedOnce(false);
|
| + for (int i = 0; i < 10; ++i) {
|
| + SimulateOffTimerFired();
|
| + ExpectTabWasRecentlyAudible(false, last_blurt_time);
|
| + AdvanceClock(one_polling_interval());
|
| + }
|
| +}
|
| +
|
| +// Tests that the AudioStreamMonitor correctly processes the blurts from two
|
| +// different streams in the same tab.
|
| +TEST_F(AudioStreamMonitorTest, HandlesMultipleStreamsBlurting) {
|
| + StartMonitoring(kRenderProcessId, kStreamId, CreatePollCallback(kStreamId));
|
| + StartMonitoring(
|
| + kRenderProcessId, kAnotherStreamId, CreatePollCallback(kAnotherStreamId));
|
| +
|
| + base::TimeTicks last_blurt_time;
|
| + ExpectTabWasRecentlyAudible(false, last_blurt_time);
|
| +
|
| + // Measure audible sound from the first stream and silence from the second.
|
| + // The indicator turns on (i.e., tab was recently audible).
|
| + ExpectWebContentsWillBeNotifiedOnce(true);
|
| + SetStreamPower(kStreamId, media::AudioPowerMonitor::max_power());
|
| + SetStreamPower(kAnotherStreamId, media::AudioPowerMonitor::zero_power());
|
| + last_blurt_time = GetTestClockTime();
|
| + SimulatePollTimerFired();
|
| + ExpectTabWasRecentlyAudible(true, last_blurt_time);
|
| +
|
| + // Halfway through the holding period, the second stream joins in. The
|
| + // indicator stays on.
|
| + AdvanceClock(holding_period() / 2);
|
| + SimulateOffTimerFired();
|
| + SetStreamPower(kAnotherStreamId, media::AudioPowerMonitor::max_power());
|
| + last_blurt_time = GetTestClockTime();
|
| + SimulatePollTimerFired(); // Restarts holding period.
|
| + ExpectTabWasRecentlyAudible(true, last_blurt_time);
|
| +
|
| + // Now, measure silence from both streams. After an entire holding period
|
| + // has passed (since the second stream joined in), the indicator should turn
|
| + // off.
|
| + ExpectWebContentsWillBeNotifiedOnce(false);
|
| + AdvanceClock(holding_period());
|
| + SimulateOffTimerFired();
|
| + SetStreamPower(kStreamId, media::AudioPowerMonitor::zero_power());
|
| + SetStreamPower(kAnotherStreamId, media::AudioPowerMonitor::zero_power());
|
| + SimulatePollTimerFired();
|
| + ExpectTabWasRecentlyAudible(false, last_blurt_time);
|
| +
|
| + // Now, measure silence from the first stream and audible sound from the
|
| + // second. The indicator turns back on.
|
| + ExpectWebContentsWillBeNotifiedOnce(true);
|
| + SetStreamPower(kAnotherStreamId, media::AudioPowerMonitor::max_power());
|
| + last_blurt_time = GetTestClockTime();
|
| + SimulatePollTimerFired();
|
| + ExpectTabWasRecentlyAudible(true, last_blurt_time);
|
| +
|
| + // From here onwards, both streams are silent. Halfway through the holding
|
| + // period, the indicator should not have changed.
|
| + SetStreamPower(kAnotherStreamId, media::AudioPowerMonitor::zero_power());
|
| + AdvanceClock(holding_period() / 2);
|
| + SimulatePollTimerFired();
|
| + SimulateOffTimerFired();
|
| + ExpectTabWasRecentlyAudible(true, last_blurt_time);
|
| +
|
| + // Just past the holding period, the indicator should be turned off.
|
| + ExpectWebContentsWillBeNotifiedOnce(false);
|
| + AdvanceClock(holding_period() - (GetTestClockTime() - last_blurt_time));
|
| + SimulateOffTimerFired();
|
| + ExpectTabWasRecentlyAudible(false, last_blurt_time);
|
| +
|
| + // Polling should not turn the indicator back while both streams are remaining
|
| + // silent.
|
| + for (int i = 0; i < 100; ++i) {
|
| + AdvanceClock(one_polling_interval());
|
| + SimulatePollTimerFired();
|
| + ExpectTabWasRecentlyAudible(false, last_blurt_time);
|
| + }
|
| +}
|
| +
|
| +TEST_F(AudioStreamMonitorTest, MultipleRendererProcesses) {
|
| + StartMonitoring(kRenderProcessId, kStreamId, CreatePollCallback(kStreamId));
|
| + StartMonitoring(
|
| + kAnotherRenderProcessId, kStreamId, CreatePollCallback(kStreamId));
|
| + ExpectIsPolling(kRenderProcessId, kStreamId, true);
|
| + ExpectIsPolling(kAnotherRenderProcessId, kStreamId, true);
|
| + StopMonitoring(kAnotherRenderProcessId, kStreamId);
|
| + ExpectIsPolling(kRenderProcessId, kStreamId, true);
|
| + ExpectIsPolling(kAnotherRenderProcessId, kStreamId, false);
|
| +}
|
| +
|
| +} // namespace content
|
|
|