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

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

Powered by Google App Engine
This is Rietveld 408576698