| 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 |