// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "content/renderer/media/webaudiosourceprovider_impl.h" #include "media/audio/audio_parameters.h" #include "media/base/fake_audio_render_callback.h" #include "media/base/mock_audio_renderer_sink.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/platform/WebAudioSourceProviderClient.h" namespace content { namespace { const float kTestVolume = 0.25; } // namespace class WebAudioSourceProviderImplTest : public testing::Test, public blink::WebAudioSourceProviderClient { public: WebAudioSourceProviderImplTest() : params_(media::AudioParameters::AUDIO_PCM_LINEAR, media::CHANNEL_LAYOUT_STEREO, 48000, 16, 64), fake_callback_(0.1), mock_sink_(new media::MockAudioRendererSink()), wasp_impl_(new WebAudioSourceProviderImpl(mock_sink_)) { } virtual ~WebAudioSourceProviderImplTest() {} void CallAllSinkMethodsAndVerify(bool verify) { testing::InSequence s; EXPECT_CALL(*mock_sink_.get(), Start()).Times(verify); wasp_impl_->Start(); EXPECT_CALL(*mock_sink_.get(), Play()).Times(verify); wasp_impl_->Play(); EXPECT_CALL(*mock_sink_.get(), Pause()).Times(verify); wasp_impl_->Pause(); EXPECT_CALL(*mock_sink_.get(), SetVolume(kTestVolume)).Times(verify); wasp_impl_->SetVolume(kTestVolume); EXPECT_CALL(*mock_sink_.get(), Stop()).Times(verify); wasp_impl_->Stop(); testing::Mock::VerifyAndClear(mock_sink_.get()); } void SetClient(blink::WebAudioSourceProviderClient* client) { testing::InSequence s; if (client) { EXPECT_CALL(*mock_sink_.get(), Stop()); EXPECT_CALL(*this, setFormat(params_.channels(), params_.sample_rate())); } wasp_impl_->setClient(client); base::RunLoop().RunUntilIdle(); testing::Mock::VerifyAndClear(mock_sink_.get()); testing::Mock::VerifyAndClear(this); } bool CompareBusses(const media::AudioBus* bus1, const media::AudioBus* bus2) { EXPECT_EQ(bus1->channels(), bus2->channels()); EXPECT_EQ(bus1->frames(), bus2->frames()); for (int ch = 0; ch < bus1->channels(); ++ch) { if (memcmp(bus1->channel(ch), bus2->channel(ch), sizeof(*bus1->channel(ch)) * bus1->frames()) != 0) { return false; } } return true; } // blink::WebAudioSourceProviderClient implementation. MOCK_METHOD2(setFormat, void(size_t numberOfChannels, float sampleRate)); protected: media::AudioParameters params_; media::FakeAudioRenderCallback fake_callback_; scoped_refptr<media::MockAudioRendererSink> mock_sink_; scoped_refptr<WebAudioSourceProviderImpl> wasp_impl_; base::MessageLoop message_loop_; DISALLOW_COPY_AND_ASSIGN(WebAudioSourceProviderImplTest); }; TEST_F(WebAudioSourceProviderImplTest, SetClientBeforeInitialize) { // setClient() with a NULL client should do nothing if no client is set. wasp_impl_->setClient(NULL); EXPECT_CALL(*mock_sink_.get(), Stop()); wasp_impl_->setClient(this); base::RunLoop().RunUntilIdle(); // When Initialize() is called after setClient(), the params should propagate // to the client via setFormat() during the call. EXPECT_CALL(*this, setFormat(params_.channels(), params_.sample_rate())); wasp_impl_->Initialize(params_, &fake_callback_); base::RunLoop().RunUntilIdle(); // setClient() with the same client should do nothing. wasp_impl_->setClient(this); base::RunLoop().RunUntilIdle(); } // Verify AudioRendererSink functionality w/ and w/o a client. TEST_F(WebAudioSourceProviderImplTest, SinkMethods) { wasp_impl_->Initialize(params_, &fake_callback_); ASSERT_EQ(mock_sink_->callback(), &fake_callback_); // Without a client all WASP calls should fall through to the underlying sink. CallAllSinkMethodsAndVerify(true); // With a client no calls should reach the Stop()'d sink. Also, setClient() // should propagate the params provided during Initialize() at call time. SetClient(this); CallAllSinkMethodsAndVerify(false); // Removing the client should cause WASP to revert to the underlying sink. EXPECT_CALL(*mock_sink_.get(), SetVolume(kTestVolume)); SetClient(NULL); CallAllSinkMethodsAndVerify(true); } // Verify underlying sink state is restored after client removal. TEST_F(WebAudioSourceProviderImplTest, SinkStateRestored) { wasp_impl_->Initialize(params_, &fake_callback_); // Verify state set before the client is set propagates back afterward. EXPECT_CALL(*mock_sink_.get(), Start()); wasp_impl_->Start(); SetClient(this); EXPECT_CALL(*mock_sink_.get(), SetVolume(1.0)); EXPECT_CALL(*mock_sink_.get(), Start()); SetClient(NULL); // Verify state set while the client was attached propagates back afterward. SetClient(this); wasp_impl_->Play(); wasp_impl_->SetVolume(kTestVolume); EXPECT_CALL(*mock_sink_.get(), SetVolume(kTestVolume)); EXPECT_CALL(*mock_sink_.get(), Start()); EXPECT_CALL(*mock_sink_.get(), Play()); SetClient(NULL); } // Test the AudioRendererSink state machine and its effects on provideInput(). TEST_F(WebAudioSourceProviderImplTest, ProvideInput) { scoped_ptr<media::AudioBus> bus1 = media::AudioBus::Create(params_); scoped_ptr<media::AudioBus> bus2 = media::AudioBus::Create(params_); // Point the WebVector into memory owned by |bus1|. blink::WebVector<float*> audio_data(static_cast<size_t>(bus1->channels())); for (size_t i = 0; i < audio_data.size(); ++i) audio_data[i] = bus1->channel(i); // Verify provideInput() works before Initialize() and returns silence. bus1->channel(0)[0] = 1; bus2->Zero(); wasp_impl_->provideInput(audio_data, params_.frames_per_buffer()); ASSERT_TRUE(CompareBusses(bus1.get(), bus2.get())); wasp_impl_->Initialize(params_, &fake_callback_); SetClient(this); // Verify provideInput() is muted prior to Start() and no calls to the render // callback have occurred. bus1->channel(0)[0] = 1; bus2->Zero(); wasp_impl_->provideInput(audio_data, params_.frames_per_buffer()); ASSERT_TRUE(CompareBusses(bus1.get(), bus2.get())); ASSERT_EQ(fake_callback_.last_audio_delay_milliseconds(), -1); wasp_impl_->Start(); // Ditto for Play(). bus1->channel(0)[0] = 1; wasp_impl_->provideInput(audio_data, params_.frames_per_buffer()); ASSERT_TRUE(CompareBusses(bus1.get(), bus2.get())); ASSERT_EQ(fake_callback_.last_audio_delay_milliseconds(), -1); wasp_impl_->Play(); // Now we should get real audio data. wasp_impl_->provideInput(audio_data, params_.frames_per_buffer()); ASSERT_FALSE(CompareBusses(bus1.get(), bus2.get())); // Ensure volume adjustment is working. fake_callback_.reset(); fake_callback_.Render(bus2.get(), 0); bus2->Scale(kTestVolume); fake_callback_.reset(); wasp_impl_->SetVolume(kTestVolume); wasp_impl_->provideInput(audio_data, params_.frames_per_buffer()); ASSERT_TRUE(CompareBusses(bus1.get(), bus2.get())); // Pause should return to silence. wasp_impl_->Pause(); bus1->channel(0)[0] = 1; bus2->Zero(); wasp_impl_->provideInput(audio_data, params_.frames_per_buffer()); ASSERT_TRUE(CompareBusses(bus1.get(), bus2.get())); // Ensure if a renderer properly fill silence for partial Render() calls by // configuring the fake callback to return half the data. After these calls // bus1 is full of junk data, and bus2 is partially filled. wasp_impl_->SetVolume(1); fake_callback_.Render(bus1.get(), 0); fake_callback_.reset(); fake_callback_.Render(bus2.get(), 0); bus2->ZeroFramesPartial(bus2->frames() / 2, bus2->frames() - bus2->frames() / 2); fake_callback_.reset(); fake_callback_.set_half_fill(true); wasp_impl_->Play(); // Play should return real audio data again, but the last half should be zero. wasp_impl_->provideInput(audio_data, params_.frames_per_buffer()); ASSERT_TRUE(CompareBusses(bus1.get(), bus2.get())); // Stop() should return silence. wasp_impl_->Stop(); bus1->channel(0)[0] = 1; bus2->Zero(); wasp_impl_->provideInput(audio_data, params_.frames_per_buffer()); ASSERT_TRUE(CompareBusses(bus1.get(), bus2.get())); } } // namespace content