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(const 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_
);
55 monitor_
= web_contents
->audio_stream_monitor();
56 const_cast<base::TickClock
*&>(monitor_
->clock_
) = &clock_
;
59 base::TimeTicks
GetTestClockTime() { return clock_
.NowTicks(); }
61 void AdvanceClock(const base::TimeDelta
& delta
) { clock_
.Advance(delta
); }
63 AudioStreamMonitor::ReadPowerAndClipCallback
CreatePollCallback(
66 &AudioStreamMonitorTest::ReadPower
, base::Unretained(this), stream_id
);
69 void SetStreamPower(int stream_id
, float power
) {
70 current_power_
[stream_id
] = power
;
73 void SimulatePollTimerFired() { monitor_
->Poll(); }
75 void SimulateOffTimerFired() { monitor_
->MaybeToggle(); }
77 void ExpectIsPolling(int render_process_id
, int stream_id
, bool is_polling
) {
78 const AudioStreamMonitor::StreamID
key(render_process_id
, stream_id
);
81 monitor_
->poll_callbacks_
.find(key
) != monitor_
->poll_callbacks_
.end());
82 EXPECT_EQ(!monitor_
->poll_callbacks_
.empty(),
83 monitor_
->poll_timer_
.IsRunning());
86 void ExpectTabWasRecentlyAudible(bool was_audible
,
87 const base::TimeTicks
& last_blurt_time
) {
88 EXPECT_EQ(was_audible
, monitor_
->was_recently_audible_
);
89 EXPECT_EQ(last_blurt_time
, monitor_
->last_blurt_time_
);
90 EXPECT_EQ(monitor_
->was_recently_audible_
,
91 monitor_
->off_timer_
.IsRunning());
94 void ExpectWebContentsWillBeNotifiedOnce(bool should_be_audible
) {
96 mock_web_contents_delegate_
,
97 NavigationStateChanged(RenderViewHostTestHarness::web_contents(),
99 .WillOnce(InvokeWithoutArgs(
102 ? &AudioStreamMonitorTest::ExpectIsNotifyingForToggleOn
103 : &AudioStreamMonitorTest::ExpectIsNotifyingForToggleOff
))
104 .RetiresOnSaturation();
107 static base::TimeDelta
one_polling_interval() {
108 return base::TimeDelta::FromSeconds(1) /
109 AudioStreamMonitor::kPowerMeasurementsPerSecond
;
112 static base::TimeDelta
holding_period() {
113 return base::TimeDelta::FromMilliseconds(
114 AudioStreamMonitor::kHoldOnMilliseconds
);
117 void StartMonitoring(
118 int render_process_id
,
120 const AudioStreamMonitor::ReadPowerAndClipCallback
& callback
) {
121 monitor_
->StartMonitoringStreamOnUIThread(
122 render_process_id
, stream_id
, callback
);
125 void StopMonitoring(int render_process_id
, int stream_id
) {
126 monitor_
->StopMonitoringStreamOnUIThread(render_process_id
, stream_id
);
130 AudioStreamMonitor
* monitor_
;
133 std::pair
<float, bool> ReadPower(int stream_id
) {
134 return std::make_pair(current_power_
[stream_id
], false);
137 void ExpectIsNotifyingForToggleOn() {
138 EXPECT_TRUE(monitor_
->WasRecentlyAudible());
141 void ExpectIsNotifyingForToggleOff() {
142 EXPECT_FALSE(monitor_
->WasRecentlyAudible());
145 MockWebContentsDelegate mock_web_contents_delegate_
;
146 base::SimpleTestTickClock clock_
;
147 std::map
<int, float> current_power_
;
149 DISALLOW_COPY_AND_ASSIGN(AudioStreamMonitorTest
);
152 // Tests that AudioStreamMonitor is polling while it has a
153 // ReadPowerAndClipCallback, and is not polling at other times.
154 TEST_F(AudioStreamMonitorTest
, PollsWhenProvidedACallback
) {
155 EXPECT_FALSE(monitor_
->WasRecentlyAudible());
156 ExpectIsPolling(kRenderProcessId
, kStreamId
, false);
158 StartMonitoring(kRenderProcessId
, kStreamId
, CreatePollCallback(kStreamId
));
159 EXPECT_FALSE(monitor_
->WasRecentlyAudible());
160 ExpectIsPolling(kRenderProcessId
, kStreamId
, true);
162 StopMonitoring(kRenderProcessId
, kStreamId
);
163 EXPECT_FALSE(monitor_
->WasRecentlyAudible());
164 ExpectIsPolling(kRenderProcessId
, kStreamId
, false);
167 // Tests that AudioStreamMonitor debounces the power level readings it's taking,
168 // which could be fluctuating rapidly between the audible versus silence
169 // threshold. See comments in audio_stream_monitor.h for expected behavior.
170 TEST_F(AudioStreamMonitorTest
,
171 ImpulsesKeepIndicatorOnUntilHoldingPeriodHasPassed
) {
172 StartMonitoring(kRenderProcessId
, kStreamId
, CreatePollCallback(kStreamId
));
174 // Expect WebContents will get one call form AudioStreamMonitor to toggle the
175 // indicator on upon the very first poll.
176 ExpectWebContentsWillBeNotifiedOnce(true);
178 // Loop, each time testing a slightly longer period of polled silence. The
179 // indicator should remain on throughout.
180 int num_silence_polls
= 0;
181 base::TimeTicks last_blurt_time
;
183 // Poll an audible signal, and expect tab indicator state is on.
184 SetStreamPower(kStreamId
, media::AudioPowerMonitor::max_power());
185 last_blurt_time
= GetTestClockTime();
186 SimulatePollTimerFired();
187 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
188 AdvanceClock(one_polling_interval());
190 // Poll a silent signal repeatedly, ensuring that the indicator is being
191 // held on during the holding period.
192 SetStreamPower(kStreamId
, media::AudioPowerMonitor::zero_power());
193 for (int i
= 0; i
< num_silence_polls
; ++i
) {
194 SimulatePollTimerFired();
195 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
196 // Note: Redundant off timer firings should not have any effect.
197 SimulateOffTimerFired();
198 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
199 AdvanceClock(one_polling_interval());
203 } while (GetTestClockTime() < last_blurt_time
+ holding_period());
205 // At this point, the clock has just advanced to beyond the holding period, so
206 // the next firing of the off timer should turn off the tab indicator. Also,
207 // make sure it stays off for several cycles thereafter.
208 ExpectWebContentsWillBeNotifiedOnce(false);
209 for (int i
= 0; i
< 10; ++i
) {
210 SimulateOffTimerFired();
211 ExpectTabWasRecentlyAudible(false, last_blurt_time
);
212 AdvanceClock(one_polling_interval());
216 // Tests that the AudioStreamMonitor correctly processes the blurts from two
217 // different streams in the same tab.
218 TEST_F(AudioStreamMonitorTest
, HandlesMultipleStreamsBlurting
) {
219 StartMonitoring(kRenderProcessId
, kStreamId
, CreatePollCallback(kStreamId
));
221 kRenderProcessId
, kAnotherStreamId
, CreatePollCallback(kAnotherStreamId
));
223 base::TimeTicks last_blurt_time
;
224 ExpectTabWasRecentlyAudible(false, last_blurt_time
);
226 // Measure audible sound from the first stream and silence from the second.
227 // The indicator turns on (i.e., tab was recently audible).
228 ExpectWebContentsWillBeNotifiedOnce(true);
229 SetStreamPower(kStreamId
, media::AudioPowerMonitor::max_power());
230 SetStreamPower(kAnotherStreamId
, media::AudioPowerMonitor::zero_power());
231 last_blurt_time
= GetTestClockTime();
232 SimulatePollTimerFired();
233 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
235 // Halfway through the holding period, the second stream joins in. The
236 // indicator stays on.
237 AdvanceClock(holding_period() / 2);
238 SimulateOffTimerFired();
239 SetStreamPower(kAnotherStreamId
, media::AudioPowerMonitor::max_power());
240 last_blurt_time
= GetTestClockTime();
241 SimulatePollTimerFired(); // Restarts holding period.
242 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
244 // Now, measure silence from both streams. After an entire holding period
245 // has passed (since the second stream joined in), the indicator should turn
247 ExpectWebContentsWillBeNotifiedOnce(false);
248 AdvanceClock(holding_period());
249 SimulateOffTimerFired();
250 SetStreamPower(kStreamId
, media::AudioPowerMonitor::zero_power());
251 SetStreamPower(kAnotherStreamId
, media::AudioPowerMonitor::zero_power());
252 SimulatePollTimerFired();
253 ExpectTabWasRecentlyAudible(false, last_blurt_time
);
255 // Now, measure silence from the first stream and audible sound from the
256 // second. The indicator turns back on.
257 ExpectWebContentsWillBeNotifiedOnce(true);
258 SetStreamPower(kAnotherStreamId
, media::AudioPowerMonitor::max_power());
259 last_blurt_time
= GetTestClockTime();
260 SimulatePollTimerFired();
261 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
263 // From here onwards, both streams are silent. Halfway through the holding
264 // period, the indicator should not have changed.
265 SetStreamPower(kAnotherStreamId
, media::AudioPowerMonitor::zero_power());
266 AdvanceClock(holding_period() / 2);
267 SimulatePollTimerFired();
268 SimulateOffTimerFired();
269 ExpectTabWasRecentlyAudible(true, last_blurt_time
);
271 // Just past the holding period, the indicator should be turned off.
272 ExpectWebContentsWillBeNotifiedOnce(false);
273 AdvanceClock(holding_period() - (GetTestClockTime() - last_blurt_time
));
274 SimulateOffTimerFired();
275 ExpectTabWasRecentlyAudible(false, last_blurt_time
);
277 // Polling should not turn the indicator back while both streams are remaining
279 for (int i
= 0; i
< 100; ++i
) {
280 AdvanceClock(one_polling_interval());
281 SimulatePollTimerFired();
282 ExpectTabWasRecentlyAudible(false, last_blurt_time
);
286 TEST_F(AudioStreamMonitorTest
, MultipleRendererProcesses
) {
287 StartMonitoring(kRenderProcessId
, kStreamId
, CreatePollCallback(kStreamId
));
289 kAnotherRenderProcessId
, kStreamId
, CreatePollCallback(kStreamId
));
290 ExpectIsPolling(kRenderProcessId
, kStreamId
, true);
291 ExpectIsPolling(kAnotherRenderProcessId
, kStreamId
, true);
292 StopMonitoring(kAnotherRenderProcessId
, kStreamId
);
293 ExpectIsPolling(kRenderProcessId
, kStreamId
, true);
294 ExpectIsPolling(kAnotherRenderProcessId
, kStreamId
, false);
297 } // namespace content