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

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: dalecurtis@'s comments addressed, build issue fixed 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 2016 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 "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 static const int kDeleteTimeoutMs = 1000;
26 } // namespace
27
28 class AudioRendererSinkCacheTest : public testing::Test {
29 public:
30 AudioRendererSinkCacheTest()
31 : cache_(new AudioRendererSinkCacheImpl(
32 message_loop_.task_runner(),
33 base::Bind(&AudioRendererSinkCacheTest::CreateSink,
34 base::Unretained(this)),
35 kDeleteTimeoutMs)) {}
36
37 void GetSink(int render_frame_id,
38 const std::string& device_id,
39 const url::Origin& security_origin,
40 media::AudioRendererSink** sink) {
41 *sink = cache_->GetSink(render_frame_id, device_id, security_origin).get();
42 }
43
44 protected:
45 int sink_count() {
46 DCHECK(message_loop_.task_runner()->BelongsToCurrentThread());
47 return cache_->sinks_.size();
48 }
49
50 scoped_refptr<media::AudioRendererSink> CreateSink(
51 int render_frame_id,
52 int session_id,
53 const std::string& device_id,
54 const url::Origin& security_origin) {
55 return new media::MockAudioRendererSink(device_id,
56 media::OUTPUT_DEVICE_STATUS_OK);
57 }
58
59 void ExpectToStop(media::AudioRendererSink* sink) {
60 // The sink must be stoped before deletion.
61 EXPECT_CALL(*static_cast<media::MockAudioRendererSink*>(sink), Stop())
62 .Times(1);
63 }
64
65 // Posts the task to the specified thread and runs current message loop until
66 // the task is completed.
67 void PostAndRunUntilDone(const base::Thread& thread,
68 const base::Closure& task) {
69 media::WaitableMessageLoopEvent event;
70 thread.task_runner()->PostTaskAndReply(FROM_HERE, task, event.GetClosure());
71 // Runs the loop and waits for the thread to call event's closure.
72 event.RunAndWait();
73 }
74
75 void WaitOnAnotherThread(const base::Thread& thread, int timeout_ms) {
76 PostAndRunUntilDone(
77 thread, base::Bind(base::IgnoreResult(&base::PlatformThread::Sleep),
78 base::TimeDelta::FromMilliseconds(timeout_ms)));
79 }
80
81 base::MessageLoop message_loop_;
82 std::unique_ptr<AudioRendererSinkCacheImpl> cache_;
83
84 private:
85 DISALLOW_COPY_AND_ASSIGN(AudioRendererSinkCacheTest);
86 };
87
88 // Verify that normal get/release sink sequence works.
89 TEST_F(AudioRendererSinkCacheTest, GetReleaseSink) {
90 // Verify that a new sink is successfully created.
91 EXPECT_EQ(0, sink_count());
92 media::AudioRendererSink* sink =
93 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin()).get();
94 ExpectToStop(sink);
95 EXPECT_EQ(kDefaultDeviceId, sink->GetOutputDeviceInfo().device_id());
96 EXPECT_EQ(1, sink_count());
97
98 // Verify that another sink with the same key is successfully created
99 media::AudioRendererSink* another_sink =
100 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin()).get();
101 ExpectToStop(another_sink);
102 EXPECT_EQ(kDefaultDeviceId, another_sink->GetOutputDeviceInfo().device_id());
103 EXPECT_EQ(2, sink_count());
104 EXPECT_NE(sink, another_sink);
105
106 // Verify that another sink with a different kay is successfully created.
107 media::AudioRendererSink* yet_another_sink =
108 cache_->GetSink(kRenderFrameId, kAnotherDeviceId, url::Origin()).get();
109 ExpectToStop(yet_another_sink);
110 EXPECT_EQ(kAnotherDeviceId,
111 yet_another_sink->GetOutputDeviceInfo().device_id());
112 EXPECT_EQ(3, sink_count());
113 EXPECT_NE(sink, yet_another_sink);
114 EXPECT_NE(another_sink, yet_another_sink);
115
116 // Verify that the first sink is successfully deleted.
117 cache_->ReleaseSink(kRenderFrameId, kDefaultDeviceId, url::Origin(), sink);
118 EXPECT_EQ(2, sink_count());
119 sink = nullptr;
120
121 // Make sure we deleted the right sink, and the memory for the rest is not
122 // corrupted.
123 EXPECT_EQ(kDefaultDeviceId, another_sink->GetOutputDeviceInfo().device_id());
124 EXPECT_EQ(kAnotherDeviceId,
125 yet_another_sink->GetOutputDeviceInfo().device_id());
126
127 // Verify that the second sink is successfully deleted.
128 cache_->ReleaseSink(kRenderFrameId, kDefaultDeviceId, url::Origin(),
129 another_sink);
130 EXPECT_EQ(1, sink_count());
131 EXPECT_EQ(kAnotherDeviceId,
132 yet_another_sink->GetOutputDeviceInfo().device_id());
133
134 cache_->ReleaseSink(kRenderFrameId, kAnotherDeviceId, url::Origin(),
135 yet_another_sink);
136 EXPECT_EQ(0, sink_count());
137 }
138
139 // Verify that the sink created with GetSinkInfo() is reused when possible.
140 TEST_F(AudioRendererSinkCacheTest, GetDeviceInfo) {
141 EXPECT_EQ(0, sink_count());
142 media::OutputDeviceInfo device_info =
143 cache_->GetSinkInfo(kRenderFrameId, 0, kDefaultDeviceId, url::Origin());
144 EXPECT_EQ(1, sink_count());
145
146 // The info on the same device is requested, so no new sink is created.
147 media::OutputDeviceInfo one_more_device_info =
148 cache_->GetSinkInfo(kRenderFrameId, 0, kDefaultDeviceId, url::Origin());
149 EXPECT_EQ(1, sink_count());
150 EXPECT_EQ(device_info.device_id(), one_more_device_info.device_id());
151
152 // Aquire the sink that was created on GetSinkInfo().
153 media::AudioRendererSink* sink =
154 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin()).get();
155 EXPECT_EQ(1, sink_count());
156 EXPECT_EQ(device_info.device_id(), sink->GetOutputDeviceInfo().device_id());
157
158 // Now the sink is in used, but we can still get the device info out of it, no
159 // new sink is created.
160 one_more_device_info =
161 cache_->GetSinkInfo(kRenderFrameId, 0, kDefaultDeviceId, url::Origin());
162 EXPECT_EQ(1, sink_count());
163 EXPECT_EQ(device_info.device_id(), one_more_device_info.device_id());
164
165 // Request sink for the same device. The first sink is in use, so a new one
166 // should be created.
167 media::AudioRendererSink* another_sink =
168 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin()).get();
169 EXPECT_EQ(2, sink_count());
170 EXPECT_EQ(device_info.device_id(),
171 another_sink->GetOutputDeviceInfo().device_id());
172 }
173
174 // Verify that the sink created with GetSinkInfo() is deleted if unused.
175 // The test produces 2 "Uninteresting mock" warnings for
176 // MockAudioRendererSink::Stop().
177 TEST_F(AudioRendererSinkCacheTest, GarbageCollection) {
178 EXPECT_EQ(0, sink_count());
179 media::OutputDeviceInfo device_info =
180 cache_->GetSinkInfo(kRenderFrameId, 0, kDefaultDeviceId, url::Origin());
181 EXPECT_EQ(1, sink_count());
182
183 media::OutputDeviceInfo another_device_info =
184 cache_->GetSinkInfo(kRenderFrameId, 0, kAnotherDeviceId, url::Origin());
185 EXPECT_EQ(2, sink_count());
186
187 base::Thread thread("timeout_thread");
188 thread.Start();
189
190 // 100 ms more than garbage collection timeout.
191 WaitOnAnotherThread(thread, kDeleteTimeoutMs + 100);
192
193 // All the sinks should be garbage-collected by now.
194 EXPECT_EQ(0, sink_count());
195 }
196
197 // Verify that the sink created with GetSinkInfo() is not deleted if used within
198 // the timeout.
199 TEST_F(AudioRendererSinkCacheTest, NoGarbageCollectionForUsedSink) {
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 base::Thread thread("timeout_thread");
206 thread.Start();
207
208 // Wait significantly less than grabage collection timeout.
209 int wait_a_bit = 100;
210 DCHECK_GT(kDeleteTimeoutMs, wait_a_bit * 2);
211 WaitOnAnotherThread(thread, wait_a_bit);
212
213 // Sink is not deleted yet.
214 EXPECT_EQ(1, sink_count());
215
216 // Request it:
217 media::AudioRendererSink* sink =
218 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin()).get();
219 EXPECT_EQ(kDefaultDeviceId, sink->GetOutputDeviceInfo().device_id());
220 EXPECT_EQ(1, sink_count());
221
222 // Wait more to hit garbage collection timeout.
223 WaitOnAnotherThread(thread, kDeleteTimeoutMs);
224
225 // The sink is still in place.
226 EXPECT_EQ(1, sink_count());
227 }
228
229 // Verify that cache works fine if a sink scheduled for delettion is aquired and
230 // released before deletion timeout elapses.
231 // The test produces one "Uninteresting mock" warning for
232 // MockAudioRendererSink::Stop().
233 TEST_F(AudioRendererSinkCacheTest, ReleaseSinkBeforeScheduledDeletion) {
234 EXPECT_EQ(0, sink_count());
235
236 media::OutputDeviceInfo device_info =
237 cache_->GetSinkInfo(kRenderFrameId, 0, kDefaultDeviceId, url::Origin());
238 EXPECT_EQ(1, sink_count()); // This sink is scheduled for deletion now.
239
240 // Request it:
241 media::AudioRendererSink* sink =
242 cache_->GetSink(kRenderFrameId, kDefaultDeviceId, url::Origin()).get();
243 ExpectToStop(sink);
244 EXPECT_EQ(1, sink_count());
245
246 // Release it:
247 cache_->ReleaseSink(kRenderFrameId, kDefaultDeviceId, url::Origin(), sink);
248 EXPECT_EQ(0, sink_count());
249
250 media::OutputDeviceInfo another_device_info =
251 cache_->GetSinkInfo(kRenderFrameId, 0, kAnotherDeviceId, url::Origin());
252 EXPECT_EQ(1, sink_count()); // This sink is scheduled for deletion now.
253
254 base::Thread thread("timeout_thread");
255 thread.Start();
256
257 // 100 ms more than garbage collection timeout.
258 WaitOnAnotherThread(thread, kDeleteTimeoutMs + 100);
259
260 // Nothing crashed and the second sink deleted on schedule.
261 EXPECT_EQ(0, sink_count());
262 }
263
264 // Check that a sink created on one thread in responce to GetSinkInfo can be
miu 2016/05/19 22:27:15 s/responce/response/
o1ka 2016/05/23 16:16:54 Done.
265 // used on another thread.
266 TEST_F(AudioRendererSinkCacheTest, MultithreadedAccess) {
267 EXPECT_EQ(0, sink_count());
268
269 base::Thread thread1("thread1");
270 thread1.Start();
271
272 base::Thread thread2("thread2");
273 thread2.Start();
274
275 // Request device information on the first thread.
276 PostAndRunUntilDone(
277 thread1,
278 base::Bind(base::IgnoreResult(&AudioRendererSinkCacheImpl::GetSinkInfo),
279 base::Unretained(cache_.get()), kRenderFrameId, 0,
280 kDefaultDeviceId, url::Origin()));
281
282 EXPECT_EQ(1, sink_count());
283
284 // Request the sink on the second thread.
285 media::AudioRendererSink* sink = nullptr;
286
287 PostAndRunUntilDone(
288 thread2,
289 base::Bind(&AudioRendererSinkCacheTest::GetSink, base::Unretained(this),
290 kRenderFrameId, kDefaultDeviceId, url::Origin(), &sink));
291
292 EXPECT_EQ(kDefaultDeviceId, sink->GetOutputDeviceInfo().device_id());
293 ExpectToStop(sink);
294 EXPECT_EQ(1, sink_count());
295
296 // Request device information on the first thread again.
297 PostAndRunUntilDone(
298 thread1,
299 base::Bind(base::IgnoreResult(&AudioRendererSinkCacheImpl::GetSinkInfo),
300 base::Unretained(cache_.get()), kRenderFrameId, 0,
301 kDefaultDeviceId, url::Origin()));
302 EXPECT_EQ(1, sink_count());
303
304 // Release the sink on the second thread.
305 PostAndRunUntilDone(thread2,
306 base::Bind(&AudioRendererSinkCache::ReleaseSink,
307 base::Unretained(cache_.get()), kRenderFrameId,
308 kDefaultDeviceId, url::Origin(), sink));
309
310 EXPECT_EQ(0, sink_count());
311 }
312
313 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698