| // 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/message_loop.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome/browser/extensions/extension_apitest.h" |
| #include "chrome/browser/speech/speech_input_extension_api.h" |
| #include "chrome/browser/speech/speech_input_extension_manager.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/common/chrome_notification_types.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "content/public/browser/speech_recognition_event_listener.h" |
| #include "content/public/common/speech_recognition_error.h" |
| #include "content/public/common/speech_recognition_result.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using content::BrowserThread; |
| |
| namespace net { |
| class URLRequestContextGetter; |
| } |
| |
| namespace { |
| const int kSessionIDForTests = 0; |
| } |
| |
| // Mock class used to test the extension speech input API. |
| class SpeechInputExtensionApiTest : public ExtensionApiTest, |
| public SpeechInputExtensionInterface { |
| public: |
| SpeechInputExtensionApiTest(); |
| virtual ~SpeechInputExtensionApiTest(); |
| |
| void SetRecordingDevicesAvailable(bool available) { |
| recording_devices_available_ = available; |
| } |
| |
| void SetRecognitionError(content::SpeechRecognitionErrorCode error) { |
| next_error_ = error; |
| } |
| |
| void SetRecognitionResult(const content::SpeechRecognitionResult& result) { |
| next_result_ = result; |
| } |
| |
| void SetRecognitionDelay(int result_delay_ms) { |
| result_delay_ms_ = result_delay_ms; |
| } |
| |
| // Used as delay when the corresponding call should not be dispatched. |
| static const int kDontDispatchCall = -1; |
| |
| // InProcessBrowserTest methods. |
| virtual void SetUpOnMainThread() OVERRIDE { |
| manager_ = SpeechInputExtensionManager::GetForProfile(browser()->profile()); |
| } |
| |
| // ExtensionApiTest methods. |
| virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { |
| ExtensionApiTest::SetUpCommandLine(command_line); |
| command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis); |
| } |
| |
| // SpeechInputExtensionInterface methods. |
| virtual bool HasAudioInputDevices() OVERRIDE { |
| return recording_devices_available_; |
| } |
| |
| virtual bool IsCapturingAudio() OVERRIDE { |
| // Only the mock recognizer is supposed to be recording during testing. |
| return HasValidRecognizer(); |
| } |
| |
| virtual bool HasValidRecognizer() OVERRIDE { |
| return recognizer_is_valid_; |
| } |
| |
| virtual void StartRecording( |
| content::SpeechRecognitionEventListener* listener, |
| net::URLRequestContextGetter* context_getter, |
| const std::string& extension_name, |
| const std::string& language, |
| const std::string& grammar, |
| bool filter_profanities, |
| int render_process_id) OVERRIDE; |
| |
| virtual void StopRecording(bool recognition_failed) OVERRIDE; |
| |
| SpeechInputExtensionManager* GetManager() { |
| return manager_.get(); |
| } |
| |
| // Auxiliary class used to hook the API manager into the test during its |
| // lifetime. Required since browser() is not available during the set up |
| // or tear down callbacks, or during the test class construction. |
| class AutoManagerHook { |
| public: |
| explicit AutoManagerHook(SpeechInputExtensionApiTest* test) |
| : test_(test) { |
| test_->GetManager()->SetSpeechInputExtensionInterface(test_); |
| } |
| |
| ~AutoManagerHook() { |
| test_->GetManager()->SetSpeechInputExtensionInterface(NULL); |
| } |
| |
| private: |
| SpeechInputExtensionApiTest* test_; |
| }; |
| |
| private: |
| void ProvideResults(); |
| |
| bool recording_devices_available_; |
| bool recognizer_is_valid_; |
| content::SpeechRecognitionErrorCode next_error_; |
| content::SpeechRecognitionResult next_result_; |
| int result_delay_ms_; |
| |
| scoped_refptr<SpeechInputExtensionManager> manager_; |
| }; |
| |
| SpeechInputExtensionApiTest::SpeechInputExtensionApiTest() |
| : recording_devices_available_(true), |
| recognizer_is_valid_(false), |
| next_error_(content::SPEECH_RECOGNITION_ERROR_NONE), |
| result_delay_ms_(0) { |
| } |
| |
| SpeechInputExtensionApiTest::~SpeechInputExtensionApiTest() { |
| } |
| |
| void SpeechInputExtensionApiTest::StartRecording( |
| content::SpeechRecognitionEventListener* listener, |
| net::URLRequestContextGetter* context_getter, |
| const std::string& extension_name, |
| const std::string& language, |
| const std::string& grammar, |
| bool filter_profanities, |
| int render_process_id) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| recognizer_is_valid_ = true; |
| |
| // Notify that recording started. |
| MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&SpeechInputExtensionManager::OnAudioStart, |
| GetManager(), |
| kSessionIDForTests), |
| base::TimeDelta()); |
| |
| // Notify sound start in the input device. |
| MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&SpeechInputExtensionManager::OnSoundStart, |
| GetManager(), |
| kSessionIDForTests), |
| base::TimeDelta()); |
| |
| if (result_delay_ms_ != kDontDispatchCall) { |
| // Dispatch the recognition results. |
| MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&SpeechInputExtensionApiTest::ProvideResults, this), |
| base::TimeDelta::FromMilliseconds(result_delay_ms_)); |
| } |
| } |
| |
| void SpeechInputExtensionApiTest::StopRecording(bool recognition_failed) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| recognizer_is_valid_ = false; |
| } |
| |
| void SpeechInputExtensionApiTest::ProvideResults() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| if (next_error_ != content::SPEECH_RECOGNITION_ERROR_NONE) { |
| GetManager()->OnRecognitionError( |
| kSessionIDForTests, content::SpeechRecognitionError(next_error_)); |
| return; |
| } |
| |
| GetManager()->OnSoundEnd(kSessionIDForTests); |
| GetManager()->OnAudioEnd(kSessionIDForTests); |
| GetManager()->OnRecognitionResult(kSessionIDForTests, next_result_); |
| GetManager()->OnRecognitionEnd(kSessionIDForTests); |
| } |
| |
| // Every test should leave the manager in the idle state when finished. |
| IN_PROC_BROWSER_TEST_F(SpeechInputExtensionApiTest, StartStopTest) { |
| AutoManagerHook hook(this); |
| |
| SetRecognitionDelay(kDontDispatchCall); |
| ASSERT_TRUE(RunExtensionTest("speech_input/start_stop")) << message_; |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SpeechInputExtensionApiTest, NoDevicesAvailable) { |
| AutoManagerHook hook(this); |
| |
| SetRecordingDevicesAvailable(false); |
| ASSERT_TRUE(RunExtensionTest("speech_input/start_error")) << message_; |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SpeechInputExtensionApiTest, RecognitionSuccessful) { |
| AutoManagerHook hook(this); |
| |
| content::SpeechRecognitionResult result; |
| result.hypotheses.push_back( |
| content::SpeechRecognitionHypothesis( |
| UTF8ToUTF16("this is a test"), 0.99)); |
| SetRecognitionResult(result); |
| ASSERT_TRUE(RunExtensionTest("speech_input/recognition")) << message_; |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SpeechInputExtensionApiTest, RecognitionError) { |
| AutoManagerHook hook(this); |
| |
| SetRecognitionError(content::SPEECH_RECOGNITION_ERROR_NETWORK); |
| ASSERT_TRUE(RunExtensionTest("speech_input/recognition_error")) << message_; |
| } |