// Copyright (c) 2012 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/bind.h" #include "base/message_loop/message_loop.h" #include "base/time/time.h" #include "media/audio/audio_buffers_state.h" #include "media/audio/audio_parameters.h" #include "media/audio/fake_audio_consumer.h" #include "media/audio/simple_sources.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { static const int kTestCallbacks = 5; class FakeAudioConsumerTest : public testing::Test { public: FakeAudioConsumerTest() : params_( AudioParameters::AUDIO_FAKE, CHANNEL_LAYOUT_STEREO, 44100, 8, 128), fake_consumer_(message_loop_.message_loop_proxy(), params_), source_(params_.channels(), 200.0, params_.sample_rate()) { time_between_callbacks_ = base::TimeDelta::FromMicroseconds( params_.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / static_cast<float>(params_.sample_rate())); } virtual ~FakeAudioConsumerTest() {} void ConsumeData(AudioBus* audio_bus) { source_.OnMoreData(audio_bus, AudioBuffersState()); } void RunOnAudioThread() { ASSERT_TRUE(message_loop_.message_loop_proxy()->BelongsToCurrentThread()); fake_consumer_.Start(base::Bind( &FakeAudioConsumerTest::ConsumeData, base::Unretained(this))); } void RunOnceOnAudioThread() { ASSERT_TRUE(message_loop_.message_loop_proxy()->BelongsToCurrentThread()); RunOnAudioThread(); // Start() should immediately post a task to run the source callback, so we // should end up with only a single callback being run. message_loop_.PostTask(FROM_HERE, base::Bind( &FakeAudioConsumerTest::EndTest, base::Unretained(this), 1)); } void StopStartOnAudioThread() { ASSERT_TRUE(message_loop_.message_loop_proxy()->BelongsToCurrentThread()); fake_consumer_.Stop(); RunOnAudioThread(); } void TimeCallbacksOnAudioThread(int callbacks) { ASSERT_TRUE(message_loop_.message_loop_proxy()->BelongsToCurrentThread()); if (source_.callbacks() == 0) { RunOnAudioThread(); start_time_ = base::TimeTicks::Now(); } // Keep going until we've seen the requested number of callbacks. if (source_.callbacks() < callbacks) { message_loop_.PostDelayedTask(FROM_HERE, base::Bind( &FakeAudioConsumerTest::TimeCallbacksOnAudioThread, base::Unretained(this), callbacks), time_between_callbacks_ / 2); } else { end_time_ = base::TimeTicks::Now(); EndTest(callbacks); } } void EndTest(int callbacks) { ASSERT_TRUE(message_loop_.message_loop_proxy()->BelongsToCurrentThread()); fake_consumer_.Stop(); EXPECT_LE(callbacks, source_.callbacks()); message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); } protected: base::MessageLoop message_loop_; AudioParameters params_; FakeAudioConsumer fake_consumer_; SineWaveAudioSource source_; base::TimeTicks start_time_; base::TimeTicks end_time_; base::TimeDelta time_between_callbacks_; private: DISALLOW_COPY_AND_ASSIGN(FakeAudioConsumerTest); }; // Ensure the fake audio stream runs on the audio thread and handles fires // callbacks to the AudioSourceCallback. TEST_F(FakeAudioConsumerTest, FakeStreamBasicCallback) { message_loop_.PostTask(FROM_HERE, base::Bind( &FakeAudioConsumerTest::RunOnceOnAudioThread, base::Unretained(this))); message_loop_.Run(); } // Ensure the time between callbacks is sane. TEST_F(FakeAudioConsumerTest, TimeBetweenCallbacks) { message_loop_.PostTask(FROM_HERE, base::Bind( &FakeAudioConsumerTest::TimeCallbacksOnAudioThread, base::Unretained(this), kTestCallbacks)); message_loop_.Run(); // There are only (kTestCallbacks - 1) intervals between kTestCallbacks. base::TimeDelta actual_time_between_callbacks = (end_time_ - start_time_) / (source_.callbacks() - 1); // Ensure callback time is no faster than the expected time between callbacks. EXPECT_TRUE(actual_time_between_callbacks >= time_between_callbacks_); // Softly check if the callback time is no slower than twice the expected time // between callbacks. Since this test runs on the bots we can't be too strict // with the bounds. if (actual_time_between_callbacks > 2 * time_between_callbacks_) LOG(ERROR) << "Time between fake audio callbacks is too large!"; } // Ensure Start()/Stop() on the stream doesn't generate too many callbacks. See // http://crbug.com/159049 TEST_F(FakeAudioConsumerTest, StartStopClearsCallbacks) { message_loop_.PostTask(FROM_HERE, base::Bind( &FakeAudioConsumerTest::TimeCallbacksOnAudioThread, base::Unretained(this), kTestCallbacks)); // Issue a Stop() / Start() in between expected callbacks to maximize the // chance of catching the FakeAudioOutputStream doing the wrong thing. message_loop_.PostDelayedTask(FROM_HERE, base::Bind( &FakeAudioConsumerTest::StopStartOnAudioThread, base::Unretained(this)), time_between_callbacks_ / 2); // EndTest() will ensure the proper number of callbacks have occurred. message_loop_.Run(); } } // namespace media