OLD | NEW |
---|---|
(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 | |
OLD | NEW |