// 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/macros.h" #include "base/message_loop/message_loop.h" #include "base/time/time.h" #include "media/audio/audio_parameters.h" #include "media/audio/fake_audio_worker.h" #include "media/audio/simple_sources.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { static const int kTestCallbacks = 5; class FakeAudioWorkerTest : public testing::Test { public: FakeAudioWorkerTest() : params_( AudioParameters::AUDIO_FAKE, CHANNEL_LAYOUT_STEREO, 44100, 8, 128), fake_worker_(message_loop_.task_runner(), params_), seen_callbacks_(0) { time_between_callbacks_ = base::TimeDelta::FromMicroseconds( params_.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / static_cast(params_.sample_rate())); } ~FakeAudioWorkerTest() override {} void CalledByFakeWorker() { seen_callbacks_++; } void RunOnAudioThread() { ASSERT_TRUE(message_loop_.task_runner()->BelongsToCurrentThread()); fake_worker_.Start(base::Bind( &FakeAudioWorkerTest::CalledByFakeWorker, base::Unretained(this))); } void RunOnceOnAudioThread() { ASSERT_TRUE(message_loop_.task_runner()->BelongsToCurrentThread()); RunOnAudioThread(); // Start() should immediately post a task to run the callback, so we // should end up with only a single callback being run. message_loop_.PostTask(FROM_HERE, base::Bind( &FakeAudioWorkerTest::EndTest, base::Unretained(this), 1)); } void StopStartOnAudioThread() { ASSERT_TRUE(message_loop_.task_runner()->BelongsToCurrentThread()); fake_worker_.Stop(); RunOnAudioThread(); } void TimeCallbacksOnAudioThread(int callbacks) { ASSERT_TRUE(message_loop_.task_runner()->BelongsToCurrentThread()); if (seen_callbacks_ == 0) { RunOnAudioThread(); start_time_ = base::TimeTicks::Now(); } // Keep going until we've seen the requested number of callbacks. if (seen_callbacks_ < callbacks) { message_loop_.PostDelayedTask(FROM_HERE, base::Bind( &FakeAudioWorkerTest::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_.task_runner()->BelongsToCurrentThread()); fake_worker_.Stop(); EXPECT_LE(callbacks, seen_callbacks_); message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); } protected: base::MessageLoop message_loop_; AudioParameters params_; FakeAudioWorker fake_worker_; base::TimeTicks start_time_; base::TimeTicks end_time_; base::TimeDelta time_between_callbacks_; int seen_callbacks_; private: DISALLOW_COPY_AND_ASSIGN(FakeAudioWorkerTest); }; // Ensure the worker runs on the audio thread and fires callbacks. TEST_F(FakeAudioWorkerTest, FakeBasicCallback) { message_loop_.PostTask(FROM_HERE, base::Bind( &FakeAudioWorkerTest::RunOnceOnAudioThread, base::Unretained(this))); message_loop_.Run(); } // Ensure the time between callbacks is sane. TEST_F(FakeAudioWorkerTest, TimeBetweenCallbacks) { message_loop_.PostTask(FROM_HERE, base::Bind( &FakeAudioWorkerTest::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_) / (seen_callbacks_ - 1); // Ensure callback time is no faster than the expected time between callbacks. EXPECT_GE(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 worker doesn't generate too many callbacks. See // http://crbug.com/159049. TEST_F(FakeAudioWorkerTest, StartStopClearsCallbacks) { message_loop_.PostTask(FROM_HERE, base::Bind( &FakeAudioWorkerTest::TimeCallbacksOnAudioThread, base::Unretained(this), kTestCallbacks)); // Issue a Stop() / Start() in between expected callbacks to maximize the // chance of catching the worker doing the wrong thing. message_loop_.PostDelayedTask(FROM_HERE, base::Bind( &FakeAudioWorkerTest::StopStartOnAudioThread, base::Unretained(this)), time_between_callbacks_ / 2); // EndTest() will ensure the proper number of callbacks have occurred. message_loop_.Run(); } } // namespace media