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