Index: media/audio/audio_debug_recording_helper_unittest.cc |
diff --git a/media/audio/audio_debug_recording_helper_unittest.cc b/media/audio/audio_debug_recording_helper_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b637006c185ecc98fcc34f564f6b970a8d2ec14e |
--- /dev/null |
+++ b/media/audio/audio_debug_recording_helper_unittest.cc |
@@ -0,0 +1,258 @@ |
+// Copyright 2017 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 "media/audio/audio_debug_recording_helper.h" |
+ |
+#include "base/bind.h" |
+#include "base/files/file_path.h" |
+#include "base/logging.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/run_loop.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/test/test_message_loop.h" |
+#include "media/base/audio_bus.h" |
+#include "media/base/audio_sample_types.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using testing::_; |
+using testing::Return; |
+ |
+#if defined(OS_WIN) |
+#define IntToStringType base::IntToString16 |
+#else |
+#define IntToStringType base::IntToString |
+#endif |
+ |
+namespace media { |
+ |
+namespace { |
+ |
+// The base file path. |
+const base::FilePath::CharType kBaseFilePath[] = FILE_PATH_LITERAL("file_path"); |
+ |
+// The filename extension the mock should return in GetFileNameExtension(). |
+const base::FilePath::CharType kFileNameExtension[] = FILE_PATH_LITERAL("wav"); |
+ |
+} // namespace |
+ |
+// Mock class for the audio file writer that the helper wraps. |
+class MockAudioDebugFileWriter : public AudioDebugFileWriter { |
+ public: |
+ MockAudioDebugFileWriter( |
+ const AudioParameters& params, |
+ scoped_refptr<base::SingleThreadTaskRunner> file_task_runner) |
+ : AudioDebugFileWriter(params, std::move(file_task_runner)), |
+ reference_data_(nullptr) {} |
+ ~MockAudioDebugFileWriter() override {} |
+ |
+ MOCK_METHOD1(Start, void(const base::FilePath&)); |
+ MOCK_METHOD0(Stop, void()); |
+ |
+ // Functions with move-only types as arguments can't be mocked directly, so |
+ // we pass on to DoWrite(). Also, we can verify the data this way. |
+ MOCK_METHOD1(DoWrite, void(AudioBus*)); |
+ void Write(std::unique_ptr<AudioBus> data) override { |
+ CHECK(reference_data_); |
+ EXPECT_EQ(reference_data_->channels(), data->channels()); |
+ EXPECT_EQ(reference_data_->frames(), data->frames()); |
+ for (int i = 0; i < data->channels(); ++i) { |
+ float* data_ptr = data->channel(i); |
+ float* ref_data_ptr = reference_data_->channel(i); |
+ for (int j = 0; j < data->frames(); ++j, ++data_ptr, ++ref_data_ptr) |
+ EXPECT_EQ(*ref_data_ptr, *data_ptr); |
+ } |
+ DoWrite(data.get()); |
+ } |
+ |
+ MOCK_METHOD0(WillWrite, bool()); |
+ MOCK_METHOD0(GetFileNameExtension, const base::FilePath::CharType*()); |
+ |
+ // Set reference data to compare against. Must be called before Write() is |
+ // called. |
+ void SetReferenceData(AudioBus* reference_data) { |
+ EXPECT_EQ(params_.channels(), reference_data->channels()); |
+ EXPECT_EQ(params_.frames_per_buffer(), reference_data->frames()); |
+ reference_data_ = reference_data; |
+ } |
+ |
+ private: |
+ AudioBus* reference_data_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MockAudioDebugFileWriter); |
+}; |
+ |
+// Sub-class of the helper that overrides the CreateAudioDebugFileWriter |
+// function to create the above mock instead. |
+class AudioDebugRecordingHelperUnderTest : public AudioDebugRecordingHelper { |
+ public: |
+ AudioDebugRecordingHelperUnderTest( |
+ const AudioParameters& params, |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
+ scoped_refptr<base::SingleThreadTaskRunner> file_task_runner, |
+ base::OnceClosure on_destruction_closure) |
+ : AudioDebugRecordingHelper(params, |
+ std::move(task_runner), |
+ std::move(file_task_runner), |
+ std::move(on_destruction_closure)) {} |
+ ~AudioDebugRecordingHelperUnderTest() override {} |
+ |
+ private: |
+ // Creates the mock writer. After the mock writer is returned, we always |
+ // expect GetFileNameExtension() and Start() to be called on it by the helper. |
+ std::unique_ptr<AudioDebugFileWriter> CreateAudioDebugFileWriter( |
+ const AudioParameters& params, |
+ scoped_refptr<base::SingleThreadTaskRunner> file_task_runner) override { |
+ MockAudioDebugFileWriter* writer = |
+ new MockAudioDebugFileWriter(params, std::move(file_task_runner)); |
+ EXPECT_CALL(*writer, GetFileNameExtension()) |
+ .WillOnce(Return(kFileNameExtension)); |
+ base::FilePath expected_file_path = |
+ base::FilePath(kBaseFilePath).AddExtension(kFileNameExtension); |
+ EXPECT_CALL(*writer, Start(expected_file_path)); |
+ return base::WrapUnique<AudioDebugFileWriter>(writer); |
+ } |
+ |
+ DISALLOW_COPY_AND_ASSIGN(AudioDebugRecordingHelperUnderTest); |
+}; |
+ |
+// The test fixture. |
+class AudioDebugRecordingHelperTest : public ::testing::Test { |
+ public: |
+ AudioDebugRecordingHelperTest() : file_path_(kBaseFilePath) {} |
+ |
+ ~AudioDebugRecordingHelperTest() override {} |
+ |
+ // Helper function that creates a recording helper. |
+ std::unique_ptr<AudioDebugRecordingHelper> CreateRecordingHelper( |
+ const AudioParameters& params, |
+ base::OnceClosure on_destruction_closure) { |
+ return base::MakeUnique<AudioDebugRecordingHelperUnderTest>( |
+ params, message_loop_.task_runner(), message_loop_.task_runner(), |
+ std::move(on_destruction_closure)); |
+ } |
+ |
+ // Helper function that unsets the mock writer pointer after disabling. |
+ void DisableDebugRecording(AudioDebugRecordingHelper* recording_helper) { |
+ recording_helper->DisableDebugRecording(); |
+ } |
+ |
+ MOCK_METHOD0(OnAudioDebugRecordingHelperDestruction, void()); |
+ |
+ protected: |
+ base::FilePath file_path_; |
+ |
+ base::TestMessageLoop message_loop_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(AudioDebugRecordingHelperTest); |
+}; |
+ |
+// Creates a helper with an on destrcution closure, and verifies that it's |
+// run. |
+TEST_F(AudioDebugRecordingHelperTest, TestDestructionClosure) { |
+ const AudioParameters params; |
+ std::unique_ptr<AudioDebugRecordingHelper> recording_helper = |
+ CreateRecordingHelper( |
+ params, base::BindOnce(&AudioDebugRecordingHelperTest:: |
+ OnAudioDebugRecordingHelperDestruction, |
+ base::Unretained(this))); |
+ |
+ EXPECT_CALL(*this, OnAudioDebugRecordingHelperDestruction()); |
+} |
+ |
+// Verifies that disable can be called without being enabled. |
+TEST_F(AudioDebugRecordingHelperTest, OnlyDisable) { |
+ const AudioParameters params; |
+ std::unique_ptr<AudioDebugRecordingHelper> recording_helper = |
+ CreateRecordingHelper(params, base::OnceClosure()); |
+ |
+ recording_helper->DisableDebugRecording(); |
+} |
+ |
+TEST_F(AudioDebugRecordingHelperTest, EnableDisable) { |
+ const AudioParameters params; |
+ std::unique_ptr<AudioDebugRecordingHelper> recording_helper = |
+ CreateRecordingHelper(params, base::OnceClosure()); |
+ |
+ recording_helper->EnableDebugRecording(file_path_); |
+ EXPECT_CALL(*static_cast<MockAudioDebugFileWriter*>( |
+ recording_helper->debug_writer_.get()), |
+ Stop()); |
+ DisableDebugRecording(recording_helper.get()); |
+ |
+ recording_helper->EnableDebugRecording(file_path_); |
+ EXPECT_CALL(*static_cast<MockAudioDebugFileWriter*>( |
+ recording_helper->debug_writer_.get()), |
+ Stop()); |
+ DisableDebugRecording(recording_helper.get()); |
+} |
+ |
+TEST_F(AudioDebugRecordingHelperTest, OnData) { |
+ // Only channel layout and frames per buffer is used in the file writer and |
+ // AudioBus, the other parameters are ignored. |
+ const int number_of_frames = 100; |
+ const AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
+ ChannelLayout::CHANNEL_LAYOUT_STEREO, 0, 0, |
+ number_of_frames); |
+ |
+ // Setup some data. |
+ const int number_of_samples = number_of_frames * params.channels(); |
+ const float step = std::numeric_limits<int16_t>::max() / number_of_frames; |
+ std::unique_ptr<float[]> source_data(new float[number_of_samples]); |
+ for (float i = 0; i < number_of_samples; ++i) |
+ source_data[i] = i * step; |
+ std::unique_ptr<AudioBus> audio_bus = AudioBus::Create(params); |
+ audio_bus->FromInterleaved<Float32SampleTypeTraits>(source_data.get(), |
+ number_of_frames); |
+ |
+ std::unique_ptr<AudioDebugRecordingHelper> recording_helper = |
+ CreateRecordingHelper(params, base::OnceClosure()); |
+ |
+ // Should not do anything. |
+ recording_helper->OnData(audio_bus.get()); |
+ |
+ recording_helper->EnableDebugRecording(file_path_); |
+ MockAudioDebugFileWriter* mock_audio_file_writer = |
+ static_cast<MockAudioDebugFileWriter*>( |
+ recording_helper->debug_writer_.get()); |
+ mock_audio_file_writer->SetReferenceData(audio_bus.get()); |
+ |
+ EXPECT_CALL(*mock_audio_file_writer, DoWrite(_)); |
+ recording_helper->OnData(audio_bus.get()); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ EXPECT_CALL(*mock_audio_file_writer, Stop()); |
+ DisableDebugRecording(recording_helper.get()); |
+ |
+ // Make sure we clear the loop before enabling again. |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ // Enable again, this time with two OnData() calls, one OnData() call |
+ // without running the message loop until after disabling, and one call after |
+ // disabling. |
+ recording_helper->EnableDebugRecording(file_path_); |
+ mock_audio_file_writer = static_cast<MockAudioDebugFileWriter*>( |
+ recording_helper->debug_writer_.get()); |
+ mock_audio_file_writer->SetReferenceData(audio_bus.get()); |
+ |
+ EXPECT_CALL(*mock_audio_file_writer, DoWrite(_)).Times(2); |
+ recording_helper->OnData(audio_bus.get()); |
+ recording_helper->OnData(audio_bus.get()); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ // This call should not yield a DoWrite() call on the mock, since the message |
+ // loop isn't run until after disabling. WillWrite() is expected since |
+ // recording is enabled. |
+ recording_helper->OnData(audio_bus.get()); |
+ |
+ EXPECT_CALL(*mock_audio_file_writer, Stop()); |
+ DisableDebugRecording(recording_helper.get()); |
+ |
+ // This call should not yield a DoWrite() call on the mock either. |
+ recording_helper->OnData(audio_bus.get()); |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+} // namespace media |