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

Side by Side Diff: content/browser/renderer_host/media/audio_output_authorization_handler_unittest.cc

Issue 2424163004: Factor out authorization from AudioRendererHost. (Closed)
Patch Set: Minor fixes. Created 4 years, 1 month 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 // Unit tests for AudioOutputAuthorizationHandler.
6
7 #include "content/browser/renderer_host/media/audio_output_authorization_handler .h"
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/run_loop.h"
12 #include "content/browser/browser_thread_impl.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/test/mock_render_process_host.h"
15 #include "content/public/test/test_browser_context.h"
16 #include "content/public/test/test_browser_thread.h"
17 #include "content/public/test/test_browser_thread_bundle.h"
18 #include "media/audio/audio_device_description.h"
19 #include "media/audio/fake_audio_log_factory.h"
20 #include "media/audio/fake_audio_manager.h"
21 #include "media/base/media_switches.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "url/gurl.h"
25 #include "url/origin.h"
26
27 using ::testing::_;
28
29 namespace content {
30
31 namespace {
32 const int kRenderProcessId = 42;
33 const int kRenderFrameId = 31415;
34 const char kSecurityOriginString[] = "http://localhost";
35 const char kBadSecurityOriginString[] = "about:about";
36 const char kDefaultDeviceId[] = "default";
37 const char kEmptyDeviceId[] = "";
38 const char kInvalidDeviceId[] = "invalid-device-id";
39 const char kSalt[] = "salt";
40
41 struct MockListener {
42 MOCK_METHOD3(MockAuthorizationCallback,
43 void(media::OutputDeviceStatus status,
44 const media::AudioParameters& params,
45 const std::string& id));
46
47 AudioOutputAuthorizationHandler::AuthorizationCompletedCallback
48 GetCallback() {
49 return base::Bind(&MockListener::MockAuthorizationCallback,
50 base::Unretained(this));
51 }
52 };
53
54 url::Origin SecurityOrigin() {
55 return url::Origin(GURL(kSecurityOriginString));
56 }
57
58 url::Origin BadSecurityOrigin() {
59 return url::Origin(GURL(kBadSecurityOriginString));
60 }
61 } // namespace
62
63 class AudioOutputAuthorizationHandlerTest : public testing::Test {
64 public:
65 AudioOutputAuthorizationHandlerTest() {
66 // Not threadsafe, thus before threads:
67 base::CommandLine::ForCurrentProcess()->AppendSwitch(
68 switches::kUseFakeDeviceForMediaStream);
69
70 loop_ =
71 base::MakeUnique<base::MessageLoop>(base::MessageLoop::TYPE_DEFAULT);
72 ui_thread_ = base::MakeUnique<TestBrowserThread>(BrowserThread::UI);
73 io_thread_ = base::MakeUnique<TestBrowserThread>(BrowserThread::IO);
74 audio_thread_ = base::MakeUnique<base::Thread>("AudioThread");
75
76 // Since this is done in content/browser/browser_main_loop.cc
77 // when creating the audio thread, we do it here as well.
78 #if defined(OS_WIN)
79 audio_thread_->init_com_with_mta(true);
80 #endif // defined(OS_WIN)
81
82 CHECK(ui_thread_->Start());
83 CHECK(io_thread_->Start());
84 CHECK(audio_thread_->Start());
85
86 audio_manager_.reset(new media::FakeAudioManager(
87 audio_thread_->task_runner(), audio_thread_->task_runner(),
88 &log_factory_));
89 media_stream_manager_ =
90 base::MakeUnique<MediaStreamManager>(audio_manager_.get());
91 // Make sure everything is done initializing:
92 SyncWithAllThreads();
93 }
94
95 ~AudioOutputAuthorizationHandlerTest() override {
96 SyncWithAllThreads();
97 // media_stream_manager_ must die after threads since it's a
98 // DestructionObserver, so we delete the threads explicitly here.
99 audio_manager_.reset();
100 audio_thread_.reset();
101 io_thread_.reset();
102 ui_thread_.reset();
103 }
104
105 protected:
106 MediaStreamManager* GetMediaStreamManager() {
107 return media_stream_manager_.get();
108 }
109
110 void SyncWithAllThreads() {
111 CHECK(audio_manager_);
112 // New tasks might be posted while we are syncing, but looping 10 times
113 // ought to be enough for anybody.
114 for (int i = 0; i < 10; ++i) {
115 SyncWith(BrowserThread::GetTaskRunnerForThread(BrowserThread::UI));
116 SyncWith(BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
117 SyncWith(audio_thread_->task_runner());
118 }
119 }
120
121 std::string GetRawNondefaultId() {
122 std::string id;
123 BrowserThread::PostTask(
124 BrowserThread::IO, FROM_HERE,
125 base::Bind(
126 &AudioOutputAuthorizationHandlerTest::GetRawNondefaultIdOnIOThread,
127 base::Unretained(this), base::Unretained(&id)));
128 SyncWithAllThreads();
129 return id;
130 }
131
132 private:
133 void SyncWith(scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
134 CHECK(task_runner);
135 CHECK(!task_runner->BelongsToCurrentThread());
136 base::WaitableEvent e = {base::WaitableEvent::ResetPolicy::MANUAL,
137 base::WaitableEvent::InitialState::NOT_SIGNALED};
138 task_runner->PostTask(FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
139 base::Unretained(&e)));
140 e.Wait();
141 }
142
143 void GetRawNondefaultIdOnIOThread(std::string* out) {
144 DCHECK_CURRENTLY_ON(BrowserThread::IO);
145 MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
146 devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
147
148 media_stream_manager_->media_devices_manager()->EnumerateDevices(
149 devices_to_enumerate,
150 base::Bind(
151 [](std::string* out, const MediaDeviceEnumeration& result) {
152 // Index 0 is default, so use 1.
153 *out = result[MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT][1]
154 .device_id;
155 },
156 base::Unretained(out)));
157 }
158
159 // We can't get a separate UI thread with TestBrowserThreadBundle,
160 // so we use TestBrowserThread instead.
161 std::unique_ptr<base::MessageLoop> loop_;
162 std::unique_ptr<TestBrowserThread> ui_thread_;
163 std::unique_ptr<TestBrowserThread> io_thread_;
164 std::unique_ptr<base::Thread> audio_thread_;
165 media::FakeAudioLogFactory log_factory_;
166 media::ScopedAudioManagerPtr audio_manager_;
167 std::unique_ptr<MediaStreamManager> media_stream_manager_;
168
169 DISALLOW_COPY_AND_ASSIGN(AudioOutputAuthorizationHandlerTest);
170 };
171
172 TEST_F(AudioOutputAuthorizationHandlerTest, AuthorizeDefaultDevice_Ok) {
173 MockListener listener;
174 EXPECT_CALL(listener,
175 MockAuthorizationCallback(media::OUTPUT_DEVICE_STATUS_OK, _,
176 kDefaultDeviceId))
177 .Times(1);
178 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
179 base::MakeUnique<AudioOutputAuthorizationHandler>(
180 GetMediaStreamManager(), kRenderProcessId, kSalt);
181
182 BrowserThread::PostTask(
183 BrowserThread::IO, FROM_HERE,
184 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
185 base::Unretained(handler.get()), kRenderFrameId, 0,
186 kDefaultDeviceId, SecurityOrigin(), listener.GetCallback())));
187
188 SyncWithAllThreads();
189 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
190 SyncWithAllThreads();
191 }
192
193 TEST_F(AudioOutputAuthorizationHandlerTest,
194 AuthorizeDefaultDeviceByEmptyId_Ok) {
195 MockListener listener;
196 EXPECT_CALL(listener,
197 MockAuthorizationCallback(media::OUTPUT_DEVICE_STATUS_OK, _,
198 kDefaultDeviceId))
199 .Times(1);
200 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
201 base::MakeUnique<AudioOutputAuthorizationHandler>(
202 GetMediaStreamManager(), kRenderProcessId, kSalt);
203
204 BrowserThread::PostTask(
205 BrowserThread::IO, FROM_HERE,
206 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
207 base::Unretained(handler.get()), kRenderFrameId, 0,
208 kEmptyDeviceId, SecurityOrigin(), listener.GetCallback())));
209
210 SyncWithAllThreads();
211 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
212 SyncWithAllThreads();
213 }
214
215 TEST_F(AudioOutputAuthorizationHandlerTest,
216 AuthorizeNondefaultDeviceIdWithoutPermission_NotAuthorized) {
217 std::string raw_nondefault_id = GetRawNondefaultId();
218 std::string hashed_id = MediaStreamManager::GetHMACForMediaDeviceID(
219 kSalt, SecurityOrigin(), raw_nondefault_id);
220
221 MockListener listener;
222 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
223 base::MakeUnique<AudioOutputAuthorizationHandler>(
224 GetMediaStreamManager(), kRenderProcessId, kSalt);
225 BrowserThread::PostTask(
226 BrowserThread::IO, FROM_HERE,
227 base::Bind(
228 &AudioOutputAuthorizationHandler::OverridePermissionsForTesting,
229 base::Unretained(handler.get()), false));
230 SyncWithAllThreads();
231
232 EXPECT_CALL(listener, MockAuthorizationCallback(
233 media::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED, _,
234 std::string()))
235 .Times(1);
236
237 BrowserThread::PostTask(
238 BrowserThread::IO, FROM_HERE,
239 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
240 base::Unretained(handler.get()), kRenderFrameId, 0, hashed_id,
241 SecurityOrigin(), listener.GetCallback())));
242
243 SyncWithAllThreads();
244 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
245 SyncWithAllThreads();
246 }
247
248 TEST_F(AudioOutputAuthorizationHandlerTest,
249 AuthorizeNondefaultDeviceIdWithPermission_Ok) {
250 std::string raw_nondefault_id = GetRawNondefaultId();
251 std::string hashed_id = MediaStreamManager::GetHMACForMediaDeviceID(
252 kSalt, SecurityOrigin(), raw_nondefault_id);
253 MockListener listener;
254 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
255 base::MakeUnique<AudioOutputAuthorizationHandler>(
256 GetMediaStreamManager(), kRenderProcessId, kSalt);
257 BrowserThread::PostTask(
258 BrowserThread::IO, FROM_HERE,
259 base::Bind(
260 &AudioOutputAuthorizationHandler::OverridePermissionsForTesting,
261 base::Unretained(handler.get()), true));
262
263 EXPECT_CALL(listener,
264 MockAuthorizationCallback(media::OUTPUT_DEVICE_STATUS_OK, _,
265 raw_nondefault_id))
266 .Times(1);
267
268 BrowserThread::PostTask(
269 BrowserThread::IO, FROM_HERE,
270 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
271 base::Unretained(handler.get()), kRenderFrameId, 0, hashed_id,
272 SecurityOrigin(), listener.GetCallback())));
273
274 SyncWithAllThreads();
275 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
276 SyncWithAllThreads();
277 }
278
279 TEST_F(AudioOutputAuthorizationHandlerTest,
280 AuthorizeInvalidDeviceId_BadMessage) {
281 std::unique_ptr<TestBrowserContext> context;
282 std::unique_ptr<MockRenderProcessHost> RPH;
283 BrowserThread::PostTask(
284 BrowserThread::UI, FROM_HERE,
285 base::Bind(
286 [](std::unique_ptr<TestBrowserContext>* context,
287 std::unique_ptr<MockRenderProcessHost>* RPH) {
288 context->reset(new TestBrowserContext());
289 RPH->reset(new MockRenderProcessHost(context->get()));
290 },
291 base::Unretained(&context), base::Unretained(&RPH)));
292 SyncWithAllThreads();
293 MockListener listener;
294 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
295 base::MakeUnique<AudioOutputAuthorizationHandler>(GetMediaStreamManager(),
296 RPH->GetID(), kSalt);
297 EXPECT_EQ(RPH->bad_msg_count(), 0);
298
299 EXPECT_CALL(listener, MockAuthorizationCallback(_, _, _)).Times(0);
300
301 BrowserThread::PostTask(
302 BrowserThread::IO, FROM_HERE,
303 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
304 base::Unretained(handler.get()), kRenderFrameId, 0,
305 kInvalidDeviceId, SecurityOrigin(), listener.GetCallback())));
306
307 SyncWithAllThreads();
308 EXPECT_EQ(RPH->bad_msg_count(), 1);
309 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
310 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, RPH.release());
311 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, context.release());
312 SyncWithAllThreads();
313 }
314
315 TEST_F(AudioOutputAuthorizationHandlerTest,
316 AuthorizeNondefaultDeviceIdWithBadOrigin_BadMessage) {
317 std::string raw_nondefault_id = GetRawNondefaultId();
318 std::string hashed_id = MediaStreamManager::GetHMACForMediaDeviceID(
319 kSalt, SecurityOrigin(), raw_nondefault_id);
320 std::unique_ptr<TestBrowserContext> context;
321 std::unique_ptr<MockRenderProcessHost> RPH;
322 BrowserThread::PostTask(
323 BrowserThread::UI, FROM_HERE,
324 base::Bind(
325 [](std::unique_ptr<TestBrowserContext>* context,
326 std::unique_ptr<MockRenderProcessHost>* RPH) {
327 context->reset(new TestBrowserContext());
328 RPH->reset(new MockRenderProcessHost(context->get()));
329 },
330 base::Unretained(&context), base::Unretained(&RPH)));
331 SyncWithAllThreads();
332 MockListener listener;
333 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
334 base::MakeUnique<AudioOutputAuthorizationHandler>(GetMediaStreamManager(),
335 RPH->GetID(), kSalt);
336
337 EXPECT_EQ(RPH->bad_msg_count(), 0);
338 EXPECT_CALL(listener, MockAuthorizationCallback(_, _, _)).Times(0);
339
340 BrowserThread::PostTask(
341 BrowserThread::IO, FROM_HERE,
342 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
343 base::Unretained(handler.get()), kRenderFrameId, 0, hashed_id,
344 BadSecurityOrigin(), listener.GetCallback())));
345
346 SyncWithAllThreads();
347 EXPECT_EQ(RPH->bad_msg_count(), 1);
348 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
349 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, RPH.release());
350 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, context.release());
351 SyncWithAllThreads();
352 }
353
354 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698