1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/media/audio_stream_monitor.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/test/simple_test_tick_clock.h"
14 #include "content/browser/web_contents/web_contents_impl.h"
15 #include "content/public/browser/invalidate_type.h"
16 #include "content/public/browser/web_contents_delegate.h"
17 #include "content/public/test/test_renderer_host.h"
18 #include "media/audio/audio_power_monitor.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 using ::testing::InvokeWithoutArgs
;
28 const int kRenderProcessId
= 1;
29 const int kAnotherRenderProcessId
= 2;
30 const int kStreamId
= 3;
31 const int kAnotherStreamId
= 6;
33 // Used to confirm audio indicator state changes occur at the correct times.
34 class MockWebContentsDelegate
: public WebContentsDelegate
{
36 MOCK_METHOD2(NavigationStateChanged
,
37 void(WebContents
* source
, InvalidateTypes changed_flags
));
42 class AudioStreamMonitorTest
: public RenderViewHostTestHarness
{
44 AudioStreamMonitorTest() {
45 // Start |clock_| at non-zero.
46 clock_
.Advance(base::TimeDelta::FromSeconds(1000000));
49 void SetUp() override
{
50 RenderViewHostTestHarness::SetUp();
52 WebContentsImpl
* web_contents
= reinterpret_cast<WebContentsImpl
*>(
53 RenderViewHostTestHarness::web_contents());
54 web_contents
->SetDelegate(&mock_web_contents_delegate_
);
56 monitor_
= web_contents
->audio_stream_monitor();
57 const_cast<base::TickClock
*&>(monitor_
->clock_
) = &clock_
;
60 base::TimeTicks
GetTestClockTime() { return clock_
.NowTicks(); }
62 void AdvanceClock(const base::TimeDelta
& delta
) { clock_
.Advance(delta
); }
64 AudioStreamMonitor::ReadPowerAndClipCallback
CreatePollCallback(
67 &AudioStreamMonitorTest::ReadPower
, base::Unretained(this), stream_id
);
70 void SetStreamPower(int stream_id
, float power
) {
71 current_power_
[stream_id
] = power
;
74 void SimulatePollTimerFired() { monitor_
->Poll(); }
76 void SimulateOffTimerFired() { monitor_
->MaybeToggle(); }
78 void ExpectIsPolling(int render_process_id
, int stream_id
, bool is_polling
) {
79 const AudioStreamMonitor::StreamID
key(render_process_id
, stream_id
);
82 monitor_
->poll_callbacks_
.find(key
) != monitor_
->poll_callbacks_
.end());
83 EXPECT_EQ(!monitor_
->poll_callbacks_
.empty(),
84 monitor_
->poll_timer_
.IsRunning());
87 void ExpectTabWasRecentlyAudible(bool was_audible
,
88 const base::TimeTicks
& last_blurt_time
) {
89 EXPECT_EQ(was_audible
, monitor_
->was_recently_audible_
);
90 EXPECT_EQ(last_blurt_time
, monitor_
->last_blurt_time_
);
91 EXPECT_EQ(monitor_
->was_recently_audible_
,
92 monitor_
->off_timer_
.IsRunning());
95 void ExpectWebContentsWillBeNotifiedOnce(bool should_be_audible
) {
97 mock_web_contents_delegate_
,
98 NavigationStateChanged(RenderViewHostTestHarness::web_contents(),
100 .WillOnce(InvokeWithoutArgs(
103 ? &AudioStreamMonitorTest::ExpectIsNotifyingForToggleOn
104 : &AudioStreamMonitorTest::ExpectIsNotifyingForToggleOff
))
105 .RetiresOnSaturation();
108 static base::TimeDelta
one_polling_interval() {
109 return base::TimeDelta::FromSeconds(1) /
110 static_cast<int>(AudioStreamMonitor::kPowerMeasurementsPerSecond
);
113 static base::TimeDelta
holding_period() {
114 return base::TimeDelta::FromMilliseconds(
115 AudioStreamMonitor::kHoldOnMilliseconds
);
118 void StartMonitoring(
119 int render_process_id
,
121 const AudioStreamMonitor::ReadPowerAndClipCallback
& callback
) {
122 monitor_
->StartMonitoringStreamOnUIThread(
123 render_process_id
, stream_id
, callback
);
126 void StopMonitoring(int render_process_id
, int stream_id
) {
127 monitor_
->StopMonitoringStreamOnUIThread(render_process_id
, stream_id
);
131 AudioStreamMonitor
* monitor_
;
134 std::pair
<float, bool> ReadPower(int stream_id
) {
135 return std::make_pair(current_power_
[stream_id
], false);
138 void ExpectIsNotifyingForToggleOn() {
139 EXPECT_TRUE(monitor_
->WasRecentlyAudible());
142 void ExpectIsNotifyingForToggleOff() {
143 EXPECT_FALSE(monitor_
->WasRecentlyAudible());
146 MockWebContentsDelegate mock_web_contents_delegate_
;
147 base::SimpleTestTickClock clock_
;
148 std::map
<int, float> current_power_
;
150 DISALLOW_COPY_AND_ASSIGN(AudioStreamMonitorTest
);
153 // Tests that AudioStreamMonitor is polling while it has a
154 // ReadPowerAndClipCallback, and is not polling at other times.
155 TEST_F(AudioStreamMonitorTest
, PollsWhenProvidedACallback
) {
156 EXPECT_FALSE(monitor_
->WasRecentlyAudible());
157 ExpectIsPolling(kRenderProcessId
, kStreamId
, false);
159 StartMonitoring(kRenderProcessId
, kStreamId
, CreatePollCallback(kStreamId
));
160 EXPECT_FALSE(monitor_
->WasRecentlyAudible());
161 ExpectIsPolling(kRenderProcessId
, kStreamId
, true);
163 StopMonitoring(kRenderProcessId
, kStreamId
);
164 EXPECT_FALSE(monitor_
->WasRecentlyAudible());
165 ExpectIsPolling(kRenderProcessId
, kStreamId
, false);
168 // Tests that AudioStreamMonitor debounces the power level readings it's taking,
169 // which could be fluctuating rapidly between the audible versus silence
170 // threshold. See comments in audio_stream_monitor.h for expected behavior.
171 TEST_F(AudioStreamMonitorTest
,
172 ImpulsesKeepIndicatorOnUntilHoldingPeriodHasPassed
) {
173 StartMonitoring(kRenderProcessId
, kStreamId
, CreatePollCallback(kStreamId
));
175 // Expect WebContents will get one call form AudioStreamMonitor to toggle the
176 // indicator on upon the very first poll.
177 ExpectWebContentsWillBeNotifiedOnce(true);
179 // Loop, each time testing a slightly longer period of polled silence. The
180 // indicator should remain on throughout.
181 int num_silence_polls
= 0;
182 base::TimeTicks last_blurt_time
;
184 // Poll an audible signal, and expect tab indicator state is on.
185 SetStreamPower(kStreamId
, media::AudioPowerMonitor::max_power());
186 last_blurt_time
= GetTestClockTime();
187 SimulatePollTimerFired();
188 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
189 AdvanceClock(one_polling_interval());
191 // Poll a silent signal repeatedly, ensuring that the indicator is being
192 // held on during the holding period.
193 SetStreamPower(kStreamId
, media::AudioPowerMonitor::zero_power());
194 for (int i
= 0; i
< num_silence_polls
; ++i
) {
195 SimulatePollTimerFired();
196 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
197 // Note: Redundant off timer firings should not have any effect.
198 SimulateOffTimerFired();
199 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
200 AdvanceClock(one_polling_interval());
204 } while (GetTestClockTime() < last_blurt_time
+ holding_period());
206 // At this point, the clock has just advanced to beyond the holding period, so
207 // the next firing of the off timer should turn off the tab indicator. Also,
208 // make sure it stays off for several cycles thereafter.
209 ExpectWebContentsWillBeNotifiedOnce(false);
210 for (int i
= 0; i
< 10; ++i
) {
211 SimulateOffTimerFired();
212 ExpectTabWasRecentlyAudible(false, last_blurt_time
);
213 AdvanceClock(one_polling_interval());
217 // Tests that the AudioStreamMonitor correctly processes the blurts from two
218 // different streams in the same tab.
219 TEST_F(AudioStreamMonitorTest
, HandlesMultipleStreamsBlurting
) {
220 StartMonitoring(kRenderProcessId
, kStreamId
, CreatePollCallback(kStreamId
));
222 kRenderProcessId
, kAnotherStreamId
, CreatePollCallback(kAnotherStreamId
));
224 base::TimeTicks last_blurt_time
;
225 ExpectTabWasRecentlyAudible(false, last_blurt_time
);
227 // Measure audible sound from the first stream and silence from the second.
228 // The indicator turns on (i.e., tab was recently audible).
229 ExpectWebContentsWillBeNotifiedOnce(true);
230 SetStreamPower(kStreamId
, media::AudioPowerMonitor::max_power());
231 SetStreamPower(kAnotherStreamId
, media::AudioPowerMonitor::zero_power());
232 last_blurt_time
= GetTestClockTime();
233 SimulatePollTimerFired();
234 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
236 // Halfway through the holding period, the second stream joins in. The
237 // indicator stays on.
238 AdvanceClock(holding_period() / 2);
239 SimulateOffTimerFired();
240 SetStreamPower(kAnotherStreamId
, media::AudioPowerMonitor::max_power());
241 last_blurt_time
= GetTestClockTime();
242 SimulatePollTimerFired(); // Restarts holding period.
243 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
245 // Now, measure silence from both streams. After an entire holding period
246 // has passed (since the second stream joined in), the indicator should turn
248 ExpectWebContentsWillBeNotifiedOnce(false);
249 AdvanceClock(holding_period());
250 SimulateOffTimerFired();
251 SetStreamPower(kStreamId
, media::AudioPowerMonitor::zero_power());
252 SetStreamPower(kAnotherStreamId
, media::AudioPowerMonitor::zero_power());
253 SimulatePollTimerFired();
254 ExpectTabWasRecentlyAudible(false, last_blurt_time
);
256 // Now, measure silence from the first stream and audible sound from the
257 // second. The indicator turns back on.
258 ExpectWebContentsWillBeNotifiedOnce(true);
259 SetStreamPower(kAnotherStreamId
, media::AudioPowerMonitor::max_power());
260 last_blurt_time
= GetTestClockTime();
261 SimulatePollTimerFired();
262 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
264 // From here onwards, both streams are silent. Halfway through the holding
265 // period, the indicator should not have changed.
266 SetStreamPower(kAnotherStreamId
, media::AudioPowerMonitor::zero_power());
267 AdvanceClock(holding_period() / 2);
268 SimulatePollTimerFired();
269 SimulateOffTimerFired();
270 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
272 // Just past the holding period, the indicator should be turned off.
273 ExpectWebContentsWillBeNotifiedOnce(false);
274 AdvanceClock(holding_period() - (GetTestClockTime() - last_blurt_time
));
275 SimulateOffTimerFired();
276 ExpectTabWasRecentlyAudible(false, last_blurt_time
);
278 // Polling should not turn the indicator back while both streams are remaining
280 for (int i
= 0; i
< 100; ++i
) {
281 AdvanceClock(one_polling_interval());
282 SimulatePollTimerFired();
283 ExpectTabWasRecentlyAudible(false, last_blurt_time
);
287 TEST_F(AudioStreamMonitorTest
, MultipleRendererProcesses
) {
288 StartMonitoring(kRenderProcessId
, kStreamId
, CreatePollCallback(kStreamId
));
290 kAnotherRenderProcessId
, kStreamId
, CreatePollCallback(kStreamId
));
291 ExpectIsPolling(kRenderProcessId
, kStreamId
, true);
292 ExpectIsPolling(kAnotherRenderProcessId
, kStreamId
, true);
293 StopMonitoring(kAnotherRenderProcessId
, kStreamId
);
294 ExpectIsPolling(kRenderProcessId
, kStreamId
, true);
295 ExpectIsPolling(kAnotherRenderProcessId
, kStreamId
, false);
298 } // namespace content