Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(325)

Side by Side Diff: content/renderer/media/audio_renderer_sink_cache_unittest.cc

Issue 1942803002: Caching AudioOutputDevice instances in mixer manager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase, fix for sleep() compile error on win and a bit of cleanup around timeouts. Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
o1ka 2016/05/11 10:17:19 Need to fix the copyright
o1ka 2016/05/17 17:17:24 Done.
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 "base/bind.h"
6 #include "base/bind_helpers.h"
7 #include "base/logging.h"
8 #include "base/test/test_simple_task_runner.h"
9 #include "content/renderer/media/audio_renderer_sink_cache_impl.h"
10 #include "media/audio/audio_device_description.h"
11 #include "media/base/audio_parameters.h"
12 #include "media/base/mock_audio_renderer_sink.h"
13 #include "media/base/test_helpers.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "url/gurl.h"
17
18 namespace content {
19
20 namespace {
21 static const char* const kDefaultDeviceId =
22 media::AudioDeviceDescription::kDefaultDeviceId;
23 static const char kAnotherDeviceId[] = "another-device-id";
24 static const int kRenderFrameId = 124;
25 } // namespace
26
27 class AudioRendererSinkCacheTest : public testing::Test {
28 public:
29 AudioRendererSinkCacheTest()
30 : cache_(new AudioRendererSinkCacheImpl(
31 message_loop_.task_runner(),
32 base::Bind(&AudioRendererSinkCacheTest::CreateSink,
33 base::Unretained(this)))) {}
34
35 void GetSink(int render_frame_id,
36 const std::string& device_id,
37 const url::Origin& security_origin,
38 media::AudioRendererSink** sink) {
39 *sink = cache_->GetSink(render_frame_id, device_id, security_origin);
40 }
41
42 protected:
43 int sink_count() {
44 DCHECK(message_loop_.task_runner()->BelongsToCurrentThread());
45 return cache_->sinks_.size();
46 }
47
48 int delete_timeout_ms() {
49 return AudioRendererSinkCacheImpl::kDeleteTimeoutMs;
50 }
51
52 scoped_refptr<media::AudioRendererSink> CreateSink(
53 int render_frame_id,
54 int session_id,
55 const std::string& device_id,
56 const url::Origin& security_origin) {
57 return new media::MockAudioRendererSink(device_id,
58 media::OUTPUT_DEVICE_STATUS_OK);
59 }
60
61 void ExpectToStop(media::AudioRendererSink* sink) {
62 // The sink must be stoped before deletion.
63 EXPECT_CALL(*static_cast<media::MockAudioRendererSink*>(sink), Stop())
64 .Times(1);
65 }
66
67 // Posts the task to the specified thread and runs current message loop until
68 // the task is completed.
69 void PostAndRunUntilDone(const base::Thread& thread,
70 const base::Closure& task) {
71 media::WaitableMessageLoopEvent event;
72 thread.task_runner()->PostTaskAndReply(FROM_HERE, task, event.GetClosure());
73 // Runs the loop and waits for the thread to call event's closure.
74 event.RunAndWait();
75 }
76
77 void WaitOnAnotherThread(const base::Thread& thread, int timeout_ms) {
78 PostAndRunUntilDone(
79 thread, base::Bind(base::IgnoreResult(&base::PlatformThread::Sleep),
80 base::TimeDelta::FromMilliseconds(timeout_ms)));
81 }
82
83 base::MessageLoop message_loop_;
84 std::unique_ptr<AudioRendererSinkCacheImpl> cache_;
85
86 private:
87 DISALLOW_COPY_AND_ASSIGN(AudioRendererSinkCacheTest);
88 };
89
90 // Verify that normal get/release sink sequence works.
91 TEST_F(AudioRendererSinkCacheTest, GetReleaseSink) {
92 // Verify that a new sink is successfully created.
93 EXPECT_EQ(0, sink_count());
94 media::AudioRendererSink* sink =
95 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin());
96 ExpectToStop(sink);
97 EXPECT_EQ(kDefaultDeviceId, sink->GetOutputDeviceInfo().device_id());
98 EXPECT_EQ(1, sink_count());
99
100 // Verify that another sink with the same key is successfully created
101 media::AudioRendererSink* another_sink =
102 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin());
103 ExpectToStop(another_sink);
104 EXPECT_EQ(kDefaultDeviceId, another_sink->GetOutputDeviceInfo().device_id());
105 EXPECT_EQ(2, sink_count());
106 EXPECT_NE(sink, another_sink);
107
108 // Verify that another sink with a different kay is successfully created.
109 media::AudioRendererSink* yet_another_sink =
110 cache_->GetSink(kRenderFrameId, kAnotherDeviceId, url::Origin());
111 ExpectToStop(yet_another_sink);
112 EXPECT_EQ(kAnotherDeviceId,
113 yet_another_sink->GetOutputDeviceInfo().device_id());
114 EXPECT_EQ(3, sink_count());
115 EXPECT_NE(sink, yet_another_sink);
116 EXPECT_NE(another_sink, yet_another_sink);
117
118 // Verify that the first sink is successfully deleted.
119 cache_->ReleaseSink(sink);
120 EXPECT_EQ(2, sink_count());
121 sink = nullptr;
122
123 // Make sure we deleted the right sink, and the memory for the rest is not
124 // corrupted.
125 EXPECT_EQ(kDefaultDeviceId, another_sink->GetOutputDeviceInfo().device_id());
126 EXPECT_EQ(kAnotherDeviceId,
127 yet_another_sink->GetOutputDeviceInfo().device_id());
128
129 // Verify that the second sink is successfully deleted.
130 cache_->ReleaseSink(another_sink);
131 EXPECT_EQ(1, sink_count());
132 EXPECT_EQ(kAnotherDeviceId,
133 yet_another_sink->GetOutputDeviceInfo().device_id());
134
135 cache_->ReleaseSink(yet_another_sink);
136 EXPECT_EQ(0, sink_count());
137 }
138
139 // Verify that all unstopped sinks will be stopped on cache destuction.
140 TEST_F(AudioRendererSinkCacheTest, StopSinksOnDestruction) {
141 EXPECT_EQ(0, sink_count());
142 media::AudioRendererSink* sink =
143 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin());
144 ExpectToStop(sink);
145 EXPECT_EQ(kDefaultDeviceId, sink->GetOutputDeviceInfo().device_id());
146 EXPECT_EQ(1, sink_count());
147
148 // Verify that another sink with the same key is successfully created
149 media::AudioRendererSink* another_sink =
150 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin());
151 ExpectToStop(another_sink);
152 EXPECT_EQ(kDefaultDeviceId, another_sink->GetOutputDeviceInfo().device_id());
153 EXPECT_EQ(2, sink_count());
154 EXPECT_NE(sink, another_sink);
155
156 cache_.reset(nullptr);
157 }
158
159 // Verify that the sink created with GetSinkInfo() is reused when possible.
160 TEST_F(AudioRendererSinkCacheTest, GetDeviceInfo) {
161 EXPECT_EQ(0, sink_count());
162 media::OutputDeviceInfo device_info =
163 cache_->GetSinkInfo(kRenderFrameId, 0, kDefaultDeviceId, url::Origin());
164 EXPECT_EQ(1, sink_count());
165
166 // The info on the same device is requested, so no new sink is created.
167 media::OutputDeviceInfo one_more_device_info =
168 cache_->GetSinkInfo(kRenderFrameId, 0, kDefaultDeviceId, url::Origin());
169 EXPECT_EQ(1, sink_count());
170 EXPECT_EQ(device_info.device_id(), one_more_device_info.device_id());
171
172 // Aquire the sink that was created on GetSinkInfo().
173 media::AudioRendererSink* sink =
174 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin());
175 ExpectToStop(sink);
176 EXPECT_EQ(1, sink_count());
177 EXPECT_EQ(device_info.device_id(), sink->GetOutputDeviceInfo().device_id());
178
179 // Now the sink is in used, but we can still get the device info out of it, no
180 // new sink is created.
181 one_more_device_info =
182 cache_->GetSinkInfo(kRenderFrameId, 0, kDefaultDeviceId, url::Origin());
183 EXPECT_EQ(1, sink_count());
184 EXPECT_EQ(device_info.device_id(), one_more_device_info.device_id());
185
186 // Request sink for the same device. The first sink is in use, so a new one
187 // should be created.
188 media::AudioRendererSink* another_sink =
189 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin());
190 ExpectToStop(another_sink);
191 EXPECT_EQ(2, sink_count());
192 EXPECT_EQ(device_info.device_id(),
193 another_sink->GetOutputDeviceInfo().device_id());
194 }
195
196 // Verify that the sink created with GetSinkInfo() is deleted if unused.
197 // The test produces 2 "Uninteresting mock" warnings for
198 // MockAudioRendererSink::Stop().
199 TEST_F(AudioRendererSinkCacheTest, GarbageCollection) {
200 EXPECT_EQ(0, sink_count());
201 media::OutputDeviceInfo device_info =
202 cache_->GetSinkInfo(kRenderFrameId, 0, kDefaultDeviceId, url::Origin());
203 EXPECT_EQ(1, sink_count());
204
205 media::OutputDeviceInfo another_device_info =
206 cache_->GetSinkInfo(kRenderFrameId, 0, kAnotherDeviceId, url::Origin());
207 EXPECT_EQ(2, sink_count());
208
209 base::Thread thread("timeout_thread");
210 thread.Start();
211
212 // 0.5 second more than garbage collection timeout.
213 WaitOnAnotherThread(thread, delete_timeout_ms() + 500);
214
215 // All the sinks should be garbage-collected by now.
216 EXPECT_EQ(0, sink_count());
217 }
218
219 // Verify that the sink created with GetSinkInfo() is not deleted if used within
220 // the timeout.
221 TEST_F(AudioRendererSinkCacheTest, NoGarbageCollectionForUsedSink) {
222 EXPECT_EQ(0, sink_count());
223 media::OutputDeviceInfo device_info =
224 cache_->GetSinkInfo(kRenderFrameId, 0, kDefaultDeviceId, url::Origin());
225 EXPECT_EQ(1, sink_count());
226
227 base::Thread thread("timeout_thread");
228 thread.Start();
229
230 // Wait significantly less than grabage collection timeout.
231 int wait_a_bit = 1000;
232 DCHECK_GT(delete_timeout_ms(), wait_a_bit * 2);
233 WaitOnAnotherThread(thread, wait_a_bit);
234
235 // Sink is not deleted yet.
236 EXPECT_EQ(1, sink_count());
237
238 // Request it:
239 media::AudioRendererSink* sink =
240 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin());
241 ExpectToStop(sink);
242 EXPECT_EQ(1, sink_count());
243
244 // Wait more to hit garbage collection timeout.
245 WaitOnAnotherThread(thread, delete_timeout_ms());
246
247 // The sink is still in place.
248 EXPECT_EQ(1, sink_count());
249 }
250
251 // Verify that cache works fine if a sink scheduled for delettion is aquired and
252 // released before deletion timeout elapses.
253 // The test produces a "Uninteresting mock" warning for
254 // MockAudioRendererSink::Stop().
255 TEST_F(AudioRendererSinkCacheTest, ReleaseSinkBeforeScheduledDeletion) {
256 EXPECT_EQ(0, sink_count());
257
258 media::OutputDeviceInfo device_info =
259 cache_->GetSinkInfo(kRenderFrameId, 0, kDefaultDeviceId, url::Origin());
260 EXPECT_EQ(1, sink_count()); // This sink is scheduled for deletion now.
261
262 // Request it:
263 media::AudioRendererSink* sink =
264 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin());
265 ExpectToStop(sink);
266 EXPECT_EQ(1, sink_count());
267
268 // Release it:
269 cache_->ReleaseSink(sink);
270 EXPECT_EQ(0, sink_count());
271
272 media::OutputDeviceInfo another_device_info =
273 cache_->GetSinkInfo(kRenderFrameId, 0, kAnotherDeviceId, url::Origin());
274 EXPECT_EQ(1, sink_count()); // This sink is scheduled for deletion now.
275
276 base::Thread thread("timeout_thread");
277 thread.Start();
278
279 // 0.5 second more than garbage collection timeout.
280 WaitOnAnotherThread(thread, delete_timeout_ms() + 500);
281
282 // Nothing crashed and the second sink deleted on schedule.
283 EXPECT_EQ(0, sink_count());
284 }
285
286 // Check that a sink created on one thread in responce to GetSinkInfo can be
287 // used on another thread.
288 TEST_F(AudioRendererSinkCacheTest, MultithreadedAccess) {
289 EXPECT_EQ(0, sink_count());
290
291 base::Thread thread1("thread1");
292 thread1.Start();
293
294 base::Thread thread2("thread2");
295 thread2.Start();
296
297 // Request device information on the first thread.
298 PostAndRunUntilDone(
299 thread1,
300 base::Bind(base::IgnoreResult(&AudioRendererSinkCacheImpl::GetSinkInfo),
301 base::Unretained(cache_.get()), kRenderFrameId, 0,
302 kDefaultDeviceId, url::Origin()));
303
304 EXPECT_EQ(1, sink_count());
305
306 // Request the sink on the second thread.
307 media::AudioRendererSink* sink = nullptr;
308
309 PostAndRunUntilDone(
310 thread2,
311 base::Bind(&AudioRendererSinkCacheTest::GetSink, base::Unretained(this),
312 kRenderFrameId, kDefaultDeviceId, url::Origin(), &sink));
313
314 EXPECT_EQ(kDefaultDeviceId, sink->GetOutputDeviceInfo().device_id());
315 ExpectToStop(sink);
316 EXPECT_EQ(1, sink_count());
317
318 // Request device information on the first thread again.
319 PostAndRunUntilDone(
320 thread1,
321 base::Bind(base::IgnoreResult(&AudioRendererSinkCacheImpl::GetSinkInfo),
322 base::Unretained(cache_.get()), kRenderFrameId, 0,
323 kDefaultDeviceId, url::Origin()));
324 EXPECT_EQ(1, sink_count());
325
326 // Release the sink on the second thread.
327 PostAndRunUntilDone(thread2,
328 base::Bind(&AudioRendererSinkCache::ReleaseSink,
329 base::Unretained(cache_.get()), sink));
330
331 EXPECT_EQ(0, sink_count());
332 }
333
334 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698