OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "content/browser/media/audio_stream_monitor.h" |
| 6 |
| 7 #include <map> |
| 8 #include <utility> |
| 9 |
| 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" |
| 21 |
| 22 using ::testing::InvokeWithoutArgs; |
| 23 |
| 24 namespace content { |
| 25 |
| 26 namespace { |
| 27 |
| 28 const int kRenderProcessId = 1; |
| 29 const int kAnotherRenderProcessId = 2; |
| 30 const int kStreamId = 3; |
| 31 const int kAnotherStreamId = 6; |
| 32 |
| 33 // Used to confirm audio indicator state changes occur at the correct times. |
| 34 class MockWebContentsDelegate : public WebContentsDelegate { |
| 35 public: |
| 36 MOCK_METHOD2(NavigationStateChanged, |
| 37 void(const WebContents* source, InvalidateTypes changed_flags)); |
| 38 }; |
| 39 |
| 40 } // namespace |
| 41 |
| 42 class AudioStreamMonitorTest : public RenderViewHostTestHarness { |
| 43 public: |
| 44 AudioStreamMonitorTest() { |
| 45 // Start |clock_| at non-zero. |
| 46 clock_.Advance(base::TimeDelta::FromSeconds(1000000)); |
| 47 } |
| 48 |
| 49 virtual void SetUp() OVERRIDE { |
| 50 RenderViewHostTestHarness::SetUp(); |
| 51 |
| 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_; |
| 57 } |
| 58 |
| 59 base::TimeTicks GetTestClockTime() { return clock_.NowTicks(); } |
| 60 |
| 61 void AdvanceClock(const base::TimeDelta& delta) { clock_.Advance(delta); } |
| 62 |
| 63 AudioStreamMonitor::ReadPowerAndClipCallback CreatePollCallback( |
| 64 int stream_id) { |
| 65 return base::Bind( |
| 66 &AudioStreamMonitorTest::ReadPower, base::Unretained(this), stream_id); |
| 67 } |
| 68 |
| 69 void SetStreamPower(int stream_id, float power) { |
| 70 current_power_[stream_id] = power; |
| 71 } |
| 72 |
| 73 void SimulatePollTimerFired() { monitor_->Poll(); } |
| 74 |
| 75 void SimulateOffTimerFired() { monitor_->MaybeToggle(); } |
| 76 |
| 77 void ExpectIsPolling(int render_process_id, int stream_id, bool is_polling) { |
| 78 const AudioStreamMonitor::StreamID key(render_process_id, stream_id); |
| 79 EXPECT_EQ( |
| 80 is_polling, |
| 81 monitor_->poll_callbacks_.find(key) != monitor_->poll_callbacks_.end()); |
| 82 EXPECT_EQ(!monitor_->poll_callbacks_.empty(), |
| 83 monitor_->poll_timer_.IsRunning()); |
| 84 } |
| 85 |
| 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()); |
| 92 } |
| 93 |
| 94 void ExpectWebContentsWillBeNotifiedOnce(bool should_be_audible) { |
| 95 EXPECT_CALL( |
| 96 mock_web_contents_delegate_, |
| 97 NavigationStateChanged(RenderViewHostTestHarness::web_contents(), |
| 98 INVALIDATE_TYPE_TAB)) |
| 99 .WillOnce(InvokeWithoutArgs( |
| 100 this, |
| 101 should_be_audible |
| 102 ? &AudioStreamMonitorTest::ExpectIsNotifyingForToggleOn |
| 103 : &AudioStreamMonitorTest::ExpectIsNotifyingForToggleOff)) |
| 104 .RetiresOnSaturation(); |
| 105 } |
| 106 |
| 107 static base::TimeDelta one_polling_interval() { |
| 108 return base::TimeDelta::FromSeconds(1) / |
| 109 AudioStreamMonitor::kPowerMeasurementsPerSecond; |
| 110 } |
| 111 |
| 112 static base::TimeDelta holding_period() { |
| 113 return base::TimeDelta::FromMilliseconds( |
| 114 AudioStreamMonitor::kHoldOnMilliseconds); |
| 115 } |
| 116 |
| 117 void StartMonitoring( |
| 118 int render_process_id, |
| 119 int stream_id, |
| 120 const AudioStreamMonitor::ReadPowerAndClipCallback& callback) { |
| 121 monitor_->StartMonitoringStreamOnUIThread( |
| 122 render_process_id, stream_id, callback); |
| 123 } |
| 124 |
| 125 void StopMonitoring(int render_process_id, int stream_id) { |
| 126 monitor_->StopMonitoringStreamOnUIThread(render_process_id, stream_id); |
| 127 } |
| 128 |
| 129 protected: |
| 130 AudioStreamMonitor* monitor_; |
| 131 |
| 132 private: |
| 133 std::pair<float, bool> ReadPower(int stream_id) { |
| 134 return std::make_pair(current_power_[stream_id], false); |
| 135 } |
| 136 |
| 137 void ExpectIsNotifyingForToggleOn() { |
| 138 EXPECT_TRUE(monitor_->WasRecentlyAudible()); |
| 139 } |
| 140 |
| 141 void ExpectIsNotifyingForToggleOff() { |
| 142 EXPECT_FALSE(monitor_->WasRecentlyAudible()); |
| 143 } |
| 144 |
| 145 MockWebContentsDelegate mock_web_contents_delegate_; |
| 146 base::SimpleTestTickClock clock_; |
| 147 std::map<int, float> current_power_; |
| 148 |
| 149 DISALLOW_COPY_AND_ASSIGN(AudioStreamMonitorTest); |
| 150 }; |
| 151 |
| 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); |
| 157 |
| 158 StartMonitoring(kRenderProcessId, kStreamId, CreatePollCallback(kStreamId)); |
| 159 EXPECT_FALSE(monitor_->WasRecentlyAudible()); |
| 160 ExpectIsPolling(kRenderProcessId, kStreamId, true); |
| 161 |
| 162 StopMonitoring(kRenderProcessId, kStreamId); |
| 163 EXPECT_FALSE(monitor_->WasRecentlyAudible()); |
| 164 ExpectIsPolling(kRenderProcessId, kStreamId, false); |
| 165 } |
| 166 |
| 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)); |
| 173 |
| 174 // Expect WebContents will get one call form AudioStreamMonitor to toggle the |
| 175 // indicator on upon the very first poll. |
| 176 ExpectWebContentsWillBeNotifiedOnce(true); |
| 177 |
| 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; |
| 182 do { |
| 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()); |
| 189 |
| 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()); |
| 200 } |
| 201 |
| 202 ++num_silence_polls; |
| 203 } while (GetTestClockTime() < last_blurt_time + holding_period()); |
| 204 |
| 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()); |
| 213 } |
| 214 } |
| 215 |
| 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)); |
| 220 StartMonitoring( |
| 221 kRenderProcessId, kAnotherStreamId, CreatePollCallback(kAnotherStreamId)); |
| 222 |
| 223 base::TimeTicks last_blurt_time; |
| 224 ExpectTabWasRecentlyAudible(false, last_blurt_time); |
| 225 |
| 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); |
| 234 |
| 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); |
| 243 |
| 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 |
| 246 // off. |
| 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); |
| 254 |
| 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); |
| 262 |
| 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); |
| 270 |
| 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); |
| 276 |
| 277 // Polling should not turn the indicator back while both streams are remaining |
| 278 // silent. |
| 279 for (int i = 0; i < 100; ++i) { |
| 280 AdvanceClock(one_polling_interval()); |
| 281 SimulatePollTimerFired(); |
| 282 ExpectTabWasRecentlyAudible(false, last_blurt_time); |
| 283 } |
| 284 } |
| 285 |
| 286 TEST_F(AudioStreamMonitorTest, MultipleRendererProcesses) { |
| 287 StartMonitoring(kRenderProcessId, kStreamId, CreatePollCallback(kStreamId)); |
| 288 StartMonitoring( |
| 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); |
| 295 } |
| 296 |
| 297 } // namespace content |
OLD | NEW |