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

Side by Side Diff: content/browser/speech/speech_recognition_browsertest.cc

Issue 10703141: End-to-end browser tests for speech recognition. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Moved test to /chrome/test Created 8 years, 5 months 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/bind.h"
6 #include "base/command_line.h"
7 #include "base/file_path.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/string_number_conversions.h"
10 #include "base/synchronization/waitable_event.h"
11 #include "base/utf_string_conversions.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_tabstrip.h"
14 #include "chrome/test/base/in_process_browser_test.h"
15 #include "chrome/test/base/ui_test_utils.h"
16 #include "content/browser/renderer_host/render_view_host_impl.h"
17 #include "content/browser/speech/input_tag_speech_dispatcher_host.h"
18 #include "content/browser/web_contents/web_contents_impl.h"
19 #include "content/public/browser/notification_types.h"
20 #include "content/public/browser/speech_recognition_manager.h"
21 #include "content/public/browser/speech_recognition_session_config.h"
22 #include "content/public/browser/speech_recognition_session_context.h"
23 #include "content/public/common/content_switches.h"
24 #include "content/public/common/speech_recognition_error.h"
25 #include "content/public/common/speech_recognition_result.h"
26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
27
28 using content::SpeechRecognitionEventListener;
29 using content::SpeechRecognitionSessionConfig;
30 using content::SpeechRecognitionSessionContext;
31 using content::NavigationController;
32 using content::WebContents;
33
34 namespace speech {
35 class FakeSpeechRecognitionManager;
36 }
37
38 namespace speech {
39
40 const char kTestResult[] = "Pictures of the moon";
41
42 class FakeSpeechRecognitionManager : public content::SpeechRecognitionManager {
43 public:
44 FakeSpeechRecognitionManager()
45 : session_id_(0),
46 listener_(NULL),
47 did_cancel_all_(false),
48 should_send_fake_response_(true),
49 recognition_started_event_(false, false) {
50 }
51
52 std::string grammar() {
53 return grammar_;
54 }
55
56 bool did_cancel_all() {
57 return did_cancel_all_;
58 }
59
60 void set_should_send_fake_response(bool send) {
61 should_send_fake_response_ = send;
62 }
63
64 bool should_send_fake_response() {
65 return should_send_fake_response_;
66 }
67
68 base::WaitableEvent& recognition_started_event() {
69 return recognition_started_event_;
70 }
71
72 // SpeechRecognitionManager methods.
73 virtual int CreateSession(
74 const content::SpeechRecognitionSessionConfig& config) OVERRIDE {
75 VLOG(1) << "FAKE CreateSession invoked.";
76 EXPECT_EQ(0, session_id_);
77 EXPECT_EQ(NULL, listener_);
78 listener_ = config.event_listener;
79 if (config.grammars.size() > 0)
80 grammar_ = config.grammars[0].url;
81 session_ctx_ = config.initial_context;
82 session_config_ = config;
83 session_id_ = 1;
84 return session_id_;
85 }
86
87 virtual void StartSession(int session_id) OVERRIDE {
88 VLOG(1) << "FAKE StartSession invoked.";
89 EXPECT_EQ(session_id, session_id_);
90 EXPECT_TRUE(listener_ != NULL);
91
92 if (should_send_fake_response_) {
93 // Give the fake result in a short while.
94 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
95 &FakeSpeechRecognitionManager::SetFakeRecognitionResult,
96 // This class does not need to be refcounted (typically done by
97 // PostTask) since it will outlive the test and gets released only
98 // when the test shuts down. Disabling refcounting here saves a bit
99 // of unnecessary code and the factory method can return a plain
100 // pointer below as required by the real code.
101 base::Unretained(this)));
102 }
103 recognition_started_event_.Signal();
104 }
105
106 virtual void AbortSession(int session_id) OVERRIDE {
107 VLOG(1) << "FAKE AbortSession invoked.";
108 EXPECT_EQ(session_id_, session_id);
109 session_id_ = 0;
110 listener_ = NULL;
111 }
112
113 virtual void StopAudioCaptureForSession(int session_id) OVERRIDE {
114 VLOG(1) << "StopRecording invoked.";
115 EXPECT_EQ(session_id_, session_id);
116 // Nothing to do here since we aren't really recording.
117 }
118
119 virtual void AbortAllSessionsForListener(
120 content::SpeechRecognitionEventListener* listener) OVERRIDE {
121 VLOG(1) << "CancelAllRequestsWithDelegate invoked.";
122 // listener_ is set to NULL if a fake result was received (see below), so
123 // check that listener_ matches the incoming parameter only when there is
124 // no fake result sent.
125 EXPECT_TRUE(should_send_fake_response_ || listener_ == listener);
126 did_cancel_all_ = true;
127 }
128
129 virtual void AbortAllSessionsForRenderView(int render_process_id,
130 int render_view_id) OVERRIDE {
131 NOTREACHED();
132 }
133 virtual bool HasAudioInputDevices() OVERRIDE { return true; }
134 virtual bool IsCapturingAudio() OVERRIDE { return true; }
135 virtual string16 GetAudioInputDeviceModel() OVERRIDE { return string16(); }
136 virtual void ShowAudioInputSettings() OVERRIDE {}
137
138 virtual int GetSession(int render_process_id,
139 int render_view_id,
140 int request_id) const OVERRIDE {
141 return session_ctx_.render_process_id == render_process_id &&
142 session_ctx_.render_view_id == render_view_id &&
143 session_ctx_.request_id == request_id;
144 }
145
146 virtual const SpeechRecognitionSessionConfig& GetSessionConfig(
147 int session_id) const OVERRIDE {
148 EXPECT_EQ(session_id, session_id_);
149 return session_config_;
150 }
151
152 virtual content::SpeechRecognitionSessionContext GetSessionContext(
153 int session_id) const OVERRIDE {
154 EXPECT_EQ(session_id, session_id_);
155 return session_ctx_;
156 }
157
158 private:
159 void SetFakeRecognitionResult() {
160 if (session_id_) { // Do a check in case we were cancelled..
161 VLOG(1) << "Setting fake recognition result.";
162 listener_->OnAudioEnd(session_id_);
163 content::SpeechRecognitionResult results;
164 results.hypotheses.push_back(content::SpeechRecognitionHypothesis(
165 ASCIIToUTF16(kTestResult), 1.0));
166 listener_->OnRecognitionResult(session_id_, results);
167 listener_->OnRecognitionEnd(session_id_);
168 session_id_ = 0;
169 listener_ = NULL;
170 VLOG(1) << "Finished setting fake recognition result.";
171 }
172 }
173
174 int session_id_;
175 SpeechRecognitionEventListener* listener_;
176 SpeechRecognitionSessionConfig session_config_;
177 SpeechRecognitionSessionContext session_ctx_;
178 std::string grammar_;
179 bool did_cancel_all_;
180 bool should_send_fake_response_;
181 base::WaitableEvent recognition_started_event_;
182 };
183
184 class SpeechRecognitionBrowserTest : public InProcessBrowserTest {
185 public:
186 // InProcessBrowserTest methods
187 virtual void SetUpCommandLine(CommandLine* command_line) {
188 EXPECT_TRUE(!command_line->HasSwitch(switches::kDisableSpeechInput));
189 }
190
191 GURL testUrl(const FilePath::CharType* filename) {
192 const FilePath kTestDir(FILE_PATH_LITERAL("speech"));
193 return ui_test_utils::GetTestUrl(kTestDir, FilePath(filename));
194 }
195
196 protected:
197 void LoadAndStartSpeechRecognitionTest(const FilePath::CharType* filename) {
198 // The test page calculates the speech button's coordinate in the page on
199 // load & sets that coordinate in the URL fragment. We send mouse down & up
200 // events at that coordinate to trigger speech recognition.
201 GURL test_url = testUrl(filename);
202 ui_test_utils::NavigateToURL(browser(), test_url);
203
204 WebKit::WebMouseEvent mouse_event;
205 mouse_event.type = WebKit::WebInputEvent::MouseDown;
206 mouse_event.button = WebKit::WebMouseEvent::ButtonLeft;
207 mouse_event.x = 0;
208 mouse_event.y = 0;
209 mouse_event.clickCount = 1;
210 WebContents* web_contents = chrome::GetActiveWebContents(browser());
211
212 ui_test_utils::WindowedNotificationObserver observer(
213 content::NOTIFICATION_LOAD_STOP,
214 content::Source<NavigationController>(&web_contents->GetController()));
215 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
216 mouse_event.type = WebKit::WebInputEvent::MouseUp;
217 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
218 fake_speech_recognition_manager_.recognition_started_event().Wait();
219
220 // We should wait for a navigation event, raised by the test page JS code
221 // upon the onwebkitspeechchange event, in all cases except when the
222 // speech response is inhibited.
223 if (fake_speech_recognition_manager_.should_send_fake_response())
224 observer.Wait();
225 }
226
227 void RunSpeechRecognitionTest(const FilePath::CharType* filename) {
228 // The fake speech input manager would receive the speech input
229 // request and return the test string as recognition result. The test page
230 // then sets the URL fragment as 'pass' if it received the expected string.
231 LoadAndStartSpeechRecognitionTest(filename);
232
233 EXPECT_EQ("pass", chrome::GetActiveWebContents(browser())->GetURL().ref());
234 }
235
236 // InProcessBrowserTest methods.
237 virtual void SetUpInProcessBrowserTestFixture() {
238 fake_speech_recognition_manager_.set_should_send_fake_response(true);
239 speech_recognition_manager_ = &fake_speech_recognition_manager_;
240
241 // Inject the fake manager factory so that the test result is returned to
242 // the web page.
243 InputTagSpeechDispatcherHost::SetManagerForTests(
244 speech_recognition_manager_);
245 }
246
247 virtual void TearDownInProcessBrowserTestFixture() {
248 speech_recognition_manager_ = NULL;
249 }
250
251 FakeSpeechRecognitionManager fake_speech_recognition_manager_;
252
253 // This is used by the static |fakeManager|, and it is a pointer rather than a
254 // direct instance per the style guide.
255 static content::SpeechRecognitionManager* speech_recognition_manager_;
256 };
257
258 content::SpeechRecognitionManager*
259 SpeechRecognitionBrowserTest::speech_recognition_manager_ = NULL;
260
261 // TODO(satish): Once this flakiness has been fixed, add a second test here to
262 // check for sending many clicks in succession to the speech button and verify
263 // that it doesn't cause any crash but works as expected. This should act as the
264 // test for http://crbug.com/59173
265 //
266 // TODO(satish): Similar to above, once this flakiness has been fixed add
267 // another test here to check that when speech recognition is in progress and
268 // a renderer crashes, we get a call to
269 // SpeechRecognitionManager::CancelAllRequestsWithDelegate.
270 IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, TestBasicRecognition) {
271 RunSpeechRecognitionTest(FILE_PATH_LITERAL("basic_recognition.html"));
272 EXPECT_TRUE(fake_speech_recognition_manager_.grammar().empty());
273 }
274
275 IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, GrammarAttribute) {
276 RunSpeechRecognitionTest(FILE_PATH_LITERAL("grammar_attribute.html"));
277 EXPECT_EQ("http://example.com/grammar.xml",
278 fake_speech_recognition_manager_.grammar());
279 }
280
281 IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, TestCancelAll) {
282 // The test checks that the cancel-all callback gets issued when a session
283 // is pending, so don't send a fake response.
284 // We are not expecting a navigation event being raised from the JS of the
285 // test page JavaScript in this case.
286 fake_speech_recognition_manager_.set_should_send_fake_response(false);
287
288 LoadAndStartSpeechRecognitionTest(
289 FILE_PATH_LITERAL("basic_recognition.html"));
290
291 // Make the renderer crash. This should trigger
292 // InputTagSpeechDispatcherHost to cancel all pending sessions.
293 GURL test_url("about:crash");
294 ui_test_utils::NavigateToURL(browser(), test_url);
295
296 EXPECT_TRUE(fake_speech_recognition_manager_.did_cancel_all());
297 }
298
299 } // namespace speech
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698