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/location.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/run_loop.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "content/browser/speech/google_streaming_remote_engine.h"
15 #include "content/browser/speech/speech_recognition_manager_impl.h"
16 #include "content/browser/speech/speech_recognizer_impl.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/test/browser_test_utils.h"
21 #include "content/public/test/content_browser_test.h"
22 #include "content/public/test/content_browser_test_utils.h"
23 #include "content/public/test/test_utils.h"
24 #include "content/shell/browser/shell.h"
25 #include "content/test/mock_google_streaming_server.h"
26 #include "media/audio/mock_audio_manager.h"
27 #include "media/audio/test_audio_input_controller_factory.h"
28 #include "testing/gtest/include/gtest/gtest.h"
34 class SpeechRecognitionBrowserTest
:
35 public ContentBrowserTest
,
36 public MockGoogleStreamingServer::Delegate
,
37 public media::TestAudioInputControllerDelegate
{
39 enum StreamingServerState
{
41 kTestAudioControllerOpened
,
44 kClientAudioUploadComplete
,
45 kTestAudioControllerClosed
,
49 // MockGoogleStreamingServerDelegate methods.
50 void OnClientConnected() override
{
51 ASSERT_EQ(kTestAudioControllerOpened
, streaming_server_state_
);
52 streaming_server_state_
= kClientConnected
;
55 void OnClientAudioUpload() override
{
56 if (streaming_server_state_
== kClientConnected
)
57 streaming_server_state_
= kClientAudioUpload
;
60 void OnClientAudioUploadComplete() override
{
61 ASSERT_EQ(kTestAudioControllerClosed
, streaming_server_state_
);
62 streaming_server_state_
= kClientAudioUploadComplete
;
65 void OnClientDisconnected() override
{
66 ASSERT_EQ(kClientAudioUploadComplete
, streaming_server_state_
);
67 streaming_server_state_
= kClientDisconnected
;
70 // media::TestAudioInputControllerDelegate methods.
71 void TestAudioControllerOpened(
72 media::TestAudioInputController
* controller
) override
{
73 ASSERT_EQ(kIdle
, streaming_server_state_
);
74 streaming_server_state_
= kTestAudioControllerOpened
;
75 const int capture_packet_interval_ms
=
76 (1000 * controller
->audio_parameters().frames_per_buffer()) /
77 controller
->audio_parameters().sample_rate();
78 ASSERT_EQ(GoogleStreamingRemoteEngine::kAudioPacketIntervalMs
,
79 capture_packet_interval_ms
);
80 FeedAudioController(500 /* ms */, /*noise=*/ false);
81 FeedAudioController(1000 /* ms */, /*noise=*/ true);
82 FeedAudioController(1000 /* ms */, /*noise=*/ false);
85 void TestAudioControllerClosed(
86 media::TestAudioInputController
* controller
) override
{
87 ASSERT_EQ(kClientAudioUpload
, streaming_server_state_
);
88 streaming_server_state_
= kTestAudioControllerClosed
;
89 mock_streaming_server_
->MockGoogleStreamingServer::SimulateResult(
90 GetGoodSpeechResult());
93 // Helper methods used by test fixtures.
94 GURL
GetTestUrlFromFragment(const std::string fragment
) {
95 return GURL(GetTestUrl("speech", "web_speech_recognition.html").spec() +
99 std::string
GetPageFragment() {
100 return shell()->web_contents()->GetLastCommittedURL().ref();
103 const StreamingServerState
&streaming_server_state() {
104 return streaming_server_state_
;
108 // ContentBrowserTest methods.
109 void SetUpInProcessBrowserTestFixture() override
{
110 test_audio_input_controller_factory_
.set_delegate(this);
111 media::AudioInputController::set_factory_for_testing(
112 &test_audio_input_controller_factory_
);
113 mock_streaming_server_
.reset(new MockGoogleStreamingServer(this));
114 streaming_server_state_
= kIdle
;
117 void SetUpOnMainThread() override
{
118 ASSERT_TRUE(SpeechRecognitionManagerImpl::GetInstance());
119 SpeechRecognizerImpl::SetAudioManagerForTesting(
120 new media::MockAudioManager(BrowserThread::GetMessageLoopProxyForThread(
121 BrowserThread::IO
)));
124 void TearDownOnMainThread() override
{
125 SpeechRecognizerImpl::SetAudioManagerForTesting(NULL
);
128 void TearDownInProcessBrowserTestFixture() override
{
129 test_audio_input_controller_factory_
.set_delegate(NULL
);
130 mock_streaming_server_
.reset();
134 static void FeedSingleBufferToAudioController(
135 scoped_refptr
<media::TestAudioInputController
> controller
,
137 bool fill_with_noise
) {
138 DCHECK(controller
.get());
139 const media::AudioParameters
& audio_params
= controller
->audio_parameters();
140 scoped_ptr
<uint8
[]> audio_buffer(new uint8
[buffer_size
]);
141 if (fill_with_noise
) {
142 for (size_t i
= 0; i
< buffer_size
; ++i
)
143 audio_buffer
[i
] = static_cast<uint8
>(127 * sin(i
* 3.14F
/
144 (16 * buffer_size
)));
146 memset(audio_buffer
.get(), 0, buffer_size
);
149 scoped_ptr
<media::AudioBus
> audio_bus
=
150 media::AudioBus::Create(audio_params
);
151 audio_bus
->FromInterleaved(&audio_buffer
.get()[0],
153 audio_params
.bits_per_sample() / 8);
154 controller
->event_handler()->OnData(controller
.get(), audio_bus
.get());
157 void FeedAudioController(int duration_ms
, bool feed_with_noise
) {
158 media::TestAudioInputController
* controller
=
159 test_audio_input_controller_factory_
.controller();
160 ASSERT_TRUE(controller
);
161 const media::AudioParameters
& audio_params
= controller
->audio_parameters();
162 const size_t buffer_size
= audio_params
.GetBytesPerBuffer();
163 const int ms_per_buffer
= audio_params
.frames_per_buffer() * 1000 /
164 audio_params
.sample_rate();
165 // We can only simulate durations that are integer multiples of the
166 // buffer size. In this regard see
167 // SpeechRecognitionEngine::GetDesiredAudioChunkDurationMs().
168 ASSERT_EQ(0, duration_ms
% ms_per_buffer
);
170 const int n_buffers
= duration_ms
/ ms_per_buffer
;
171 for (int i
= 0; i
< n_buffers
; ++i
) {
172 base::ThreadTaskRunnerHandle::Get()->PostTask(
174 base::Bind(&FeedSingleBufferToAudioController
,
175 scoped_refptr
<media::TestAudioInputController
>(controller
),
176 buffer_size
, feed_with_noise
));
180 SpeechRecognitionResult
GetGoodSpeechResult() {
181 SpeechRecognitionResult result
;
182 result
.hypotheses
.push_back(SpeechRecognitionHypothesis(
183 base::UTF8ToUTF16("Pictures of the moon"), 1.0F
));
187 StreamingServerState streaming_server_state_
;
188 scoped_ptr
<MockGoogleStreamingServer
> mock_streaming_server_
;
189 media::TestAudioInputControllerFactory test_audio_input_controller_factory_
;
192 // Simply loads the test page and checks if it was able to create a Speech
193 // Recognition object in JavaScript, to make sure the Web Speech API is enabled.
194 // Flaky on all platforms. http://crbug.com/396414.
195 IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest
, DISABLED_Precheck
) {
196 NavigateToURLBlockUntilNavigationsComplete(
197 shell(), GetTestUrlFromFragment("precheck"), 2);
199 EXPECT_EQ(kIdle
, streaming_server_state());
200 EXPECT_EQ("success", GetPageFragment());
203 IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest
, OneShotRecognition
) {
204 NavigateToURLBlockUntilNavigationsComplete(
205 shell(), GetTestUrlFromFragment("oneshot"), 2);
207 EXPECT_EQ(kClientDisconnected
, streaming_server_state());
208 EXPECT_EQ("goodresult1", GetPageFragment());
211 } // namespace content