| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/speech/network_speech_recognizer.h" |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <optional> |
| |
| #include "base/command_line.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/speech/speech_recognizer_delegate.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/fake_speech_recognition_manager.h" |
| #include "content/public/test/test_utils.h" |
| #include "media/mojo/mojom/speech_recognition.mojom.h" |
| #include "media/mojo/mojom/speech_recognition_service.mojom.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::DoDefault; |
| using ::testing::InvokeWithoutArgs; |
| |
| class MockSpeechRecognizerDelegate : public SpeechRecognizerDelegate { |
| public: |
| MockSpeechRecognizerDelegate() = default; |
| |
| base::WeakPtr<MockSpeechRecognizerDelegate> GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| MOCK_METHOD3( |
| OnSpeechResult, |
| void(const std::u16string& text, |
| bool is_final, |
| const std::optional<media::SpeechRecognitionResult>& timing)); |
| MOCK_METHOD1(OnSpeechSoundLevelChanged, void(int16_t)); |
| MOCK_METHOD1(OnSpeechRecognitionStateChanged, void(SpeechRecognizerStatus)); |
| MOCK_METHOD0(OnSpeechRecognitionStopped, void()); |
| MOCK_METHOD1(OnLanguageIdentificationEvent, |
| void(media::mojom::LanguageIdentificationEventPtr)); |
| |
| private: |
| base::WeakPtrFactory<MockSpeechRecognizerDelegate> weak_factory_{this}; |
| }; |
| |
| class NetworkSpeechRecognizerBrowserTest : public InProcessBrowserTest { |
| public: |
| NetworkSpeechRecognizerBrowserTest() = default; |
| ~NetworkSpeechRecognizerBrowserTest() override = default; |
| NetworkSpeechRecognizerBrowserTest( |
| const NetworkSpeechRecognizerBrowserTest&) = delete; |
| NetworkSpeechRecognizerBrowserTest& operator=( |
| const NetworkSpeechRecognizerBrowserTest&) = delete; |
| |
| void SetUpOnMainThread() override { |
| fake_speech_recognition_manager_ = |
| std::make_unique<content::FakeSpeechRecognitionManager>(); |
| fake_speech_recognition_manager_->set_should_send_fake_response(false); |
| content::SpeechRecognitionManager::SetManagerForTesting( |
| fake_speech_recognition_manager_.get()); |
| mock_speech_delegate_ = std::make_unique<MockSpeechRecognizerDelegate>(); |
| } |
| |
| void TearDownOnMainThread() override { |
| content::RunAllPendingInMessageLoop(content::BrowserThread::IO); |
| } |
| |
| protected: |
| std::unique_ptr<content::FakeSpeechRecognitionManager> |
| fake_speech_recognition_manager_; |
| std::unique_ptr<MockSpeechRecognizerDelegate> mock_speech_delegate_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(NetworkSpeechRecognizerBrowserTest, RecognizeSpeech) { |
| NetworkSpeechRecognizer recognizer( |
| mock_speech_delegate_->GetWeakPtr(), |
| browser() |
| ->profile() |
| ->GetDefaultStoragePartition() |
| ->GetURLLoaderFactoryForBrowserProcessIOThread(), |
| "en" /* locale */); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(*mock_speech_delegate_, |
| OnSpeechRecognitionStateChanged(SPEECH_RECOGNIZER_READY)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*mock_speech_delegate_, |
| OnSpeechRecognitionStateChanged(SPEECH_RECOGNIZER_RECOGNIZING)) |
| .Times(1); |
| recognizer.Start(); |
| fake_speech_recognition_manager_->WaitForRecognitionStarted(); |
| base::RunLoop().RunUntilIdle(); |
| |
| base::RunLoop first_response_loop; |
| EXPECT_CALL(*mock_speech_delegate_, |
| OnSpeechRecognitionStateChanged(SPEECH_RECOGNIZER_IN_SPEECH)) |
| .Times(1); |
| EXPECT_CALL( |
| *mock_speech_delegate_, |
| OnSpeechResult(std::u16string(u"Pictures of the moon"), true, testing::_)) |
| .WillOnce(InvokeWithoutArgs(&first_response_loop, &base::RunLoop::Quit)) |
| .RetiresOnSaturation(); |
| fake_speech_recognition_manager_->SendFakeResponse( |
| false /* end recognition */, base::DoNothing()); |
| first_response_loop.Run(); |
| |
| // Try another speech response. |
| fake_speech_recognition_manager_->SetFakeResult("Pictures of mars!", |
| /*is_final=*/true); |
| base::RunLoop second_response_loop; |
| EXPECT_CALL( |
| *mock_speech_delegate_, |
| OnSpeechResult(std::u16string(u"Pictures of mars!"), true, testing::_)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*mock_speech_delegate_, |
| OnSpeechRecognitionStateChanged(SPEECH_RECOGNIZER_READY)) |
| .WillOnce(InvokeWithoutArgs(&second_response_loop, &base::RunLoop::Quit)); |
| fake_speech_recognition_manager_->SendFakeResponse(true /* end recognition */, |
| base::DoNothing()); |
| second_response_loop.Run(); |
| |
| // Stop listening, no more callbacks expected. |
| recognizer.Stop(); |
| } |