Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / webrtc_audio_private / webrtc_audio_private_browsertest.cc
1 // Copyright 2013 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/json/json_writer.h"
6 #include "base/strings/string_util.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/threading/platform_thread.h"
11 #include "base/time/time.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h"
14 #include "chrome/browser/extensions/component_loader.h"
15 #include "chrome/browser/extensions/extension_apitest.h"
16 #include "chrome/browser/extensions/extension_function_test_utils.h"
17 #include "chrome/browser/extensions/extension_tab_util.h"
18 #include "chrome/browser/media/webrtc_log_uploader.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
21 #include "chrome/test/base/in_process_browser_test.h"
22 #include "chrome/test/base/ui_test_utils.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/media_device_id.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/test/browser_test_utils.h"
27 #include "extensions/common/permissions/permission_set.h"
28 #include "extensions/common/permissions/permissions_data.h"
29 #include "media/audio/audio_manager.h"
30 #include "media/audio/audio_manager_base.h"
31 #include "net/test/embedded_test_server/embedded_test_server.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33
34 using base::JSONWriter;
35 using content::RenderViewHost;
36 using content::WebContents;
37 using media::AudioDeviceNames;
38 using media::AudioManager;
39
40 namespace extensions {
41
42 using extension_function_test_utils::RunFunctionAndReturnError;
43 using extension_function_test_utils::RunFunctionAndReturnSingleResult;
44
45 class AudioWaitingExtensionTest : public ExtensionApiTest {
46  protected:
47   void WaitUntilAudioIsPlaying(WebContents* tab) {
48     // Wait for audio to start playing. We gate this on there being one
49     // or more AudioOutputController objects for our tab.
50     bool audio_playing = false;
51     for (size_t remaining_tries = 50; remaining_tries > 0; --remaining_tries) {
52       tab->GetRenderViewHost()->GetAudioOutputControllers(
53           base::Bind(OnAudioControllers, &audio_playing));
54       base::MessageLoop::current()->RunUntilIdle();
55       if (audio_playing)
56         break;
57
58       base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
59     }
60
61     if (!audio_playing)
62       FAIL() << "Audio did not start playing within ~5 seconds.";
63   }
64
65   // Used by the test above to wait until audio is playing.
66   static void OnAudioControllers(
67       bool* audio_playing,
68       const RenderViewHost::AudioOutputControllerList& list) {
69     if (!list.empty())
70       *audio_playing = true;
71   }
72 };
73
74 class WebrtcAudioPrivateTest : public AudioWaitingExtensionTest {
75  public:
76   WebrtcAudioPrivateTest()
77       : enumeration_event_(false, false) {
78   }
79
80   void SetUpOnMainThread() override {
81     AudioWaitingExtensionTest::SetUpOnMainThread();
82     // Needs to happen after chrome's schemes are added.
83     source_url_ = GURL("chrome-extension://fakeid012345678/fakepage.html");
84   }
85
86  protected:
87   std::string InvokeGetActiveSink(int tab_id) {
88     base::ListValue parameters;
89     parameters.AppendInteger(tab_id);
90     std::string parameter_string;
91     JSONWriter::Write(&parameters, &parameter_string);
92
93     scoped_refptr<WebrtcAudioPrivateGetActiveSinkFunction> function =
94         new WebrtcAudioPrivateGetActiveSinkFunction();
95     function->set_source_url(source_url_);
96     scoped_ptr<base::Value> result(
97         RunFunctionAndReturnSingleResult(function.get(),
98                                          parameter_string,
99                                          browser()));
100     std::string device_id;
101     result->GetAsString(&device_id);
102     return device_id;
103   }
104
105   scoped_ptr<base::Value> InvokeGetSinks(base::ListValue** sink_list) {
106     scoped_refptr<WebrtcAudioPrivateGetSinksFunction> function =
107         new WebrtcAudioPrivateGetSinksFunction();
108     function->set_source_url(source_url_);
109
110     scoped_ptr<base::Value> result(
111         RunFunctionAndReturnSingleResult(function.get(), "[]", browser()));
112     result->GetAsList(sink_list);
113     return result.Pass();
114   }
115
116   // Synchronously (from the calling thread's point of view) runs the
117   // given enumeration function on the device thread. On return,
118   // |device_names| has been filled with the device names resulting
119   // from that call.
120   void GetAudioDeviceNames(
121       void (AudioManager::*EnumerationFunc)(AudioDeviceNames*),
122       AudioDeviceNames* device_names) {
123     AudioManager* audio_manager = AudioManager::Get();
124
125     if (!audio_manager->GetWorkerTaskRunner()->BelongsToCurrentThread()) {
126       audio_manager->GetWorkerTaskRunner()->PostTask(
127           FROM_HERE,
128           base::Bind(&WebrtcAudioPrivateTest::GetAudioDeviceNames, this,
129                      EnumerationFunc, device_names));
130       enumeration_event_.Wait();
131     } else {
132       (audio_manager->*EnumerationFunc)(device_names);
133       enumeration_event_.Signal();
134     }
135   }
136
137   // Synchronously (from the calling thread's point of view) retrieve the
138   // device id in the |origin| on the IO thread. On return,
139   // |id_in_origin| contains the id |raw_device_id| is known by in
140   // the origin.
141   void GetIDInOrigin(content::ResourceContext* resource_context,
142                      GURL origin,
143                      const std::string& raw_device_id,
144                      std::string* id_in_origin) {
145     if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) {
146       content::BrowserThread::PostTask(
147           content::BrowserThread::IO, FROM_HERE,
148           base::Bind(&WebrtcAudioPrivateTest::GetIDInOrigin,
149                      this, resource_context, origin, raw_device_id,
150                      id_in_origin));
151       enumeration_event_.Wait();
152     } else {
153       *id_in_origin = content::GetHMACForMediaDeviceID(
154           resource_context->GetMediaDeviceIDSalt(),
155           origin,
156           raw_device_id);
157       enumeration_event_.Signal();
158     }
159   }
160
161   // Event used to signal completion of enumeration.
162   base::WaitableEvent enumeration_event_;
163
164   GURL source_url_;
165 };
166
167 #if !defined(OS_MACOSX)
168 // http://crbug.com/334579
169 IN_PROC_BROWSER_TEST_F(WebrtcAudioPrivateTest, GetSinks) {
170   AudioDeviceNames devices;
171   GetAudioDeviceNames(&AudioManager::GetAudioOutputDeviceNames, &devices);
172
173   base::ListValue* sink_list = NULL;
174   scoped_ptr<base::Value> result = InvokeGetSinks(&sink_list);
175
176   std::string result_string;
177   JSONWriter::Write(result.get(), &result_string);
178   VLOG(2) << result_string;
179
180   EXPECT_EQ(devices.size(), sink_list->GetSize());
181
182   // Iterate through both lists in lockstep and compare. The order
183   // should be identical.
184   size_t ix = 0;
185   AudioDeviceNames::const_iterator it = devices.begin();
186   for (; ix < sink_list->GetSize() && it != devices.end();
187        ++ix, ++it) {
188     base::DictionaryValue* dict = NULL;
189     sink_list->GetDictionary(ix, &dict);
190     std::string sink_id;
191     dict->GetString("sinkId", &sink_id);
192
193     std::string expected_id;
194     if (it->unique_id.empty() ||
195         it->unique_id == media::AudioManagerBase::kDefaultDeviceId) {
196       expected_id = media::AudioManagerBase::kDefaultDeviceId;
197     } else {
198       GetIDInOrigin(profile()->GetResourceContext(),
199                     source_url_.GetOrigin(),
200                     it->unique_id,
201                     &expected_id);
202     }
203
204     EXPECT_EQ(expected_id, sink_id);
205     std::string sink_label;
206     dict->GetString("sinkLabel", &sink_label);
207     EXPECT_EQ(it->device_name, sink_label);
208
209     // TODO(joi): Verify the contents of these once we start actually
210     // filling them in.
211     EXPECT_TRUE(dict->HasKey("isDefault"));
212     EXPECT_TRUE(dict->HasKey("isReady"));
213     EXPECT_TRUE(dict->HasKey("sampleRate"));
214   }
215 }
216 #endif  // OS_MACOSX
217
218 // This exercises the case where you have a tab with no active media
219 // stream and try to retrieve the currently active audio sink.
220 IN_PROC_BROWSER_TEST_F(WebrtcAudioPrivateTest, GetActiveSinkNoMediaStream) {
221   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
222   int tab_id = ExtensionTabUtil::GetTabId(tab);
223   base::ListValue parameters;
224   parameters.AppendInteger(tab_id);
225   std::string parameter_string;
226   JSONWriter::Write(&parameters, &parameter_string);
227
228   scoped_refptr<WebrtcAudioPrivateGetActiveSinkFunction> function =
229       new WebrtcAudioPrivateGetActiveSinkFunction();
230   function->set_source_url(source_url_);
231   scoped_ptr<base::Value> result(
232       RunFunctionAndReturnSingleResult(function.get(),
233                                        parameter_string,
234                                        browser()));
235
236   std::string result_string;
237   JSONWriter::Write(result.get(), &result_string);
238   EXPECT_EQ("\"\"", result_string);
239 }
240
241 // This exercises the case where you have a tab with no active media
242 // stream and try to set the audio sink.
243 IN_PROC_BROWSER_TEST_F(WebrtcAudioPrivateTest, SetActiveSinkNoMediaStream) {
244   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
245   int tab_id = ExtensionTabUtil::GetTabId(tab);
246   base::ListValue parameters;
247   parameters.AppendInteger(tab_id);
248   parameters.AppendString("no such id");
249   std::string parameter_string;
250   JSONWriter::Write(&parameters, &parameter_string);
251
252   scoped_refptr<WebrtcAudioPrivateSetActiveSinkFunction> function =
253       new WebrtcAudioPrivateSetActiveSinkFunction();
254   function->set_source_url(source_url_);
255   std::string error(RunFunctionAndReturnError(function.get(),
256                                               parameter_string,
257                                               browser()));
258   EXPECT_EQ(base::StringPrintf("No active stream for tab with id: %d.", tab_id),
259             error);
260 }
261
262 IN_PROC_BROWSER_TEST_F(WebrtcAudioPrivateTest, GetAndSetWithMediaStream) {
263   // First retrieve the list of all sinks, so that we can run a test
264   // where we set the active sink to each of the different available
265   // sinks in turn.
266   base::ListValue* sink_list = NULL;
267   scoped_ptr<base::Value> result = InvokeGetSinks(&sink_list);
268
269   ASSERT_TRUE(StartEmbeddedTestServer());
270
271   // Open a normal page that uses an audio sink.
272   ui_test_utils::NavigateToURL(
273       browser(),
274       GURL(embedded_test_server()->GetURL("/extensions/loop_audio.html")));
275
276   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
277   int tab_id = ExtensionTabUtil::GetTabId(tab);
278
279   WaitUntilAudioIsPlaying(tab);
280
281   std::string current_device = InvokeGetActiveSink(tab_id);
282   VLOG(2) << "Before setting, current device: " << current_device;
283   EXPECT_NE("", current_device);
284
285   // Set to each of the other devices in turn.
286   for (size_t ix = 0; ix < sink_list->GetSize(); ++ix) {
287     base::DictionaryValue* dict = NULL;
288     sink_list->GetDictionary(ix, &dict);
289     std::string target_device;
290     dict->GetString("sinkId", &target_device);
291
292     base::ListValue parameters;
293     parameters.AppendInteger(tab_id);
294     parameters.AppendString(target_device);
295     std::string parameter_string;
296     JSONWriter::Write(&parameters, &parameter_string);
297
298     scoped_refptr<WebrtcAudioPrivateSetActiveSinkFunction> function =
299       new WebrtcAudioPrivateSetActiveSinkFunction();
300     function->set_source_url(source_url_);
301     scoped_ptr<base::Value> result(RunFunctionAndReturnSingleResult(
302         function.get(), parameter_string, browser()));
303     // The function was successful if the above invocation doesn't
304     // fail. Just for kicks, also check that it returns no result.
305     EXPECT_EQ(NULL, result.get());
306
307     current_device = InvokeGetActiveSink(tab_id);
308     VLOG(2) << "After setting to " << target_device
309             << ", current device is " << current_device;
310     EXPECT_EQ(target_device, current_device);
311   }
312 }
313
314 IN_PROC_BROWSER_TEST_F(WebrtcAudioPrivateTest, GetAssociatedSink) {
315   // Get the list of input devices. We can cheat in the unit test and
316   // run this on the main thread since nobody else will be running at
317   // the same time.
318   AudioDeviceNames devices;
319   GetAudioDeviceNames(&AudioManager::GetAudioInputDeviceNames, &devices);
320
321   // Try to get an associated sink for each source.
322   for (AudioDeviceNames::const_iterator device = devices.begin();
323        device != devices.end();
324        ++device) {
325     scoped_refptr<WebrtcAudioPrivateGetAssociatedSinkFunction> function =
326         new WebrtcAudioPrivateGetAssociatedSinkFunction();
327     function->set_source_url(source_url_);
328
329     std::string raw_device_id = device->unique_id;
330     VLOG(2) << "Trying to find associated sink for device " << raw_device_id;
331     std::string source_id_in_origin;
332     GURL origin(GURL("http://www.google.com/").GetOrigin());
333     GetIDInOrigin(profile()->GetResourceContext(),
334                   origin,
335                   raw_device_id,
336                   &source_id_in_origin);
337
338     base::ListValue parameters;
339     parameters.AppendString(origin.spec());
340     parameters.AppendString(source_id_in_origin);
341     std::string parameter_string;
342     JSONWriter::Write(&parameters, &parameter_string);
343
344     scoped_ptr<base::Value> result(
345         RunFunctionAndReturnSingleResult(function.get(),
346                                          parameter_string,
347                                          browser()));
348     std::string result_string;
349     JSONWriter::Write(result.get(), &result_string);
350     VLOG(2) << "Results: " << result_string;
351   }
352 }
353
354 IN_PROC_BROWSER_TEST_F(WebrtcAudioPrivateTest, TriggerEvent) {
355   WebrtcAudioPrivateEventService* service =
356       WebrtcAudioPrivateEventService::GetFactoryInstance()->Get(profile());
357
358   // Just trigger, without any extension listening.
359   service->OnDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE);
360
361   // Now load our test extension and do it again.
362   const extensions::Extension* extension = LoadExtension(
363       test_data_dir_.AppendASCII("webrtc_audio_private_event_listener"));
364   service->OnDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE);
365
366   // Check that the extension got the notification.
367   std::string result = ExecuteScriptInBackgroundPage(extension->id(),
368                                                      "reportIfGot()");
369   EXPECT_EQ("true", result);
370 }
371
372 class HangoutServicesBrowserTest : public AudioWaitingExtensionTest {
373  public:
374   void SetUp() override {
375     // Make sure the Hangout Services component extension gets loaded.
376     ComponentLoader::EnableBackgroundExtensionsForTesting();
377     AudioWaitingExtensionTest::SetUp();
378   }
379 };
380
381 #if defined(GOOGLE_CHROME_BUILD) || defined(ENABLE_HANGOUT_SERVICES_EXTENSION)
382 IN_PROC_BROWSER_TEST_F(HangoutServicesBrowserTest,
383                        RunComponentExtensionTest) {
384   // This runs the end-to-end JavaScript test for the Hangout Services
385   // component extension, which uses the webrtcAudioPrivate API among
386   // others.
387   ASSERT_TRUE(StartEmbeddedTestServer());
388   GURL url(embedded_test_server()->GetURL(
389                "/extensions/hangout_services_test.html"));
390   // The "externally connectable" extension permission doesn't seem to
391   // like when we use 127.0.0.1 as the host, but using localhost works.
392   std::string url_spec = url.spec();
393   ReplaceFirstSubstringAfterOffset(&url_spec, 0, "127.0.0.1", "localhost");
394   GURL localhost_url(url_spec);
395   ui_test_utils::NavigateToURL(browser(), localhost_url);
396
397   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
398   WaitUntilAudioIsPlaying(tab);
399
400   // Override, i.e. disable, uploading. We don't want to try sending data to
401   // servers when running the test. We don't bother about the contents of the
402   // buffer |dummy|, that's tested in other tests.
403   std::string dummy;
404   g_browser_process->webrtc_log_uploader()->
405       OverrideUploadWithBufferForTesting(&dummy);
406
407   ASSERT_TRUE(content::ExecuteScript(tab, "browsertestRunAllTests();"));
408
409   content::TitleWatcher title_watcher(tab, base::ASCIIToUTF16("success"));
410   title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16("failure"));
411   base::string16 result = title_watcher.WaitAndGetTitle();
412   EXPECT_EQ(base::ASCIIToUTF16("success"), result);
413
414   g_browser_process->webrtc_log_uploader()->OverrideUploadWithBufferForTesting(
415       NULL);
416 }
417 #endif  // defined(GOOGLE_CHROME_BUILD) || defined(ENABLE_HANGOUT_SERVICES_EXTENSION)
418
419 }  // namespace extensions