OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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 "media/audio/audio_debug_recording_helper.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/files/file_path.h" |
| 9 #include "base/logging.h" |
| 10 #include "base/memory/ptr_util.h" |
| 11 #include "base/run_loop.h" |
| 12 #include "base/strings/string_number_conversions.h" |
| 13 #include "base/test/test_message_loop.h" |
| 14 #include "media/base/audio_bus.h" |
| 15 #include "media/base/audio_sample_types.h" |
| 16 #include "testing/gmock/include/gmock/gmock.h" |
| 17 #include "testing/gtest/include/gtest/gtest.h" |
| 18 |
| 19 using testing::_; |
| 20 using testing::Return; |
| 21 |
| 22 #if defined(OS_WIN) |
| 23 #define IntToStringType base::IntToString16 |
| 24 #else |
| 25 #define IntToStringType base::IntToString |
| 26 #endif |
| 27 |
| 28 namespace media { |
| 29 |
| 30 namespace { |
| 31 |
| 32 // The base file path. |
| 33 const base::FilePath::CharType kBaseFilePath[] = FILE_PATH_LITERAL("file_path"); |
| 34 |
| 35 // The filename extension the mock should return in GetFileNameExtension(). |
| 36 const base::FilePath::CharType kFileNameExtension[] = FILE_PATH_LITERAL("wav"); |
| 37 |
| 38 } // namespace |
| 39 |
| 40 // Mock class for the audio file writer that the helper wraps. |
| 41 class MockAudioDebugFileWriter : public AudioDebugFileWriter { |
| 42 public: |
| 43 MockAudioDebugFileWriter( |
| 44 const AudioParameters& params, |
| 45 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner) |
| 46 : AudioDebugFileWriter(params, std::move(file_task_runner)), |
| 47 reference_data_(nullptr) {} |
| 48 ~MockAudioDebugFileWriter() override {} |
| 49 |
| 50 MOCK_METHOD1(Start, void(const base::FilePath&)); |
| 51 MOCK_METHOD0(Stop, void()); |
| 52 |
| 53 // Functions with move-only types as arguments can't be mocked directly, so |
| 54 // we pass on to DoWrite(). Also, we can verify the data this way. |
| 55 MOCK_METHOD1(DoWrite, void(AudioBus*)); |
| 56 void Write(std::unique_ptr<AudioBus> data) override { |
| 57 CHECK(reference_data_); |
| 58 EXPECT_EQ(reference_data_->channels(), data->channels()); |
| 59 EXPECT_EQ(reference_data_->frames(), data->frames()); |
| 60 for (int i = 0; i < data->channels(); ++i) { |
| 61 float* data_ptr = data->channel(i); |
| 62 float* ref_data_ptr = reference_data_->channel(i); |
| 63 for (int j = 0; j < data->frames(); ++j, ++data_ptr, ++ref_data_ptr) |
| 64 EXPECT_EQ(*ref_data_ptr, *data_ptr); |
| 65 } |
| 66 DoWrite(data.get()); |
| 67 } |
| 68 |
| 69 MOCK_METHOD0(WillWrite, bool()); |
| 70 MOCK_METHOD0(GetFileNameExtension, const base::FilePath::CharType*()); |
| 71 |
| 72 // Set reference data to compare against. Must be called before Write() is |
| 73 // called. |
| 74 void SetReferenceData(AudioBus* reference_data) { |
| 75 EXPECT_EQ(params_.channels(), reference_data->channels()); |
| 76 EXPECT_EQ(params_.frames_per_buffer(), reference_data->frames()); |
| 77 reference_data_ = reference_data; |
| 78 } |
| 79 |
| 80 private: |
| 81 AudioBus* reference_data_; |
| 82 |
| 83 DISALLOW_COPY_AND_ASSIGN(MockAudioDebugFileWriter); |
| 84 }; |
| 85 |
| 86 // Sub-class of the helper that overrides the CreateAudioDebugFileWriter |
| 87 // function to create the above mock instead. |
| 88 class AudioDebugRecordingHelperUnderTest : public AudioDebugRecordingHelper { |
| 89 public: |
| 90 AudioDebugRecordingHelperUnderTest( |
| 91 const AudioParameters& params, |
| 92 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| 93 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner, |
| 94 base::OnceClosure on_destruction_closure) |
| 95 : AudioDebugRecordingHelper(params, |
| 96 std::move(task_runner), |
| 97 std::move(file_task_runner), |
| 98 std::move(on_destruction_closure)) {} |
| 99 ~AudioDebugRecordingHelperUnderTest() override {} |
| 100 |
| 101 private: |
| 102 // Creates the mock writer. After the mock writer is returned, we always |
| 103 // expect GetFileNameExtension() and Start() to be called on it by the helper. |
| 104 std::unique_ptr<AudioDebugFileWriter> CreateAudioDebugFileWriter( |
| 105 const AudioParameters& params, |
| 106 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner) override { |
| 107 MockAudioDebugFileWriter* writer = |
| 108 new MockAudioDebugFileWriter(params, std::move(file_task_runner)); |
| 109 EXPECT_CALL(*writer, GetFileNameExtension()) |
| 110 .WillOnce(Return(kFileNameExtension)); |
| 111 base::FilePath expected_file_path = |
| 112 base::FilePath(kBaseFilePath).AddExtension(kFileNameExtension); |
| 113 EXPECT_CALL(*writer, Start(expected_file_path)); |
| 114 return base::WrapUnique<AudioDebugFileWriter>(writer); |
| 115 } |
| 116 |
| 117 DISALLOW_COPY_AND_ASSIGN(AudioDebugRecordingHelperUnderTest); |
| 118 }; |
| 119 |
| 120 // The test fixture. |
| 121 class AudioDebugRecordingHelperTest : public ::testing::Test { |
| 122 public: |
| 123 AudioDebugRecordingHelperTest() : file_path_(kBaseFilePath) {} |
| 124 |
| 125 ~AudioDebugRecordingHelperTest() override {} |
| 126 |
| 127 // Helper function that creates a recording helper. |
| 128 std::unique_ptr<AudioDebugRecordingHelper> CreateRecordingHelper( |
| 129 const AudioParameters& params, |
| 130 base::OnceClosure on_destruction_closure) { |
| 131 return base::MakeUnique<AudioDebugRecordingHelperUnderTest>( |
| 132 params, message_loop_.task_runner(), message_loop_.task_runner(), |
| 133 std::move(on_destruction_closure)); |
| 134 } |
| 135 |
| 136 // Helper function that unsets the mock writer pointer after disabling. |
| 137 void DisableDebugRecording(AudioDebugRecordingHelper* recording_helper) { |
| 138 recording_helper->DisableDebugRecording(); |
| 139 } |
| 140 |
| 141 MOCK_METHOD0(OnAudioDebugRecordingHelperDestruction, void()); |
| 142 |
| 143 protected: |
| 144 base::FilePath file_path_; |
| 145 |
| 146 base::TestMessageLoop message_loop_; |
| 147 |
| 148 private: |
| 149 DISALLOW_COPY_AND_ASSIGN(AudioDebugRecordingHelperTest); |
| 150 }; |
| 151 |
| 152 // Creates a helper with an on destrcution closure, and verifies that it's |
| 153 // run. |
| 154 TEST_F(AudioDebugRecordingHelperTest, TestDestructionClosure) { |
| 155 const AudioParameters params; |
| 156 std::unique_ptr<AudioDebugRecordingHelper> recording_helper = |
| 157 CreateRecordingHelper( |
| 158 params, base::BindOnce(&AudioDebugRecordingHelperTest:: |
| 159 OnAudioDebugRecordingHelperDestruction, |
| 160 base::Unretained(this))); |
| 161 |
| 162 EXPECT_CALL(*this, OnAudioDebugRecordingHelperDestruction()); |
| 163 } |
| 164 |
| 165 // Verifies that disable can be called without being enabled. |
| 166 TEST_F(AudioDebugRecordingHelperTest, OnlyDisable) { |
| 167 const AudioParameters params; |
| 168 std::unique_ptr<AudioDebugRecordingHelper> recording_helper = |
| 169 CreateRecordingHelper(params, base::OnceClosure()); |
| 170 |
| 171 recording_helper->DisableDebugRecording(); |
| 172 } |
| 173 |
| 174 TEST_F(AudioDebugRecordingHelperTest, EnableDisable) { |
| 175 const AudioParameters params; |
| 176 std::unique_ptr<AudioDebugRecordingHelper> recording_helper = |
| 177 CreateRecordingHelper(params, base::OnceClosure()); |
| 178 |
| 179 recording_helper->EnableDebugRecording(file_path_); |
| 180 EXPECT_CALL(*static_cast<MockAudioDebugFileWriter*>( |
| 181 recording_helper->debug_writer_.get()), |
| 182 Stop()); |
| 183 DisableDebugRecording(recording_helper.get()); |
| 184 |
| 185 recording_helper->EnableDebugRecording(file_path_); |
| 186 EXPECT_CALL(*static_cast<MockAudioDebugFileWriter*>( |
| 187 recording_helper->debug_writer_.get()), |
| 188 Stop()); |
| 189 DisableDebugRecording(recording_helper.get()); |
| 190 } |
| 191 |
| 192 TEST_F(AudioDebugRecordingHelperTest, OnData) { |
| 193 // Only channel layout and frames per buffer is used in the file writer and |
| 194 // AudioBus, the other parameters are ignored. |
| 195 const int number_of_frames = 100; |
| 196 const AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, |
| 197 ChannelLayout::CHANNEL_LAYOUT_STEREO, 0, 0, |
| 198 number_of_frames); |
| 199 |
| 200 // Setup some data. |
| 201 const int number_of_samples = number_of_frames * params.channels(); |
| 202 const float step = std::numeric_limits<int16_t>::max() / number_of_frames; |
| 203 std::unique_ptr<float[]> source_data(new float[number_of_samples]); |
| 204 for (float i = 0; i < number_of_samples; ++i) |
| 205 source_data[i] = i * step; |
| 206 std::unique_ptr<AudioBus> audio_bus = AudioBus::Create(params); |
| 207 audio_bus->FromInterleaved<Float32SampleTypeTraits>(source_data.get(), |
| 208 number_of_frames); |
| 209 |
| 210 std::unique_ptr<AudioDebugRecordingHelper> recording_helper = |
| 211 CreateRecordingHelper(params, base::OnceClosure()); |
| 212 |
| 213 // Should not do anything. |
| 214 recording_helper->OnData(audio_bus.get()); |
| 215 |
| 216 recording_helper->EnableDebugRecording(file_path_); |
| 217 MockAudioDebugFileWriter* mock_audio_file_writer = |
| 218 static_cast<MockAudioDebugFileWriter*>( |
| 219 recording_helper->debug_writer_.get()); |
| 220 mock_audio_file_writer->SetReferenceData(audio_bus.get()); |
| 221 |
| 222 EXPECT_CALL(*mock_audio_file_writer, DoWrite(_)); |
| 223 recording_helper->OnData(audio_bus.get()); |
| 224 base::RunLoop().RunUntilIdle(); |
| 225 |
| 226 EXPECT_CALL(*mock_audio_file_writer, Stop()); |
| 227 DisableDebugRecording(recording_helper.get()); |
| 228 |
| 229 // Make sure we clear the loop before enabling again. |
| 230 base::RunLoop().RunUntilIdle(); |
| 231 |
| 232 // Enable again, this time with two OnData() calls, one OnData() call |
| 233 // without running the message loop until after disabling, and one call after |
| 234 // disabling. |
| 235 recording_helper->EnableDebugRecording(file_path_); |
| 236 mock_audio_file_writer = static_cast<MockAudioDebugFileWriter*>( |
| 237 recording_helper->debug_writer_.get()); |
| 238 mock_audio_file_writer->SetReferenceData(audio_bus.get()); |
| 239 |
| 240 EXPECT_CALL(*mock_audio_file_writer, DoWrite(_)).Times(2); |
| 241 recording_helper->OnData(audio_bus.get()); |
| 242 recording_helper->OnData(audio_bus.get()); |
| 243 base::RunLoop().RunUntilIdle(); |
| 244 |
| 245 // This call should not yield a DoWrite() call on the mock, since the message |
| 246 // loop isn't run until after disabling. WillWrite() is expected since |
| 247 // recording is enabled. |
| 248 recording_helper->OnData(audio_bus.get()); |
| 249 |
| 250 EXPECT_CALL(*mock_audio_file_writer, Stop()); |
| 251 DisableDebugRecording(recording_helper.get()); |
| 252 |
| 253 // This call should not yield a DoWrite() call on the mock either. |
| 254 recording_helper->OnData(audio_bus.get()); |
| 255 base::RunLoop().RunUntilIdle(); |
| 256 } |
| 257 |
| 258 } // namespace media |
OLD | NEW |