diff options
Diffstat (limited to 'content/browser/speech/speech_recognition_browsertest.cc')
| -rw-r--r-- | content/browser/speech/speech_recognition_browsertest.cc | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/content/browser/speech/speech_recognition_browsertest.cc b/content/browser/speech/speech_recognition_browsertest.cc new file mode 100644 index 0000000..0b56226 --- /dev/null +++ b/content/browser/speech/speech_recognition_browsertest.cc @@ -0,0 +1,254 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/string_number_conversions.h" +#include "base/synchronization/waitable_event.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/browser/speech/input_tag_speech_dispatcher_host.h" +#include "content/browser/speech/speech_recognition_manager_impl.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "content/public/browser/notification_types.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/speech_recognition_result.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" + +using content::NavigationController; +using content::WebContents; + +namespace speech { +class FakeSpeechRecognitionManager; +} + +namespace speech { + +const char kTestResult[] = "Pictures of the moon"; + +class FakeSpeechRecognitionManager : public SpeechRecognitionManagerImpl { + public: + FakeSpeechRecognitionManager() + : caller_id_(0), + delegate_(NULL), + did_cancel_all_(false), + should_send_fake_response_(true), + recognition_started_event_(false, false) { + } + + std::string grammar() { + return grammar_; + } + + bool did_cancel_all() { + return did_cancel_all_; + } + + void set_should_send_fake_response(bool send) { + should_send_fake_response_ = send; + } + + bool should_send_fake_response() { + return should_send_fake_response_; + } + + base::WaitableEvent& recognition_started_event() { + return recognition_started_event_; + } + + // SpeechRecognitionManager methods. + virtual void StartRecognition( + InputTagSpeechDispatcherHost* delegate, + int caller_id, + int render_process_id, + int render_view_id, + const gfx::Rect& element_rect, + const std::string& language, + const std::string& grammar, + const std::string& origin_url, + net::URLRequestContextGetter* context_getter, + content::SpeechRecognitionPreferences* recognition_prefs) OVERRIDE { + VLOG(1) << "StartRecognition invoked."; + EXPECT_EQ(0, caller_id_); + EXPECT_EQ(NULL, delegate_); + caller_id_ = caller_id; + delegate_ = delegate; + grammar_ = grammar; + if (should_send_fake_response_) { + // Give the fake result in a short while. + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &FakeSpeechRecognitionManager::SetFakeRecognitionResult, + // This class does not need to be refcounted (typically done by + // PostTask) since it will outlive the test and gets released only + // when the test shuts down. Disabling refcounting here saves a bit + // of unnecessary code and the factory method can return a plain + // pointer below as required by the real code. + base::Unretained(this))); + } + recognition_started_event_.Signal(); + } + virtual void CancelRecognition(int caller_id) OVERRIDE { + VLOG(1) << "CancelRecognition invoked."; + EXPECT_EQ(caller_id_, caller_id); + caller_id_ = 0; + delegate_ = NULL; + } + virtual void StopRecording(int caller_id) OVERRIDE { + VLOG(1) << "StopRecording invoked."; + EXPECT_EQ(caller_id_, caller_id); + // Nothing to do here since we aren't really recording. + } + virtual void CancelAllRequestsWithDelegate( + InputTagSpeechDispatcherHost* delegate) OVERRIDE { + VLOG(1) << "CancelAllRequestsWithDelegate invoked."; + // delegate_ is set to NULL if a fake result was received (see below), so + // check that delegate_ matches the incoming parameter only when there is + // no fake result sent. + EXPECT_TRUE(should_send_fake_response_ || delegate_ == delegate); + did_cancel_all_ = true; + } + + private: + void SetFakeRecognitionResult() { + if (caller_id_) { // Do a check in case we were cancelled.. + VLOG(1) << "Setting fake recognition result."; + delegate_->DidCompleteRecording(caller_id_); + content::SpeechRecognitionResult results; + results.hypotheses.push_back(content::SpeechRecognitionHypothesis( + ASCIIToUTF16(kTestResult), 1.0)); + delegate_->SetRecognitionResult(caller_id_, results); + delegate_->DidCompleteRecognition(caller_id_); + caller_id_ = 0; + delegate_ = NULL; + VLOG(1) << "Finished setting fake recognition result."; + } + } + + int caller_id_; + InputTagSpeechDispatcherHost* delegate_; + std::string grammar_; + bool did_cancel_all_; + bool should_send_fake_response_; + base::WaitableEvent recognition_started_event_; +}; + +class SpeechRecognitionBrowserTest : public InProcessBrowserTest { + public: + // InProcessBrowserTest methods + virtual void SetUpCommandLine(CommandLine* command_line) { + EXPECT_TRUE(!command_line->HasSwitch(switches::kDisableSpeechInput)); + } + + GURL testUrl(const FilePath::CharType* filename) { + const FilePath kTestDir(FILE_PATH_LITERAL("speech")); + return ui_test_utils::GetTestUrl(kTestDir, FilePath(filename)); + } + + protected: + void LoadAndStartSpeechRecognitionTest(const FilePath::CharType* filename) { + // The test page calculates the speech button's coordinate in the page on + // load & sets that coordinate in the URL fragment. We send mouse down & up + // events at that coordinate to trigger speech recognition. + GURL test_url = testUrl(filename); + ui_test_utils::NavigateToURL(browser(), test_url); + + WebKit::WebMouseEvent mouse_event; + mouse_event.type = WebKit::WebInputEvent::MouseDown; + mouse_event.button = WebKit::WebMouseEvent::ButtonLeft; + mouse_event.x = 0; + mouse_event.y = 0; + mouse_event.clickCount = 1; + WebContents* web_contents = browser()->GetSelectedWebContents(); + + ui_test_utils::WindowedNotificationObserver observer( + content::NOTIFICATION_LOAD_STOP, + content::Source<NavigationController>(&web_contents->GetController())); + web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); + mouse_event.type = WebKit::WebInputEvent::MouseUp; + web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); + fake_speech_recognition_manager_.recognition_started_event().Wait(); + + // We should wait for a navigation event, raised by the test page JS code + // upon the onwebkitspeechchange event, in all cases except when the + // speech response is inhibited. + if (fake_speech_recognition_manager_.should_send_fake_response()) + observer.Wait(); + } + + void RunSpeechRecognitionTest(const FilePath::CharType* filename) { + // The fake speech input manager would receive the speech input + // request and return the test string as recognition result. The test page + // then sets the URL fragment as 'pass' if it received the expected string. + LoadAndStartSpeechRecognitionTest(filename); + + EXPECT_EQ("pass", browser()->GetSelectedWebContents()->GetURL().ref()); + } + + // InProcessBrowserTest methods. + virtual void SetUpInProcessBrowserTestFixture() { + fake_speech_recognition_manager_.set_should_send_fake_response(true); + speech_recognition_manager_ = &fake_speech_recognition_manager_; + + // Inject the fake manager factory so that the test result is returned to + // the web page. + InputTagSpeechDispatcherHost::set_manager(speech_recognition_manager_); + } + + virtual void TearDownInProcessBrowserTestFixture() { + speech_recognition_manager_ = NULL; + } + + FakeSpeechRecognitionManager fake_speech_recognition_manager_; + + // This is used by the static |fakeManager|, and it is a pointer rather than a + // direct instance per the style guide. + static SpeechRecognitionManagerImpl* speech_recognition_manager_; +}; + +SpeechRecognitionManagerImpl* + SpeechRecognitionBrowserTest::speech_recognition_manager_ = NULL; + +// TODO(satish): Once this flakiness has been fixed, add a second test here to +// check for sending many clicks in succession to the speech button and verify +// that it doesn't cause any crash but works as expected. This should act as the +// test for http://crbug.com/59173 +// +// TODO(satish): Similar to above, once this flakiness has been fixed add +// another test here to check that when speech recognition is in progress and +// a renderer crashes, we get a call to +// SpeechRecognitionManager::CancelAllRequestsWithDelegate. +IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, TestBasicRecognition) { + RunSpeechRecognitionTest(FILE_PATH_LITERAL("basic_recognition.html")); + EXPECT_TRUE(fake_speech_recognition_manager_.grammar().empty()); +} + +IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, GrammarAttribute) { + RunSpeechRecognitionTest(FILE_PATH_LITERAL("grammar_attribute.html")); + EXPECT_EQ("http://example.com/grammar.xml", + fake_speech_recognition_manager_.grammar()); +} + +IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, TestCancelAll) { + // The test checks that the cancel-all callback gets issued when a session + // is pending, so don't send a fake response. + // We are not expecting a navigation event being raised from the JS of the + // test page JavaScript in this case. + fake_speech_recognition_manager_.set_should_send_fake_response(false); + + LoadAndStartSpeechRecognitionTest( + FILE_PATH_LITERAL("basic_recognition.html")); + + // Make the renderer crash. This should trigger + // InputTagSpeechDispatcherHost to cancel all pending sessions. + GURL test_url("about:crash"); + ui_test_utils::NavigateToURL(browser(), test_url); + + EXPECT_TRUE(fake_speech_recognition_manager_.did_cancel_all()); +} + +} // namespace speech |
