OLD | NEW |
| (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/string_number_conversions.h" | |
9 #include "base/synchronization/waitable_event.h" | |
10 #include "base/utf_string_conversions.h" | |
11 #include "chrome/browser/ui/browser.h" | |
12 #include "chrome/test/base/in_process_browser_test.h" | |
13 #include "chrome/test/base/ui_test_utils.h" | |
14 #include "content/browser/renderer_host/render_view_host_impl.h" | |
15 #include "content/browser/speech/speech_input_dispatcher_host.h" | |
16 #include "content/browser/speech/speech_input_manager_impl.h" | |
17 #include "content/browser/tab_contents/tab_contents.h" | |
18 #include "content/public/browser/notification_types.h" | |
19 #include "content/public/common/content_switches.h" | |
20 #include "content/public/common/speech_input_result.h" | |
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" | |
22 | |
23 using content::NavigationController; | |
24 using content::WebContents; | |
25 | |
26 namespace speech_input { | |
27 class FakeSpeechInputManager; | |
28 } | |
29 | |
30 namespace speech_input { | |
31 | |
32 const char kTestResult[] = "Pictures of the moon"; | |
33 | |
34 class FakeSpeechInputManager : public SpeechInputManagerImpl { | |
35 public: | |
36 FakeSpeechInputManager() | |
37 : caller_id_(0), | |
38 delegate_(NULL), | |
39 did_cancel_all_(false), | |
40 should_send_fake_response_(true), | |
41 recognition_started_event_(false, false) { | |
42 } | |
43 | |
44 std::string grammar() { | |
45 return grammar_; | |
46 } | |
47 | |
48 bool did_cancel_all() { | |
49 return did_cancel_all_; | |
50 } | |
51 | |
52 void set_should_send_fake_response(bool send) { | |
53 should_send_fake_response_ = send; | |
54 } | |
55 | |
56 bool should_send_fake_response() { | |
57 return should_send_fake_response_; | |
58 } | |
59 | |
60 base::WaitableEvent& recognition_started_event() { | |
61 return recognition_started_event_; | |
62 } | |
63 | |
64 // SpeechInputManager methods. | |
65 virtual void StartRecognition( | |
66 SpeechInputDispatcherHost* delegate, | |
67 int caller_id, | |
68 int render_process_id, | |
69 int render_view_id, | |
70 const gfx::Rect& element_rect, | |
71 const std::string& language, | |
72 const std::string& grammar, | |
73 const std::string& origin_url, | |
74 net::URLRequestContextGetter* context_getter, | |
75 content::SpeechInputPreferences* speech_input_prefs) OVERRIDE { | |
76 VLOG(1) << "StartRecognition invoked."; | |
77 EXPECT_EQ(0, caller_id_); | |
78 EXPECT_EQ(NULL, delegate_); | |
79 caller_id_ = caller_id; | |
80 delegate_ = delegate; | |
81 grammar_ = grammar; | |
82 if (should_send_fake_response_) { | |
83 // Give the fake result in a short while. | |
84 MessageLoop::current()->PostTask(FROM_HERE, base::Bind( | |
85 &FakeSpeechInputManager::SetFakeRecognitionResult, | |
86 // This class does not need to be refcounted (typically done by | |
87 // PostTask) since it will outlive the test and gets released only | |
88 // when the test shuts down. Disabling refcounting here saves a bit | |
89 // of unnecessary code and the factory method can return a plain | |
90 // pointer below as required by the real code. | |
91 base::Unretained(this))); | |
92 } | |
93 recognition_started_event_.Signal(); | |
94 } | |
95 virtual void CancelRecognition(int caller_id) OVERRIDE { | |
96 VLOG(1) << "CancelRecognition invoked."; | |
97 EXPECT_EQ(caller_id_, caller_id); | |
98 caller_id_ = 0; | |
99 delegate_ = NULL; | |
100 } | |
101 virtual void StopRecording(int caller_id) OVERRIDE { | |
102 VLOG(1) << "StopRecording invoked."; | |
103 EXPECT_EQ(caller_id_, caller_id); | |
104 // Nothing to do here since we aren't really recording. | |
105 } | |
106 virtual void CancelAllRequestsWithDelegate( | |
107 SpeechInputDispatcherHost* delegate) OVERRIDE { | |
108 VLOG(1) << "CancelAllRequestsWithDelegate invoked."; | |
109 // delegate_ is set to NULL if a fake result was received (see below), so | |
110 // check that delegate_ matches the incoming parameter only when there is | |
111 // no fake result sent. | |
112 EXPECT_TRUE(should_send_fake_response_ || delegate_ == delegate); | |
113 did_cancel_all_ = true; | |
114 } | |
115 | |
116 private: | |
117 void SetFakeRecognitionResult() { | |
118 if (caller_id_) { // Do a check in case we were cancelled.. | |
119 VLOG(1) << "Setting fake recognition result."; | |
120 delegate_->DidCompleteRecording(caller_id_); | |
121 content::SpeechInputResult results; | |
122 results.hypotheses.push_back(content::SpeechInputHypothesis( | |
123 ASCIIToUTF16(kTestResult), 1.0)); | |
124 delegate_->SetRecognitionResult(caller_id_, results); | |
125 delegate_->DidCompleteRecognition(caller_id_); | |
126 caller_id_ = 0; | |
127 delegate_ = NULL; | |
128 VLOG(1) << "Finished setting fake recognition result."; | |
129 } | |
130 } | |
131 | |
132 int caller_id_; | |
133 SpeechInputDispatcherHost* delegate_; | |
134 std::string grammar_; | |
135 bool did_cancel_all_; | |
136 bool should_send_fake_response_; | |
137 base::WaitableEvent recognition_started_event_; | |
138 }; | |
139 | |
140 class SpeechInputBrowserTest : public InProcessBrowserTest { | |
141 public: | |
142 // InProcessBrowserTest methods | |
143 virtual void SetUpCommandLine(CommandLine* command_line) { | |
144 EXPECT_TRUE(!command_line->HasSwitch(switches::kDisableSpeechInput)); | |
145 } | |
146 | |
147 GURL testUrl(const FilePath::CharType* filename) { | |
148 const FilePath kTestDir(FILE_PATH_LITERAL("speech")); | |
149 return ui_test_utils::GetTestUrl(kTestDir, FilePath(filename)); | |
150 } | |
151 | |
152 protected: | |
153 void LoadAndStartSpeechInputTest(const FilePath::CharType* filename) { | |
154 // The test page calculates the speech button's coordinate in the page on | |
155 // load & sets that coordinate in the URL fragment. We send mouse down & up | |
156 // events at that coordinate to trigger speech recognition. | |
157 GURL test_url = testUrl(filename); | |
158 ui_test_utils::NavigateToURL(browser(), test_url); | |
159 | |
160 WebKit::WebMouseEvent mouse_event; | |
161 mouse_event.type = WebKit::WebInputEvent::MouseDown; | |
162 mouse_event.button = WebKit::WebMouseEvent::ButtonLeft; | |
163 mouse_event.x = 0; | |
164 mouse_event.y = 0; | |
165 mouse_event.clickCount = 1; | |
166 WebContents* web_contents = browser()->GetSelectedWebContents(); | |
167 | |
168 ui_test_utils::WindowedNotificationObserver observer( | |
169 content::NOTIFICATION_LOAD_STOP, | |
170 content::Source<NavigationController>(&web_contents->GetController())); | |
171 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); | |
172 mouse_event.type = WebKit::WebInputEvent::MouseUp; | |
173 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); | |
174 fake_speech_input_manager_.recognition_started_event().Wait(); | |
175 | |
176 // We should wait for a navigation event, raised by the test page JS code | |
177 // upon the onwebkitspeechchange event, in all cases except when the | |
178 // speech response is inhibited. | |
179 if (fake_speech_input_manager_.should_send_fake_response()) | |
180 observer.Wait(); | |
181 } | |
182 | |
183 void RunSpeechInputTest(const FilePath::CharType* filename) { | |
184 // The fake speech input manager would receive the speech input | |
185 // request and return the test string as recognition result. The test page | |
186 // then sets the URL fragment as 'pass' if it received the expected string. | |
187 LoadAndStartSpeechInputTest(filename); | |
188 | |
189 EXPECT_EQ("pass", browser()->GetSelectedWebContents()->GetURL().ref()); | |
190 } | |
191 | |
192 // InProcessBrowserTest methods. | |
193 virtual void SetUpInProcessBrowserTestFixture() { | |
194 fake_speech_input_manager_.set_should_send_fake_response(true); | |
195 speech_input_manager_ = &fake_speech_input_manager_; | |
196 | |
197 // Inject the fake manager factory so that the test result is returned to | |
198 // the web page. | |
199 SpeechInputDispatcherHost::set_manager(speech_input_manager_); | |
200 } | |
201 | |
202 virtual void TearDownInProcessBrowserTestFixture() { | |
203 speech_input_manager_ = NULL; | |
204 } | |
205 | |
206 FakeSpeechInputManager fake_speech_input_manager_; | |
207 | |
208 // This is used by the static |fakeManager|, and it is a pointer rather than a | |
209 // direct instance per the style guide. | |
210 static SpeechInputManagerImpl* speech_input_manager_; | |
211 }; | |
212 | |
213 SpeechInputManagerImpl* SpeechInputBrowserTest::speech_input_manager_ = NULL; | |
214 | |
215 // TODO(satish): Once this flakiness has been fixed, add a second test here to | |
216 // check for sending many clicks in succession to the speech button and verify | |
217 // that it doesn't cause any crash but works as expected. This should act as the | |
218 // test for http://crbug.com/59173 | |
219 // | |
220 // TODO(satish): Similar to above, once this flakiness has been fixed add | |
221 // another test here to check that when speech recognition is in progress and | |
222 // a renderer crashes, we get a call to | |
223 // SpeechInputManager::CancelAllRequestsWithDelegate. | |
224 IN_PROC_BROWSER_TEST_F(SpeechInputBrowserTest, TestBasicRecognition) { | |
225 RunSpeechInputTest(FILE_PATH_LITERAL("basic_recognition.html")); | |
226 EXPECT_TRUE(fake_speech_input_manager_.grammar().empty()); | |
227 } | |
228 | |
229 IN_PROC_BROWSER_TEST_F(SpeechInputBrowserTest, GrammarAttribute) { | |
230 RunSpeechInputTest(FILE_PATH_LITERAL("grammar_attribute.html")); | |
231 EXPECT_EQ("http://example.com/grammar.xml", | |
232 fake_speech_input_manager_.grammar()); | |
233 } | |
234 | |
235 IN_PROC_BROWSER_TEST_F(SpeechInputBrowserTest, TestCancelAll) { | |
236 // The test checks that the cancel-all callback gets issued when a session | |
237 // is pending, so don't send a fake response. | |
238 // We are not expecting a navigation event being raised from the JS of the | |
239 // test page JavaScript in this case. | |
240 fake_speech_input_manager_.set_should_send_fake_response(false); | |
241 | |
242 LoadAndStartSpeechInputTest(FILE_PATH_LITERAL("basic_recognition.html")); | |
243 | |
244 // Make the renderer crash. This should trigger SpeechInputDispatcherHost to | |
245 // cancel all pending sessions. | |
246 GURL test_url("about:crash"); | |
247 ui_test_utils::NavigateToURL(browser(), test_url); | |
248 | |
249 EXPECT_TRUE(fake_speech_input_manager_.did_cancel_all()); | |
250 } | |
251 | |
252 } // namespace speech_input | |
OLD | NEW |