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

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: Don't use separate UI thread for test. 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.
o1ka 2016/11/14 13:24:04 Awesome that you've done all that testing!
Max Morin 2016/11/16 08:49:49 :)
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 set before threads are started:
67 base::CommandLine::ForCurrentProcess()->AppendSwitch(
68 switches::kUseFakeDeviceForMediaStream);
69
70 thread_bundle_ = base::MakeUnique<TestBrowserThreadBundle>(
71 TestBrowserThreadBundle::Options::REAL_IO_THREAD);
72 audio_thread_ = base::MakeUnique<base::Thread>("AudioThread");
o1ka 2016/11/14 13:24:04 make audio_thread_ same as UI thread on Mac?
Max Morin 2016/11/16 08:49:49 Done.
73
74 // Since this is done in content/browser/browser_main_loop.cc when creating the
75 // audio thread, we do it here as well. Since the audio manager is fake, we
76 // don't need to special-case mac here.
77 #if defined(OS_WIN)
78 audio_thread_->init_com_with_mta(true);
79 #endif // defined(OS_WIN)
80
81 CHECK(audio_thread_->Start());
82
83 audio_manager_.reset(new media::FakeAudioManager(
84 audio_thread_->task_runner(), audio_thread_->task_runner(),
85 &log_factory_));
86 media_stream_manager_ =
87 base::MakeUnique<MediaStreamManager>(audio_manager_.get());
88 // Make sure everything is done initializing:
89 SyncWithAllThreads();
90 }
91
92 ~AudioOutputAuthorizationHandlerTest() override {
93 SyncWithAllThreads();
94 // media_stream_manager_ must die after threads since it's a
95 // DestructionObserver, so we delete the threads explicitly here.
o1ka 2016/11/14 13:24:04 can you do it by just declaring them in the correc
Max Morin 2016/11/16 08:49:49 Done.
96 audio_manager_.reset();
97 audio_thread_.reset();
98 thread_bundle_.reset();
99 }
100
101 protected:
102 MediaStreamManager* GetMediaStreamManager() {
103 return media_stream_manager_.get();
104 }
105
106 void SyncWithAllThreads() {
107 CHECK(audio_manager_);
108 // New tasks might be posted while we are syncing, but looping 10 times
109 // ought to be enough for anybody.
110 for (int i = 0; i < 10; ++i) {
111 base::RunLoop().RunUntilIdle();
112 SyncWith(BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
113 SyncWith(audio_thread_->task_runner());
114 }
115 }
116
117 std::string GetRawNondefaultId() {
118 std::string id;
119 BrowserThread::PostTask(
120 BrowserThread::IO, FROM_HERE,
121 base::Bind(
122 &AudioOutputAuthorizationHandlerTest::GetRawNondefaultIdOnIOThread,
123 base::Unretained(this), base::Unretained(&id)));
124 SyncWithAllThreads();
125 return id;
126 }
127
128 private:
129 void SyncWith(scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
130 CHECK(task_runner);
131 CHECK(!task_runner->BelongsToCurrentThread());
132 base::WaitableEvent e = {base::WaitableEvent::ResetPolicy::MANUAL,
133 base::WaitableEvent::InitialState::NOT_SIGNALED};
134 task_runner->PostTask(FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
135 base::Unretained(&e)));
136 e.Wait();
137 }
138
139 void GetRawNondefaultIdOnIOThread(std::string* out) {
140 DCHECK_CURRENTLY_ON(BrowserThread::IO);
141 MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
142 devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
143
144 media_stream_manager_->media_devices_manager()->EnumerateDevices(
145 devices_to_enumerate,
146 base::Bind(
147 [](std::string* out, const MediaDeviceEnumeration& result) {
148 // Index 0 is default, so use 1.
149 CHECK(result[MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]
150 .size() > 1)
151 << "Expected to have a nondefault device.";
152 *out = result[MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT][1]
153 .device_id;
154 },
155 base::Unretained(out)));
156 }
157
158 std::unique_ptr<TestBrowserThreadBundle> thread_bundle_;
159 std::unique_ptr<base::Thread> audio_thread_;
160 media::FakeAudioLogFactory log_factory_;
161 media::ScopedAudioManagerPtr audio_manager_;
162 std::unique_ptr<MediaStreamManager> media_stream_manager_;
163
164 DISALLOW_COPY_AND_ASSIGN(AudioOutputAuthorizationHandlerTest);
165 };
166
167 TEST_F(AudioOutputAuthorizationHandlerTest, AuthorizeDefaultDevice_Ok) {
168 MockListener listener;
169 EXPECT_CALL(listener,
170 MockAuthorizationCallback(media::OUTPUT_DEVICE_STATUS_OK, _,
171 kDefaultDeviceId))
172 .Times(1);
173 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
174 base::MakeUnique<AudioOutputAuthorizationHandler>(
175 GetMediaStreamManager(), kRenderProcessId, kSalt);
176
177 BrowserThread::PostTask(
178 BrowserThread::IO, FROM_HERE,
179 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
180 base::Unretained(handler.get()), kRenderFrameId, 0,
181 kDefaultDeviceId, SecurityOrigin(), listener.GetCallback())));
182
183 SyncWithAllThreads();
184 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
185 SyncWithAllThreads();
186 }
187
188 TEST_F(AudioOutputAuthorizationHandlerTest,
189 AuthorizeDefaultDeviceByEmptyId_Ok) {
190 MockListener listener;
191 EXPECT_CALL(listener,
192 MockAuthorizationCallback(media::OUTPUT_DEVICE_STATUS_OK, _,
193 kDefaultDeviceId))
194 .Times(1);
195 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
196 base::MakeUnique<AudioOutputAuthorizationHandler>(
197 GetMediaStreamManager(), kRenderProcessId, kSalt);
198
199 BrowserThread::PostTask(
200 BrowserThread::IO, FROM_HERE,
201 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
202 base::Unretained(handler.get()), kRenderFrameId, 0,
203 kEmptyDeviceId, SecurityOrigin(), listener.GetCallback())));
204
205 SyncWithAllThreads();
206 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
207 SyncWithAllThreads();
208 }
209
210 TEST_F(AudioOutputAuthorizationHandlerTest,
211 AuthorizeNondefaultDeviceIdWithoutPermission_NotAuthorized) {
212 std::string raw_nondefault_id = GetRawNondefaultId();
213 std::string hashed_id = MediaStreamManager::GetHMACForMediaDeviceID(
214 kSalt, SecurityOrigin(), raw_nondefault_id);
215
216 MockListener listener;
217 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
218 base::MakeUnique<AudioOutputAuthorizationHandler>(
219 GetMediaStreamManager(), kRenderProcessId, kSalt);
220 BrowserThread::PostTask(
221 BrowserThread::IO, FROM_HERE,
222 base::Bind(
223 &AudioOutputAuthorizationHandler::OverridePermissionsForTesting,
224 base::Unretained(handler.get()), false));
225 SyncWithAllThreads();
226
227 EXPECT_CALL(listener, MockAuthorizationCallback(
228 media::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED, _,
229 std::string()))
230 .Times(1);
231
232 BrowserThread::PostTask(
233 BrowserThread::IO, FROM_HERE,
234 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
235 base::Unretained(handler.get()), kRenderFrameId, 0, hashed_id,
236 SecurityOrigin(), listener.GetCallback())));
237
238 SyncWithAllThreads();
239 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
240 SyncWithAllThreads();
241 }
242
243 TEST_F(AudioOutputAuthorizationHandlerTest,
244 AuthorizeNondefaultDeviceIdWithPermission_Ok) {
245 std::string raw_nondefault_id = GetRawNondefaultId();
246 std::string hashed_id = MediaStreamManager::GetHMACForMediaDeviceID(
247 kSalt, SecurityOrigin(), raw_nondefault_id);
248 MockListener listener;
249 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
250 base::MakeUnique<AudioOutputAuthorizationHandler>(
251 GetMediaStreamManager(), kRenderProcessId, kSalt);
252 BrowserThread::PostTask(
253 BrowserThread::IO, FROM_HERE,
254 base::Bind(
255 &AudioOutputAuthorizationHandler::OverridePermissionsForTesting,
256 base::Unretained(handler.get()), true));
257
258 EXPECT_CALL(listener,
259 MockAuthorizationCallback(media::OUTPUT_DEVICE_STATUS_OK, _,
260 raw_nondefault_id))
261 .Times(1);
262
263 BrowserThread::PostTask(
264 BrowserThread::IO, FROM_HERE,
265 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
266 base::Unretained(handler.get()), kRenderFrameId, 0, hashed_id,
267 SecurityOrigin(), listener.GetCallback())));
268
269 SyncWithAllThreads();
270 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
271 SyncWithAllThreads();
272 }
273
274 TEST_F(AudioOutputAuthorizationHandlerTest,
275 AuthorizeInvalidDeviceId_BadMessage) {
276 std::unique_ptr<TestBrowserContext> context =
277 base::MakeUnique<TestBrowserContext>();
278 std::unique_ptr<MockRenderProcessHost> RPH =
279 base::MakeUnique<MockRenderProcessHost>(context.get());
280 MockListener listener;
281 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
282 base::MakeUnique<AudioOutputAuthorizationHandler>(GetMediaStreamManager(),
283 RPH->GetID(), kSalt);
284 EXPECT_EQ(RPH->bad_msg_count(), 0);
285
286 EXPECT_CALL(listener, MockAuthorizationCallback(_, _, _)).Times(0);
287
288 BrowserThread::PostTask(
289 BrowserThread::IO, FROM_HERE,
290 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
291 base::Unretained(handler.get()), kRenderFrameId, 0,
292 kInvalidDeviceId, SecurityOrigin(), listener.GetCallback())));
293
294 SyncWithAllThreads();
295 EXPECT_EQ(RPH->bad_msg_count(), 1);
296 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
297 SyncWithAllThreads();
298 RPH.reset();
299 context.reset();
300 }
301
302 TEST_F(AudioOutputAuthorizationHandlerTest,
303 AuthorizeNondefaultDeviceIdWithBadOrigin_BadMessage) {
304 std::string raw_nondefault_id = GetRawNondefaultId();
305 std::string hashed_id = MediaStreamManager::GetHMACForMediaDeviceID(
306 kSalt, SecurityOrigin(), raw_nondefault_id);
307 std::unique_ptr<TestBrowserContext> context =
308 base::MakeUnique<TestBrowserContext>();
309 std::unique_ptr<MockRenderProcessHost> RPH =
310 base::MakeUnique<MockRenderProcessHost>(context.get());
311 MockListener listener;
312 std::unique_ptr<AudioOutputAuthorizationHandler> handler =
313 base::MakeUnique<AudioOutputAuthorizationHandler>(GetMediaStreamManager(),
314 RPH->GetID(), kSalt);
315
316 EXPECT_EQ(RPH->bad_msg_count(), 0);
317 EXPECT_CALL(listener, MockAuthorizationCallback(_, _, _)).Times(0);
318
319 BrowserThread::PostTask(
320 BrowserThread::IO, FROM_HERE,
321 (base::Bind(&AudioOutputAuthorizationHandler::RequestDeviceAuthorization,
322 base::Unretained(handler.get()), kRenderFrameId, 0, hashed_id,
323 BadSecurityOrigin(), listener.GetCallback())));
324
325 SyncWithAllThreads();
326 EXPECT_EQ(RPH->bad_msg_count(), 1);
327 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, handler.release());
328 SyncWithAllThreads();
329 RPH.reset();
330 context.reset();
331 }
332
333 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698