Home | History | Annotate | Download | only in media
      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 "chrome/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 "chrome/test/base/testing_profile.h"
     15 #include "content/public/browser/invalidate_type.h"
     16 #include "content/public/browser/web_contents.h"
     17 #include "content/public/browser/web_contents_delegate.h"
     18 #include "content/public/test/test_browser_thread_bundle.h"
     19 #include "media/audio/audio_power_monitor.h"
     20 #include "testing/gmock/include/gmock/gmock.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 
     23 using ::testing::InvokeWithoutArgs;
     24 
     25 namespace {
     26 
     27 const int kStreamId = 3;
     28 const int kAnotherStreamId = 6;
     29 
     30 // Used to confirm audio indicator state changes occur at the correct times.
     31 class MockWebContentsDelegate : public content::WebContentsDelegate {
     32  public:
     33   MOCK_METHOD2(NavigationStateChanged,
     34                void(const content::WebContents* source,
     35                     unsigned changed_flags));
     36 };
     37 
     38 }  // namespace
     39 
     40 class AudioStreamMonitorTest : public testing::Test {
     41  public:
     42   AudioStreamMonitorTest() {
     43     // Start |clock_| at non-zero.
     44     clock_.Advance(base::TimeDelta::FromSeconds(1000000));
     45 
     46     // Create a WebContents instance and set it to use our mock delegate.
     47     web_contents_.reset(content::WebContents::Create(
     48         content::WebContents::CreateParams(&profile_, NULL)));
     49     web_contents_->SetDelegate(&mock_web_contents_delegate_);
     50 
     51     // Create an AudioStreamMonitor instance whose lifecycle is tied to that of
     52     // |web_contents_|, and override its clock with the test clock.
     53     AudioStreamMonitor::CreateForWebContents(web_contents_.get());
     54     CHECK(audio_stream_monitor());
     55     const_cast<base::TickClock*&>(audio_stream_monitor()->clock_) = &clock_;
     56   }
     57 
     58   AudioStreamMonitor* audio_stream_monitor() {
     59     return AudioStreamMonitor::FromWebContents(web_contents_.get());
     60   }
     61 
     62   base::TimeTicks GetTestClockTime() { return clock_.NowTicks(); }
     63 
     64   void AdvanceClock(const base::TimeDelta& delta) { clock_.Advance(delta); }
     65 
     66   AudioStreamMonitor::ReadPowerAndClipCallback CreatePollCallback(
     67       int stream_id) {
     68     return base::Bind(
     69         &AudioStreamMonitorTest::ReadPower, base::Unretained(this), stream_id);
     70   }
     71 
     72   void SetStreamPower(int stream_id, float power) {
     73     current_power_[stream_id] = power;
     74   }
     75 
     76   void SimulatePollTimerFired() { audio_stream_monitor()->Poll(); }
     77 
     78   void SimulateOffTimerFired() { audio_stream_monitor()->MaybeToggle(); }
     79 
     80   void ExpectIsPolling(int stream_id, bool is_polling) {
     81     AudioStreamMonitor* const monitor = audio_stream_monitor();
     82     EXPECT_EQ(is_polling,
     83               monitor->poll_callbacks_.find(stream_id) !=
     84                   monitor->poll_callbacks_.end());
     85     EXPECT_EQ(!monitor->poll_callbacks_.empty(),
     86               monitor->poll_timer_.IsRunning());
     87   }
     88 
     89   void ExpectTabWasRecentlyAudible(bool was_audible,
     90                                    const base::TimeTicks& last_blurt_time) {
     91     AudioStreamMonitor* const monitor = audio_stream_monitor();
     92     EXPECT_EQ(was_audible, monitor->was_recently_audible_);
     93     EXPECT_EQ(last_blurt_time, monitor->last_blurt_time_);
     94     EXPECT_EQ(monitor->was_recently_audible_, monitor->off_timer_.IsRunning());
     95   }
     96 
     97   void ExpectWebContentsWillBeNotifiedOnce(bool should_be_audible) {
     98     EXPECT_CALL(mock_web_contents_delegate_,
     99                 NavigationStateChanged(web_contents_.get(),
    100                                        content::INVALIDATE_TYPE_TAB))
    101         .WillOnce(InvokeWithoutArgs(
    102             this,
    103             should_be_audible
    104                 ? &AudioStreamMonitorTest::ExpectIsNotifyingForToggleOn
    105                 : &AudioStreamMonitorTest::ExpectIsNotifyingForToggleOff))
    106         .RetiresOnSaturation();
    107   }
    108 
    109   static base::TimeDelta one_polling_interval() {
    110     return base::TimeDelta::FromSeconds(1) /
    111            AudioStreamMonitor::kPowerMeasurementsPerSecond;
    112   }
    113 
    114   static base::TimeDelta holding_period() {
    115     return base::TimeDelta::FromMilliseconds(
    116         AudioStreamMonitor::kHoldOnMilliseconds);
    117   }
    118 
    119  private:
    120   std::pair<float, bool> ReadPower(int stream_id) {
    121     return std::make_pair(current_power_[stream_id], false);
    122   }
    123 
    124   void ExpectIsNotifyingForToggleOn() {
    125     EXPECT_TRUE(audio_stream_monitor()->WasRecentlyAudible());
    126   }
    127 
    128   void ExpectIsNotifyingForToggleOff() {
    129     EXPECT_FALSE(audio_stream_monitor()->WasRecentlyAudible());
    130   }
    131 
    132   content::TestBrowserThreadBundle browser_thread_bundle_;
    133   TestingProfile profile_;
    134   MockWebContentsDelegate mock_web_contents_delegate_;
    135   base::SimpleTestTickClock clock_;
    136   std::map<int, float> current_power_;
    137   scoped_ptr<content::WebContents> web_contents_;
    138 
    139   DISALLOW_COPY_AND_ASSIGN(AudioStreamMonitorTest);
    140 };
    141 
    142 // Tests that AudioStreamMonitor is polling while it has a
    143 // ReadPowerAndClipCallback, and is not polling at other times.
    144 TEST_F(AudioStreamMonitorTest, PollsWhenProvidedACallback) {
    145   EXPECT_FALSE(audio_stream_monitor()->WasRecentlyAudible());
    146   ExpectIsPolling(kStreamId, false);
    147 
    148   audio_stream_monitor()->StartMonitoringStream(kStreamId,
    149                                                 CreatePollCallback(kStreamId));
    150   EXPECT_FALSE(audio_stream_monitor()->WasRecentlyAudible());
    151   ExpectIsPolling(kStreamId, true);
    152 
    153   audio_stream_monitor()->StopMonitoringStream(kStreamId);
    154   EXPECT_FALSE(audio_stream_monitor()->WasRecentlyAudible());
    155   ExpectIsPolling(kStreamId, false);
    156 }
    157 
    158 // Tests that AudioStreamMonitor debounces the power level readings it's taking,
    159 // which could be fluctuating rapidly between the audible versus silence
    160 // threshold.  See comments in audio_stream_monitor.h for expected behavior.
    161 TEST_F(AudioStreamMonitorTest,
    162        ImpulsesKeepIndicatorOnUntilHoldingPeriodHasPassed) {
    163   audio_stream_monitor()->StartMonitoringStream(kStreamId,
    164                                                 CreatePollCallback(kStreamId));
    165 
    166   // Expect WebContents will get one call form AudioStreamMonitor to toggle the
    167   // indicator on upon the very first poll.
    168   ExpectWebContentsWillBeNotifiedOnce(true);
    169 
    170   // Loop, each time testing a slightly longer period of polled silence.  The
    171   // indicator should remain on throughout.
    172   int num_silence_polls = 0;
    173   base::TimeTicks last_blurt_time;
    174   do {
    175     // Poll an audible signal, and expect tab indicator state is on.
    176     SetStreamPower(kStreamId, media::AudioPowerMonitor::max_power());
    177     last_blurt_time = GetTestClockTime();
    178     SimulatePollTimerFired();
    179     ExpectTabWasRecentlyAudible(true, last_blurt_time);
    180     AdvanceClock(one_polling_interval());
    181 
    182     // Poll a silent signal repeatedly, ensuring that the indicator is being
    183     // held on during the holding period.
    184     SetStreamPower(kStreamId, media::AudioPowerMonitor::zero_power());
    185     for (int i = 0; i < num_silence_polls; ++i) {
    186       SimulatePollTimerFired();
    187       ExpectTabWasRecentlyAudible(true, last_blurt_time);
    188       // Note: Redundant off timer firings should not have any effect.
    189       SimulateOffTimerFired();
    190       ExpectTabWasRecentlyAudible(true, last_blurt_time);
    191       AdvanceClock(one_polling_interval());
    192     }
    193 
    194     ++num_silence_polls;
    195   } while (GetTestClockTime() < last_blurt_time + holding_period());
    196 
    197   // At this point, the clock has just advanced to beyond the holding period, so
    198   // the next firing of the off timer should turn off the tab indicator.  Also,
    199   // make sure it stays off for several cycles thereafter.
    200   ExpectWebContentsWillBeNotifiedOnce(false);
    201   for (int i = 0; i < 10; ++i) {
    202     SimulateOffTimerFired();
    203     ExpectTabWasRecentlyAudible(false, last_blurt_time);
    204     AdvanceClock(one_polling_interval());
    205   }
    206 }
    207 
    208 // Tests that the AudioStreamMonitor correctly processes the blurts from two
    209 // different streams in the same tab.
    210 TEST_F(AudioStreamMonitorTest, HandlesMultipleStreamsBlurting) {
    211   audio_stream_monitor()->StartMonitoringStream(kStreamId,
    212                                                 CreatePollCallback(kStreamId));
    213   audio_stream_monitor()->StartMonitoringStream(
    214       kAnotherStreamId,
    215       CreatePollCallback(kAnotherStreamId));
    216 
    217   base::TimeTicks last_blurt_time;
    218   ExpectTabWasRecentlyAudible(false, last_blurt_time);
    219 
    220   // Measure audible sound from the first stream and silence from the second.
    221   // The indicator turns on (i.e., tab was recently audible).
    222   ExpectWebContentsWillBeNotifiedOnce(true);
    223   SetStreamPower(kStreamId, media::AudioPowerMonitor::max_power());
    224   SetStreamPower(kAnotherStreamId, media::AudioPowerMonitor::zero_power());
    225   last_blurt_time = GetTestClockTime();
    226   SimulatePollTimerFired();
    227   ExpectTabWasRecentlyAudible(true, last_blurt_time);
    228 
    229   // Halfway through the holding period, the second stream joins in.  The
    230   // indicator stays on.
    231   AdvanceClock(holding_period() / 2);
    232   SimulateOffTimerFired();
    233   SetStreamPower(kAnotherStreamId, media::AudioPowerMonitor::max_power());
    234   last_blurt_time = GetTestClockTime();
    235   SimulatePollTimerFired();  // Restarts holding period.
    236   ExpectTabWasRecentlyAudible(true, last_blurt_time);
    237 
    238   // Now, measure silence from both streams.  After an entire holding period
    239   // has passed (since the second stream joined in), the indicator should turn
    240   // off.
    241   ExpectWebContentsWillBeNotifiedOnce(false);
    242   AdvanceClock(holding_period());
    243   SimulateOffTimerFired();
    244   SetStreamPower(kStreamId, media::AudioPowerMonitor::zero_power());
    245   SetStreamPower(kAnotherStreamId, media::AudioPowerMonitor::zero_power());
    246   SimulatePollTimerFired();
    247   ExpectTabWasRecentlyAudible(false, last_blurt_time);
    248 
    249   // Now, measure silence from the first stream and audible sound from the
    250   // second.  The indicator turns back on.
    251   ExpectWebContentsWillBeNotifiedOnce(true);
    252   SetStreamPower(kAnotherStreamId, media::AudioPowerMonitor::max_power());
    253   last_blurt_time = GetTestClockTime();
    254   SimulatePollTimerFired();
    255   ExpectTabWasRecentlyAudible(true, last_blurt_time);
    256 
    257   // From here onwards, both streams are silent.  Halfway through the holding
    258   // period, the indicator should not have changed.
    259   SetStreamPower(kAnotherStreamId, media::AudioPowerMonitor::zero_power());
    260   AdvanceClock(holding_period() / 2);
    261   SimulatePollTimerFired();
    262   SimulateOffTimerFired();
    263   ExpectTabWasRecentlyAudible(true, last_blurt_time);
    264 
    265   // Just past the holding period, the indicator should be turned off.
    266   ExpectWebContentsWillBeNotifiedOnce(false);
    267   AdvanceClock(holding_period() - (GetTestClockTime() - last_blurt_time));
    268   SimulateOffTimerFired();
    269   ExpectTabWasRecentlyAudible(false, last_blurt_time);
    270 
    271   // Polling should not turn the indicator back while both streams are remaining
    272   // silent.
    273   for (int i = 0; i < 100; ++i) {
    274     AdvanceClock(one_polling_interval());
    275     SimulatePollTimerFired();
    276     ExpectTabWasRecentlyAudible(false, last_blurt_time);
    277   }
    278 }
    279