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.
8 #include "base/memory/scoped_ptr.h"
9 #include "base/run_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/browser/speech/google_streaming_remote_engine.h"
12 #include "content/browser/speech/speech_recognition_manager_impl.h"
13 #include "content/browser/speech/speech_recognizer_impl.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/test/browser_test_utils.h"
18 #include "content/public/test/content_browser_test.h"
19 #include "content/public/test/content_browser_test_utils.h"
20 #include "content/public/test/test_utils.h"
21 #include "content/shell/browser/shell.h"
22 #include "content/test/mock_google_streaming_server.h"
23 #include "media/audio/mock_audio_manager.h"
24 #include "media/audio/test_audio_input_controller_factory.h"
25 #include "testing/gtest/include/gtest/gtest.h"
31 class SpeechRecognitionBrowserTest
:
32 public ContentBrowserTest
,
33 public MockGoogleStreamingServer::Delegate
,
34 public media::TestAudioInputControllerDelegate
{
36 enum StreamingServerState
{
38 kTestAudioControllerOpened
,
41 kClientAudioUploadComplete
,
42 kTestAudioControllerClosed
,
46 // MockGoogleStreamingServerDelegate methods.
47 void OnClientConnected() override
{
48 ASSERT_EQ(kTestAudioControllerOpened
, streaming_server_state_
);
49 streaming_server_state_
= kClientConnected
;
52 void OnClientAudioUpload() override
{
53 if (streaming_server_state_
== kClientConnected
)
54 streaming_server_state_
= kClientAudioUpload
;
57 void OnClientAudioUploadComplete() override
{
58 ASSERT_EQ(kTestAudioControllerClosed
, streaming_server_state_
);
59 streaming_server_state_
= kClientAudioUploadComplete
;
62 void OnClientDisconnected() override
{
63 ASSERT_EQ(kClientAudioUploadComplete
, streaming_server_state_
);
64 streaming_server_state_
= kClientDisconnected
;
67 // media::TestAudioInputControllerDelegate methods.
68 void TestAudioControllerOpened(
69 media::TestAudioInputController
* controller
) override
{
70 ASSERT_EQ(kIdle
, streaming_server_state_
);
71 streaming_server_state_
= kTestAudioControllerOpened
;
72 const int capture_packet_interval_ms
=
73 (1000 * controller
->audio_parameters().frames_per_buffer()) /
74 controller
->audio_parameters().sample_rate();
75 ASSERT_EQ(GoogleStreamingRemoteEngine::kAudioPacketIntervalMs
,
76 capture_packet_interval_ms
);
77 FeedAudioController(500 /* ms */, /*noise=*/ false);
78 FeedAudioController(1000 /* ms */, /*noise=*/ true);
79 FeedAudioController(1000 /* ms */, /*noise=*/ false);
82 void TestAudioControllerClosed(
83 media::TestAudioInputController
* controller
) override
{
84 ASSERT_EQ(kClientAudioUpload
, streaming_server_state_
);
85 streaming_server_state_
= kTestAudioControllerClosed
;
86 mock_streaming_server_
->MockGoogleStreamingServer::SimulateResult(
87 GetGoodSpeechResult());
90 // Helper methods used by test fixtures.
91 GURL
GetTestUrlFromFragment(const std::string fragment
) {
92 return GURL(GetTestUrl("speech", "web_speech_recognition.html").spec() +
96 std::string
GetPageFragment() {
97 return shell()->web_contents()->GetURL().ref();
100 const StreamingServerState
&streaming_server_state() {
101 return streaming_server_state_
;
105 // ContentBrowserTest methods.
106 void SetUpInProcessBrowserTestFixture() override
{
107 test_audio_input_controller_factory_
.set_delegate(this);
108 media::AudioInputController::set_factory_for_testing(
109 &test_audio_input_controller_factory_
);
110 mock_streaming_server_
.reset(new MockGoogleStreamingServer(this));
111 streaming_server_state_
= kIdle
;
114 void SetUpOnMainThread() override
{
115 ASSERT_TRUE(SpeechRecognitionManagerImpl::GetInstance());
116 SpeechRecognizerImpl::SetAudioManagerForTesting(
117 new media::MockAudioManager(BrowserThread::GetMessageLoopProxyForThread(
118 BrowserThread::IO
)));
121 void TearDownOnMainThread() override
{
122 SpeechRecognizerImpl::SetAudioManagerForTesting(NULL
);
125 void TearDownInProcessBrowserTestFixture() override
{
126 test_audio_input_controller_factory_
.set_delegate(NULL
);
127 mock_streaming_server_
.reset();
131 static void FeedSingleBufferToAudioController(
132 scoped_refptr
<media::TestAudioInputController
> controller
,
134 bool fill_with_noise
) {
135 DCHECK(controller
.get());
136 const media::AudioParameters
& audio_params
= controller
->audio_parameters();
137 scoped_ptr
<uint8
[]> audio_buffer(new uint8
[buffer_size
]);
138 if (fill_with_noise
) {
139 for (size_t i
= 0; i
< buffer_size
; ++i
)
140 audio_buffer
[i
] = static_cast<uint8
>(127 * sin(i
* 3.14F
/
141 (16 * buffer_size
)));
143 memset(audio_buffer
.get(), 0, buffer_size
);
146 scoped_ptr
<media::AudioBus
> audio_bus
=
147 media::AudioBus::Create(audio_params
);
148 audio_bus
->FromInterleaved(&audio_buffer
.get()[0],
150 audio_params
.bits_per_sample() / 8);
151 controller
->event_handler()->OnData(controller
.get(), audio_bus
.get());
154 void FeedAudioController(int duration_ms
, bool feed_with_noise
) {
155 media::TestAudioInputController
* controller
=
156 test_audio_input_controller_factory_
.controller();
157 ASSERT_TRUE(controller
);
158 const media::AudioParameters
& audio_params
= controller
->audio_parameters();
159 const size_t buffer_size
= audio_params
.GetBytesPerBuffer();
160 const int ms_per_buffer
= audio_params
.frames_per_buffer() * 1000 /
161 audio_params
.sample_rate();
162 // We can only simulate durations that are integer multiples of the
163 // buffer size. In this regard see
164 // SpeechRecognitionEngine::GetDesiredAudioChunkDurationMs().
165 ASSERT_EQ(0, duration_ms
% ms_per_buffer
);
167 const int n_buffers
= duration_ms
/ ms_per_buffer
;
168 for (int i
= 0; i
< n_buffers
; ++i
) {
169 base::MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(
170 &FeedSingleBufferToAudioController
,
171 scoped_refptr
<media::TestAudioInputController
>(controller
),
177 SpeechRecognitionResult
GetGoodSpeechResult() {
178 SpeechRecognitionResult result
;
179 result
.hypotheses
.push_back(SpeechRecognitionHypothesis(
180 base::UTF8ToUTF16("Pictures of the moon"), 1.0F
));
184 StreamingServerState streaming_server_state_
;
185 scoped_ptr
<MockGoogleStreamingServer
> mock_streaming_server_
;
186 media::TestAudioInputControllerFactory test_audio_input_controller_factory_
;
189 // Simply loads the test page and checks if it was able to create a Speech
190 // Recognition object in JavaScript, to make sure the Web Speech API is enabled.
191 // http://crbug.com/396414
192 #if defined(OS_WIN) || defined(OS_MACOSX)
193 #define MAYBE_Precheck DISABLED_Precheck
195 #define MAYBE_Precheck Precheck
197 IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest
, MAYBE_Precheck
) {
198 NavigateToURLBlockUntilNavigationsComplete(
199 shell(), GetTestUrlFromFragment("precheck"), 2);
201 EXPECT_EQ(kIdle
, streaming_server_state());
202 EXPECT_EQ("success", GetPageFragment());
205 IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest
, OneShotRecognition
) {
206 NavigateToURLBlockUntilNavigationsComplete(
207 shell(), GetTestUrlFromFragment("oneshot"), 2);
209 EXPECT_EQ(kClientDisconnected
, streaming_server_state());
210 EXPECT_EQ("goodresult1", GetPageFragment());
213 } // namespace content