[BabelOrca] FakeSpeechRecognitionService supports multiple recognizers
Re-factors the Fake SpeechRecognitionService to support multiple
recognizers or AudioSourceFetchers. This is done by re-factoring the
Service such that tests will assert against specific recognizers
instead of the service as a whole.
Justification for this re-factor comes from three sources:
1.) Since this test dependency is used in browser tests it should
behave as much like the real speech recognition service as possible.
This includes the fact that the real service can handle multiple
recognizers concurrently.
2.) In the next CL we will implement the majority of
go/babel-orca-mic-integration which modifies the
SystemLiveCaptionService. In the new paradigm we want to be able
to test in an environment in which both versions of the
SystemLiveCaptionService are instantiated at the same time. see
the design for more information.
3.) In the future we plan on explicitly supporting mutliple
concurrent recognizers for LiveCaption. In order to test behavior
there we have to ensure that tests can also support mutliple
concurrent recognizers.
DanglingUntriaged-notes: Adding new dangling pointers in tests only
AX-Relnotes: N/A
Design: go/babel-orca-mic-integration
Bug: N/A
Change-Id: I33353ead7a1dca48b630d9fd0e4386035ca63c2b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5664296
Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org>
Reviewed-by: Evan Liu <evliu@google.com>
Reviewed-by: Ahmed Nasr <anasr@google.com>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: Katie Dektar <katie@chromium.org>
Commit-Queue: Avynn Donaghe <avynn@google.com>
Cr-Commit-Position: refs/heads/main@{#1346771}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 36d5b3f..914c1c9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -9077,6 +9077,8 @@
"sessions/tab_restore_service_load_waiter.h",
"speech/fake_speech_recognition_service.cc",
"speech/fake_speech_recognition_service.h",
+ "speech/fake_speech_recognizer.cc",
+ "speech/fake_speech_recognizer.h",
"ui/tabs/tab_activity_simulator.cc",
"ui/tabs/tab_activity_simulator.h",
]
diff --git a/chrome/browser/ash/accessibility/dictation_test_utils.cc b/chrome/browser/ash/accessibility/dictation_test_utils.cc
index 4c6a2f2..bd708eb 100644
--- a/chrome/browser/ash/accessibility/dictation_test_utils.cc
+++ b/chrome/browser/ash/accessibility/dictation_test_utils.cc
@@ -123,8 +123,8 @@
editable_type_(editable_type) {
automation_test_utils_ = std::make_unique<AutomationTestUtils>(
extension_misc::kAccessibilityCommonExtensionId);
- test_helper_ =
- std::make_unique<SpeechRecognitionTestHelper>(speech_recognition_type);
+ test_helper_ = std::make_unique<SpeechRecognitionTestHelper>(
+ speech_recognition_type, media::mojom::RecognizerClientType::kDictation);
}
DictationTestUtils::~DictationTestUtils() {
diff --git a/chrome/browser/ash/accessibility/live_caption/system_live_caption_service.cc b/chrome/browser/ash/accessibility/live_caption/system_live_caption_service.cc
index 7d9dc6aa..0dc284b 100644
--- a/chrome/browser/ash/accessibility/live_caption/system_live_caption_service.cc
+++ b/chrome/browser/ash/accessibility/live_caption/system_live_caption_service.cc
@@ -287,7 +287,7 @@
}
void SystemLiveCaptionService::CreateClient() {
- // We must reset first to detach everything first, and then reattach.
+ // We must reset to detach everything first, and then reattach.
client_.reset();
client_ = std::make_unique<SpeechRecognitionRecognizerClientImpl>(
weak_ptr_factory_.GetWeakPtr(), profile_,
diff --git a/chrome/browser/ash/accessibility/live_caption/system_live_caption_service_browsertest.cc b/chrome/browser/ash/accessibility/live_caption/system_live_caption_service_browsertest.cc
index a9999a0ea..4befb3f 100644
--- a/chrome/browser/ash/accessibility/live_caption/system_live_caption_service_browsertest.cc
+++ b/chrome/browser/ash/accessibility/live_caption/system_live_caption_service_browsertest.cc
@@ -9,6 +9,7 @@
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "chrome/browser/accessibility/live_caption/live_caption_controller_factory.h"
#include "chrome/browser/ash/accessibility/live_caption/system_live_caption_service_factory.h"
@@ -19,6 +20,7 @@
#include "chrome/browser/profiles/profile_test_util.h"
#include "chrome/browser/speech/cros_speech_recognition_service_factory.h"
#include "chrome/browser/speech/fake_speech_recognition_service.h"
+#include "chrome/browser/speech/fake_speech_recognizer.h"
#include "chrome/browser/speech/speech_recognizer_delegate.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
@@ -95,7 +97,9 @@
// Runs the system live caption service backed by a fake audio system and SODA
// installation.
-class SystemLiveCaptionServiceTest : public InProcessBrowserTest {
+class SystemLiveCaptionServiceTest
+ : public InProcessBrowserTest,
+ public speech::FakeSpeechRecognitionService::Observer {
public:
SystemLiveCaptionServiceTest() {
scoped_feature_list_.InitWithFeatures(
@@ -136,6 +140,7 @@
return std::make_unique<
speech::FakeSpeechRecognitionService>();
}));
+ fake_speech_recognition_service_->AddObserver(this);
// Pass in an inert audio system backend.
SystemLiveCaptionServiceFactory::GetInstance()
@@ -172,7 +177,8 @@
// Emit the given text from our fake speech recognition service.
void EmulateRecognizedSpeech(const std::string& text) {
- fake_speech_recognition_service_->SendSpeechRecognitionResult(
+ ASSERT_TRUE(current_audio_fetcher_);
+ current_audio_fetcher_->SendSpeechRecognitionResult(
media::SpeechRecognitionResult(text, /*is_final=*/false));
base::RunLoop().RunUntilIdle();
}
@@ -193,9 +199,29 @@
base::RunLoop().RunUntilIdle();
}
+ // FakeSpeechRecognitionService::Observer
+ void OnRecognizerBound(
+ speech::FakeSpeechRecognizer* bound_recognizer) override {
+ if (bound_recognizer->recognition_options()->recognizer_client_type ==
+ media::mojom::RecognizerClientType::kLiveCaption) {
+ current_audio_fetcher_ = bound_recognizer->GetWeakPtr();
+ }
+ }
+
// Unowned.
raw_ptr<Profile, DanglingUntriaged> primary_profile_;
raw_ptr<Profile, DanglingUntriaged> secondary_profile_;
+
+ // current_audio_fetcher_ is a speech recognizer fake that is used to assert
+ // correct behavior when a session is started by the SystemLiveCaptionService.
+ // When a session is started `OnRecognizerBound` is invoked which will
+ // populate the current_audio_fetcher_ with the correct audio fetcher. If
+ // this pointer is null then that means that the SystemLiveCapitonService
+ // has yet to start a session, so if we want to assert that a session
+ // hasn't been started yet thus far in a test we can expect this
+ // pointer to be null.
+ base::WeakPtr<speech::FakeSpeechRecognizer> current_audio_fetcher_ = nullptr;
+
raw_ptr<speech::FakeSpeechRecognitionService, DanglingUntriaged>
fake_speech_recognition_service_;
@@ -207,13 +233,13 @@
IN_PROC_BROWSER_TEST_F(SystemLiveCaptionServiceTest, Triggering) {
// We should be waiting for the feature to be enabled and for SODA to be
// installed.
- EXPECT_FALSE(fake_speech_recognition_service_->is_capturing_audio());
+ EXPECT_FALSE(current_audio_fetcher_);
// Enable feature.
SetLiveCaptionsPref(/*enabled=*/true);
// We should still be waiting for SODA to be installed.
- EXPECT_FALSE(fake_speech_recognition_service_->is_capturing_audio());
+ EXPECT_FALSE(current_audio_fetcher_);
// Fake successful language pack install.
speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(
@@ -221,15 +247,16 @@
base::RunLoop().RunUntilIdle();
// We should be waiting for the base binary too.
- EXPECT_FALSE(fake_speech_recognition_service_->is_capturing_audio());
+ EXPECT_FALSE(current_audio_fetcher_);
// Fake successful base binary install.
speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting();
base::RunLoop().RunUntilIdle();
// After language and binary install, still should be false until output is
- // triggered.
- EXPECT_FALSE(fake_speech_recognition_service_->is_capturing_audio());
+ // triggered. The client should be created at this point though.
+ ASSERT_TRUE(current_audio_fetcher_);
+ EXPECT_FALSE(current_audio_fetcher_->is_capturing_audio());
// Start audio.
// Set audio output running.
@@ -239,13 +266,13 @@
base::RunLoop().RunUntilIdle();
// Should now be processing system audio.
- EXPECT_TRUE(fake_speech_recognition_service_->is_capturing_audio());
+ EXPECT_TRUE(current_audio_fetcher_->is_capturing_audio());
// Now turn off live captioning.
SetLiveCaptionsPref(/*enabled=*/false);
// This should stop audio fetching.
- EXPECT_FALSE(fake_speech_recognition_service_->is_capturing_audio());
+ EXPECT_FALSE(current_audio_fetcher_);
}
// Test that feature is gated on successful SODA install.
@@ -260,7 +287,7 @@
base::RunLoop().RunUntilIdle();
// Our language is not yet installed, so we shouldn't be processing audio.
- EXPECT_FALSE(fake_speech_recognition_service_->is_capturing_audio());
+ EXPECT_FALSE(current_audio_fetcher_);
}
// Tests that our feature listens to the correct SODA language.
@@ -283,7 +310,8 @@
base::RunLoop().RunUntilIdle();
// Our language is not yet installed, so we shouldn't be processing audio.
- EXPECT_FALSE(fake_speech_recognition_service_->is_capturing_audio());
+ // Therefore the current_audio_fetcher_ should be null.
+ EXPECT_FALSE(current_audio_fetcher_);
// Fake successful install of our language.
speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(
@@ -294,7 +322,8 @@
live_caption_service->OnNonChromeOutputStarted();
base::RunLoop().RunUntilIdle();
// We should have ignored the unrelated error.
- EXPECT_TRUE(fake_speech_recognition_service_->is_capturing_audio());
+ ASSERT_TRUE(current_audio_fetcher_);
+ EXPECT_TRUE(current_audio_fetcher_->is_capturing_audio());
}
// Test that captions are only dispatched for the primary profile.
@@ -303,7 +332,8 @@
// Capture fake audio.
EmulateRecognizedSpeech("System audio caption");
- EXPECT_TRUE(fake_speech_recognition_service_->is_capturing_audio());
+ ASSERT_TRUE(current_audio_fetcher_);
+ EXPECT_TRUE(current_audio_fetcher_->is_capturing_audio());
// Transcribed speech should be displayed from the primary profile.
auto* primary_bubble = GetCaptionBubbleController(primary_profile_);
@@ -322,7 +352,8 @@
// Capture fake audio.
EmulateRecognizedSpeech("System audio caption");
- EXPECT_TRUE(fake_speech_recognition_service_->is_capturing_audio());
+ ASSERT_TRUE(current_audio_fetcher_);
+ EXPECT_TRUE(current_audio_fetcher_->is_capturing_audio());
// Transcribed speech should be displayed from the primary profile.
// The added captions are all added as non-finals, so they over-write not
@@ -374,12 +405,14 @@
EmulateRecognizedSpeech("More system audio captions");
// The speech recognition service should have received the early stop request.
- EXPECT_FALSE(fake_speech_recognition_service_->is_capturing_audio());
+ // The client will be deleted.
+ ASSERT_FALSE(current_audio_fetcher_);
}
// Test that the UI is closed when transcription is complete.
IN_PROC_BROWSER_TEST_F(SystemLiveCaptionServiceTest, EndOfStream) {
StartLiveCaptioning();
+ ASSERT_TRUE(current_audio_fetcher_);
// Fake some speech.
EmulateRecognizedSpeech("System audio caption");
@@ -390,7 +423,7 @@
EXPECT_TRUE(primary_bubble->IsWidgetVisibleForTesting());
// Emulate end of audio stream.
- fake_speech_recognition_service_->MarkDone();
+ current_audio_fetcher_->MarkDone();
base::RunLoop().RunUntilIdle();
// Bubble should not be shown since there is no more audio.
@@ -402,6 +435,7 @@
// Test that an error message is shown if something goes wrong.
IN_PROC_BROWSER_TEST_F(SystemLiveCaptionServiceTest, ServiceError) {
StartLiveCaptioning();
+ ASSERT_TRUE(current_audio_fetcher_);
// Fake some speech.
EmulateRecognizedSpeech("System audio caption");
@@ -413,7 +447,7 @@
EXPECT_FALSE(primary_bubble->IsGenericErrorMessageVisibleForTesting());
// Emulate recognition error.
- fake_speech_recognition_service_->SendSpeechRecognitionError();
+ current_audio_fetcher_->SendSpeechRecognitionError();
base::RunLoop().RunUntilIdle();
// Bubble should still be shown and should display error text.
@@ -428,14 +462,15 @@
IN_PROC_BROWSER_TEST_F(SystemLiveCaptionServiceTest, UsesCorrectLanguage) {
SetLanguagePref(kAlternativeLanguageName);
StartLiveCaptioning();
+ ASSERT_TRUE(current_audio_fetcher_);
// retrieve the recognition options struct passed to the recognition service.
// we use this to assert that the correct language was passed to the service.
- media::mojom::SpeechRecognitionOptions* recognition_options =
- fake_speech_recognition_service_->get_speech_recognition_options();
+ const media::mojom::SpeechRecognitionOptions* recognition_options =
+ current_audio_fetcher_->recognition_options();
// Should now be processing system audio.
- EXPECT_TRUE(fake_speech_recognition_service_->is_capturing_audio());
+ EXPECT_TRUE(current_audio_fetcher_->is_capturing_audio());
// Assert language is correct.
ASSERT_NE(recognition_options, nullptr);
@@ -450,14 +485,15 @@
IN_PROC_BROWSER_TEST_F(SystemLiveCaptionServiceTest,
SwitchesLanguageCorrectly) {
StartLiveCaptioning();
+ ASSERT_TRUE(current_audio_fetcher_);
// retrieve the recognition options struct passed to the recognition service.
// we use this to assert that the correct language was passed to the service.
- media::mojom::SpeechRecognitionOptions* recognition_options =
- fake_speech_recognition_service_->get_speech_recognition_options();
+ const media::mojom::SpeechRecognitionOptions* recognition_options =
+ current_audio_fetcher_->recognition_options();
// Should now be processing system audio.
- EXPECT_TRUE(fake_speech_recognition_service_->is_capturing_audio());
+ EXPECT_TRUE(current_audio_fetcher_->is_capturing_audio());
// Assert language is correct.
ASSERT_NE(recognition_options, nullptr);
@@ -477,20 +513,21 @@
->set_num_non_chrome_output_streams_for_testing(
/*num_output_streams=*/1);
- // Until SODA installs we should do nothing.
- EXPECT_FALSE(fake_speech_recognition_service_->is_capturing_audio());
+ // Until SODA installs we should do nothing. The Client will be created at
+ // this point so we can assert that the current audio fetcher is not capturing
+ // audio.
+ EXPECT_FALSE(current_audio_fetcher_);
// Emulate successful SODA installation from LiveCaptionController.
speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(
speech::GetLanguageCode(kAlternativeLanguageName));
base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(fake_speech_recognition_service_->is_capturing_audio());
+ EXPECT_TRUE(current_audio_fetcher_->is_capturing_audio());
// We destroy the old options struct when resetting the speech recogntion
// client.
- recognition_options =
- fake_speech_recognition_service_->get_speech_recognition_options();
+ recognition_options = current_audio_fetcher_->recognition_options();
ASSERT_NE(recognition_options, nullptr);
ASSERT_TRUE(recognition_options->language.has_value());
diff --git a/chrome/browser/ash/accessibility/service/accessibility_service_client_browsertest.cc b/chrome/browser/ash/accessibility/service/accessibility_service_client_browsertest.cc
index 7c134d0..2f2a7c9 100644
--- a/chrome/browser/ash/accessibility/service/accessibility_service_client_browsertest.cc
+++ b/chrome/browser/ash/accessibility/service/accessibility_service_client_browsertest.cc
@@ -339,7 +339,8 @@
&AccessibilityServiceClientTest::CreateTestAccessibilityService,
base::Unretained(this)));
sr_test_helper_ = std::make_unique<SpeechRecognitionTestHelper>(
- speech::SpeechRecognitionType::kNetwork);
+ speech::SpeechRecognitionType::kNetwork,
+ media::mojom::RecognizerClientType::kDictation);
sr_test_helper_->SetUp(browser()->profile());
}
diff --git a/chrome/browser/ash/accessibility/service/speech_recognition_impl_browsertest.cc b/chrome/browser/ash/accessibility/service/speech_recognition_impl_browsertest.cc
index 97163f6..2a60a98 100644
--- a/chrome/browser/ash/accessibility/service/speech_recognition_impl_browsertest.cc
+++ b/chrome/browser/ash/accessibility/service/speech_recognition_impl_browsertest.cc
@@ -12,6 +12,7 @@
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"
+#include "media/mojo/mojom/speech_recognizer.mojom.h"
#include "services/accessibility/public/mojom/assistive_technology_type.mojom.h"
#include "services/accessibility/public/mojom/speech_recognition.mojom.h"
@@ -69,7 +70,8 @@
protected:
void SetUpOnMainThread() override {
sr_test_helper_ = std::make_unique<SpeechRecognitionTestHelper>(
- speech::SpeechRecognitionType::kNetwork);
+ speech::SpeechRecognitionType::kNetwork,
+ media::mojom::RecognizerClientType::kDictation);
sr_test_helper_->SetUp(browser()->profile());
sr_impl_ = std::make_unique<SpeechRecognitionImpl>(browser()->profile());
}
diff --git a/chrome/browser/ash/extensions/speech/speech_recognition_private_base_test.cc b/chrome/browser/ash/extensions/speech/speech_recognition_private_base_test.cc
index b3ada250..79c78a4 100644
--- a/chrome/browser/ash/extensions/speech/speech_recognition_private_base_test.cc
+++ b/chrome/browser/ash/extensions/speech/speech_recognition_private_base_test.cc
@@ -14,7 +14,8 @@
namespace extensions {
SpeechRecognitionPrivateBaseTest::SpeechRecognitionPrivateBaseTest()
- : test_helper_(GetParam()) {}
+ : test_helper_(GetParam(), media::mojom::RecognizerClientType::kDictation) {
+}
SpeechRecognitionPrivateBaseTest::~SpeechRecognitionPrivateBaseTest() = default;
diff --git a/chrome/browser/speech/fake_speech_recognition_service.cc b/chrome/browser/speech/fake_speech_recognition_service.cc
index b8e7138..d03e69b 100644
--- a/chrome/browser/speech/fake_speech_recognition_service.cc
+++ b/chrome/browser/speech/fake_speech_recognition_service.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/speech/fake_speech_recognition_service.h"
+#include <memory>
#include <utility>
#include "base/notimplemented.h"
@@ -12,6 +13,7 @@
#include "media/mojo/mojom/media_types.mojom.h"
#include "media/mojo/mojom/speech_recognition.mojom.h"
#include "media/mojo/mojom/speech_recognition_service.mojom.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace speech {
@@ -31,17 +33,34 @@
speech_recognition_contexts_.Add(this, std::move(receiver));
}
+std::unique_ptr<FakeSpeechRecognizer>
+FakeSpeechRecognitionService::GetNextRecognizerAndBindItsRemote(
+ mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient> client,
+ media::mojom::SpeechRecognitionOptionsPtr options) {
+ // If the test has injected a fake recognizer for assertions then use it here
+ // otherwise create a new one which can be ignored.
+ std::unique_ptr<FakeSpeechRecognizer> next_fake_recognizer =
+ std::make_unique<FakeSpeechRecognizer>();
+ next_fake_recognizer->BindRecognizerClientRemoteAndPassRecognitionOptions(
+ std::move(client), std::move(options));
+
+ for (Observer& obs : observers_) {
+ obs.OnRecognizerBound(next_fake_recognizer.get());
+ }
+
+ return next_fake_recognizer;
+}
+
void FakeSpeechRecognitionService::BindRecognizer(
mojo::PendingReceiver<media::mojom::SpeechRecognitionRecognizer> receiver,
mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient> client,
media::mojom::SpeechRecognitionOptionsPtr options,
BindRecognizerCallback callback) {
- recognizer_receiver_.Bind(std::move(receiver));
- recognizer_client_remote_.Bind(std::move(client));
- recognizer_client_remote_.set_disconnect_handler(base::BindOnce(
- &FakeSpeechRecognitionService::OnRecognizerClientDisconnected,
- base::Unretained(this)));
- recognition_options_ = std::move(options);
+ // Bind the remote for the fake recognizer, then make it a self owned
+ // receiver.
+ mojo::MakeSelfOwnedReceiver(
+ GetNextRecognizerAndBindItsRemote(std::move(client), std::move(options)),
+ std::move(receiver));
std::move(callback).Run(is_multichannel_supported_);
}
@@ -64,76 +83,11 @@
mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient> client,
media::mojom::SpeechRecognitionOptionsPtr options,
BindRecognizerCallback callback) {
- fetcher_receiver_.Bind(std::move(fetcher_receiver));
- recognizer_client_remote_.Bind(std::move(client));
- recognizer_client_remote_.set_disconnect_handler(base::BindOnce(
- &FakeSpeechRecognitionService::OnRecognizerClientDisconnected,
- base::Unretained(this)));
- recognition_options_ = std::move(options);
+ // See above, BindRecognizer.
+ mojo::MakeSelfOwnedReceiver(
+ GetNextRecognizerAndBindItsRemote(std::move(client), std::move(options)),
+ std::move(fetcher_receiver));
std::move(callback).Run(is_multichannel_supported_);
}
-void FakeSpeechRecognitionService::Start(
- mojo::PendingRemote<media::mojom::AudioStreamFactory> stream_factory,
- const std::string& device_id,
- const ::media::AudioParameters& audio_parameters) {
- capturing_audio_ = true;
- device_id_ = device_id;
- audio_parameters_ = audio_parameters;
- if (recognition_started_closure_) {
- std::move(recognition_started_closure_).Run();
- }
-}
-void FakeSpeechRecognitionService::Stop() {
- capturing_audio_ = false;
- device_id_ = "";
- audio_parameters_ = std::nullopt;
- MarkDone();
-}
-
-void FakeSpeechRecognitionService::SendAudioToSpeechRecognitionService(
- media::mojom::AudioDataS16Ptr buffer) {
- has_received_audio_ = true;
-}
-
-void FakeSpeechRecognitionService::MarkDone() {
- recognizer_client_remote_->OnSpeechRecognitionStopped();
-}
-
-void FakeSpeechRecognitionService::SendSpeechRecognitionResult(
- const media::SpeechRecognitionResult& result) {
- ASSERT_TRUE(recognizer_client_remote_.is_bound());
- EXPECT_TRUE(capturing_audio_ || has_received_audio_);
- recognizer_client_remote_->OnSpeechRecognitionRecognitionEvent(
- result, base::BindOnce(&FakeSpeechRecognitionService::
- OnSpeechRecognitionRecognitionEventCallback,
- base::Unretained(this)));
-}
-
-void FakeSpeechRecognitionService::OnSpeechRecognitionRecognitionEventCallback(
- bool success) {
- capturing_audio_ = success;
-}
-
-void FakeSpeechRecognitionService::SendSpeechRecognitionError() {
- ASSERT_TRUE(recognizer_client_remote_.is_bound());
- recognizer_client_remote_->OnSpeechRecognitionError();
-}
-
-void FakeSpeechRecognitionService::WaitForRecognitionStarted() {
- base::RunLoop runner;
- recognition_started_closure_ = runner.QuitClosure();
- runner.Run();
-}
-
-void FakeSpeechRecognitionService::OnRecognizerClientDisconnected() {
- // Reset everything in case it will be re-used.
- recognizer_client_remote_.reset();
- fetcher_receiver_.reset();
- recognizer_receiver_.reset();
- capturing_audio_ = false;
- device_id_ = "";
- audio_parameters_ = std::nullopt;
-}
-
} // namespace speech
diff --git a/chrome/browser/speech/fake_speech_recognition_service.h b/chrome/browser/speech/fake_speech_recognition_service.h
index 5cf34fc9..9b83ea8 100644
--- a/chrome/browser/speech/fake_speech_recognition_service.h
+++ b/chrome/browser/speech/fake_speech_recognition_service.h
@@ -5,9 +5,13 @@
#ifndef CHROME_BROWSER_SPEECH_FAKE_SPEECH_RECOGNITION_SERVICE_H_
#define CHROME_BROWSER_SPEECH_FAKE_SPEECH_RECOGNITION_SERVICE_H_
+#include <memory>
#include <string>
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
#include "chrome/browser/speech/chrome_speech_recognition_service.h"
+#include "chrome/browser/speech/fake_speech_recognizer.h"
#include "media/base/audio_parameters.h"
#include "media/mojo/mojom/audio_data.mojom.h"
#include "media/mojo/mojom/speech_recognition.mojom.h"
@@ -18,17 +22,23 @@
namespace speech {
-// A fake SpeechRecognitionService. This can only be used by one client at a
-// time. This class also acts as the AudioSourceFetcher receiver and
-// SpeechRecognitionRecognizer receiver to allow tests to inspect whether
-// methods have been called properly.
+// A fake SpeechRecognitionService. This service works by creating
+// FakeSpeechRecognizers as self owned receivers. If a test wants to assert
+// against the state of a particular session then the fixture will need to
+// implement the observer interface and add itself. When the recognizer is
+// bound the fixture can then grab a reference to the recognizer and assert
+// against it. Fixtures may also want to confirm that they have the correct
+// recognizer by checking the recognition type in the options struct.
class FakeSpeechRecognitionService
: public SpeechRecognitionService,
public media::mojom::SpeechRecognitionContext,
- public media::mojom::SpeechRecognitionRecognizer,
- public media::mojom::AudioSourceSpeechRecognitionContext,
- public media::mojom::AudioSourceFetcher {
+ public media::mojom::AudioSourceSpeechRecognitionContext {
public:
+ class Observer : public base::CheckedObserver {
+ public:
+ virtual void OnRecognizerBound(FakeSpeechRecognizer* bound_recognizer) = 0;
+ };
+
FakeSpeechRecognitionService();
FakeSpeechRecognitionService(const FakeSpeechRecognitionService&) = delete;
FakeSpeechRecognitionService& operator=(const SpeechRecognitionService&) =
@@ -70,77 +80,32 @@
media::mojom::SpeechRecognitionOptionsPtr options,
BindRecognizerCallback callback) override;
- // media::mojom::AudioSourceFetcher:
- void Start(
- mojo::PendingRemote<media::mojom::AudioStreamFactory> stream_factory,
- const std::string& device_id,
- const ::media::AudioParameters& audio_parameters) override;
- void Stop() override;
-
- // media::mojom::SpeechRecognitionRecognizer:
- void SendAudioToSpeechRecognitionService(
- media::mojom::AudioDataS16Ptr buffer) override;
- void OnLanguageChanged(const std::string& language) override {}
- void OnMaskOffensiveWordsChanged(bool mask_offensive_words) override {}
- void MarkDone() override;
-
- // Methods for testing plumbing to SpeechRecognitionRecognizerClient.
- void SendSpeechRecognitionResult(
- const media::SpeechRecognitionResult& result);
- void SendSpeechRecognitionError();
-
- void WaitForRecognitionStarted();
-
- // Whether AudioSourceFetcher is capturing audio.
- bool is_capturing_audio() { return capturing_audio_; }
-
- // Whether SendAudioToSpeechRecognitionService has been called.
- bool has_received_audio() { return has_received_audio_; }
-
- std::string device_id() { return device_id_; }
-
- media::mojom::SpeechRecognitionOptions* get_speech_recognition_options() {
- return recognition_options_.get();
- }
-
- const std::optional<::media::AudioParameters>& audio_parameters() {
- return audio_parameters_;
- }
-
void set_multichannel_supported(bool is_multichannel_supported) {
is_multichannel_supported_ = is_multichannel_supported;
}
+ void AddObserver(Observer* obs) { observers_.AddObserver(obs); }
+
+ void RemoveObsever(Observer* obs) { observers_.RemoveObserver(obs); }
+
private:
void OnRecognizerClientDisconnected();
-
void OnSpeechRecognitionRecognitionEventCallback(bool success);
+ std::unique_ptr<FakeSpeechRecognizer> GetNextRecognizerAndBindItsRemote(
+ mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient>
+ client,
+ media::mojom::SpeechRecognitionOptionsPtr options);
+
// Whether multichannel audio is supported.
bool is_multichannel_supported_ = false;
- // Whether the AudioSourceFetcher has been started.
- bool capturing_audio_ = false;
- // Whether any audio has been sent to the SpeechRecognitionRecognizer.
- bool has_received_audio_ = false;
- // The device ID used to capture audio.
- std::string device_id_;
- // The audio parameters used to capture audio.
- std::optional<::media::AudioParameters> audio_parameters_;
-
- media::mojom::SpeechRecognitionOptionsPtr recognition_options_;
-
- base::OnceClosure recognition_started_closure_;
-
- mojo::Remote<media::mojom::SpeechRecognitionRecognizerClient>
- recognizer_client_remote_;
mojo::ReceiverSet<media::mojom::AudioSourceSpeechRecognitionContext>
audio_source_speech_recognition_contexts_;
mojo::ReceiverSet<media::mojom::SpeechRecognitionContext>
speech_recognition_contexts_;
- mojo::Receiver<media::mojom::SpeechRecognitionRecognizer>
- recognizer_receiver_{this};
- mojo::Receiver<media::mojom::AudioSourceFetcher> fetcher_receiver_{this};
+
+ base::ObserverList<Observer> observers_;
};
} // namespace speech
diff --git a/chrome/browser/speech/fake_speech_recognizer.cc b/chrome/browser/speech/fake_speech_recognizer.cc
new file mode 100644
index 0000000..297050a
--- /dev/null
+++ b/chrome/browser/speech/fake_speech_recognizer.cc
@@ -0,0 +1,92 @@
+// Copyright 2024 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/fake_speech_recognizer.h"
+
+#include "base/run_loop.h"
+#include "media/mojo/mojom/audio_data.mojom.h"
+#include "media/mojo/mojom/audio_logging.mojom.h"
+#include "media/mojo/mojom/audio_stream_factory.mojom.h"
+#include "media/mojo/mojom/media_types.mojom.h"
+#include "media/mojo/mojom/speech_recognition.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace speech {
+
+FakeSpeechRecognizer::FakeSpeechRecognizer() = default;
+FakeSpeechRecognizer::~FakeSpeechRecognizer() = default;
+
+void FakeSpeechRecognizer::BindRecognizerClientRemoteAndPassRecognitionOptions(
+ mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient> client,
+ media::mojom::SpeechRecognitionOptionsPtr recognition_options) {
+ BindSpeechRecognizerClientRemote(std::move(client));
+ recognition_options_ = std::move(recognition_options);
+}
+
+void FakeSpeechRecognizer::BindSpeechRecognizerClientRemote(
+ mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient>
+ client) {
+ recognizer_client_remote_.Bind(std::move(client));
+}
+
+void FakeSpeechRecognizer::Start(
+ mojo::PendingRemote<media::mojom::AudioStreamFactory> stream_factory,
+ const std::string& device_id,
+ const ::media::AudioParameters& audio_parameters) {
+ capturing_audio_ = true;
+ device_id_ = device_id;
+ audio_parameters_ = audio_parameters;
+ if (recognition_started_closure_) {
+ std::move(recognition_started_closure_).Run();
+ }
+}
+
+void FakeSpeechRecognizer::Stop() {
+ capturing_audio_ = false;
+ device_id_ = "";
+ audio_parameters_ = std::nullopt;
+ MarkDone();
+}
+
+void FakeSpeechRecognizer::MarkDone() {
+ ASSERT_TRUE(recognizer_client_remote_.is_bound());
+ recognizer_client_remote_->OnSpeechRecognitionStopped();
+}
+
+void FakeSpeechRecognizer::SendSpeechRecognitionResult(
+ const media::SpeechRecognitionResult& result) {
+ ASSERT_TRUE(recognizer_client_remote_.is_bound());
+ EXPECT_TRUE(capturing_audio_ || has_received_audio_);
+ recognizer_client_remote_->OnSpeechRecognitionRecognitionEvent(
+ result,
+ base::BindOnce(&FakeSpeechRecognizer::OnSpeechRecognitionEventCallback,
+ base::Unretained(this)));
+}
+
+void FakeSpeechRecognizer::SendAudioToSpeechRecognitionService(
+ media::mojom::AudioDataS16Ptr buffer) {
+ has_received_audio_ = true;
+}
+
+void FakeSpeechRecognizer::SendSpeechRecognitionError() {
+ ASSERT_TRUE(recognizer_client_remote_.is_bound());
+ recognizer_client_remote_->OnSpeechRecognitionError();
+}
+
+void FakeSpeechRecognizer::OnSpeechRecognitionEventCallback(bool success) {
+ capturing_audio_ = success;
+}
+
+void FakeSpeechRecognizer::WaitForRecognitionStarted() {
+ // We're already capturing audio so recognition has already started!
+ if (capturing_audio_) {
+ return;
+ }
+
+ base::RunLoop runner;
+ recognition_started_closure_ = runner.QuitClosure();
+ runner.Run();
+}
+
+} // namespace speech
diff --git a/chrome/browser/speech/fake_speech_recognizer.h b/chrome/browser/speech/fake_speech_recognizer.h
new file mode 100644
index 0000000..6e74235
--- /dev/null
+++ b/chrome/browser/speech/fake_speech_recognizer.h
@@ -0,0 +1,104 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SPEECH_FAKE_SPEECH_RECOGNIZER_H_
+#define CHROME_BROWSER_SPEECH_FAKE_SPEECH_RECOGNIZER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "media/base/audio_parameters.h"
+#include "media/mojo/mojom/audio_data.mojom.h"
+#include "media/mojo/mojom/speech_recognition.mojom.h"
+#include "media/mojo/mojom/speech_recognition_service.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace speech {
+
+// A fake SpeechRecognizer which will be a self owned receiver. Allows for
+// asserting the state of a speech recognition session.
+class FakeSpeechRecognizer : public media::mojom::AudioSourceFetcher,
+ public media::mojom::SpeechRecognitionRecognizer {
+ public:
+ FakeSpeechRecognizer();
+ FakeSpeechRecognizer(const FakeSpeechRecognizer&) = delete;
+ FakeSpeechRecognizer& operator=(const FakeSpeechRecognizer&) = delete;
+ ~FakeSpeechRecognizer() override;
+
+ // Binds the RecognizerClient remote to this recognizer.
+ void BindRecognizerClientRemoteAndPassRecognitionOptions(
+ mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient>
+ client,
+ media::mojom::SpeechRecognitionOptionsPtr recognition_options);
+
+ // Methods for testing plumbing to SpeechRecognitionRecognizerClient.
+ void SendSpeechRecognitionResult(
+ const media::SpeechRecognitionResult& result);
+ void SendSpeechRecognitionError();
+
+ // media::mojom::SpeechRecognitionRecognizer:
+ void SendAudioToSpeechRecognitionService(
+ media::mojom::AudioDataS16Ptr buffer) override;
+ void OnLanguageChanged(const std::string& language) override {}
+ void OnMaskOffensiveWordsChanged(bool mask_offensive_words) override {}
+ void MarkDone() override;
+
+ // media::mojom::AudioSourceFetcher:
+ void Start(
+ mojo::PendingRemote<media::mojom::AudioStreamFactory> stream_factory,
+ const std::string& device_id,
+ const ::media::AudioParameters& audio_parameters) override;
+ void Stop() override;
+
+ // Halts test execution until after start has been called.
+ void WaitForRecognitionStarted();
+
+ // Whether AudioSourceFetcher is capturing audio.
+ bool is_capturing_audio() { return capturing_audio_; }
+
+ // Device ID used by the AudioSource Fetcher.
+ std::string device_id() { return device_id_; }
+
+ // Speech recognition options passed to this Recognizer.
+ const media::mojom::SpeechRecognitionOptions* recognition_options() const {
+ return recognition_options_.get();
+ }
+
+ // Audio parameters passed to this recognizer.
+ const std::optional<::media::AudioParameters>& audio_parameters() const {
+ return audio_parameters_;
+ }
+
+ base::WeakPtr<FakeSpeechRecognizer> GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ private:
+ // Bind a client to this audio fetcher.
+ void BindSpeechRecognizerClientRemote(
+ mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient>
+ client);
+
+ void OnSpeechRecognitionEventCallback(bool success);
+
+ base::OnceClosure recognition_started_closure_;
+
+ // Speech recognition options passed to this recognizer.
+ media::mojom::SpeechRecognitionOptionsPtr recognition_options_;
+ // Audio parameters associated with this recognizer.
+ std::optional<::media::AudioParameters> audio_parameters_;
+ // Whether the recognizer has been started.
+ bool capturing_audio_ = false;
+ // Whether or not the recognizer has received audio.
+ bool has_received_audio_ = false;
+ // The device ID used to capture audio.
+ std::string device_id_;
+ // Client tied to this fetcher.
+ mojo::Remote<media::mojom::SpeechRecognitionRecognizerClient>
+ recognizer_client_remote_;
+
+ base::WeakPtrFactory<FakeSpeechRecognizer> weak_ptr_factory_{this};
+};
+
+} // namespace speech
+
+#endif // CHROME_BROWSER_SPEECH_FAKE_SPEECH_RECOGNIZER_H_
diff --git a/chrome/browser/speech/speech_recognition_recognizer_client_impl_browsertest.cc b/chrome/browser/speech/speech_recognition_recognizer_client_impl_browsertest.cc
index c795b3a..205870f6 100644
--- a/chrome/browser/speech/speech_recognition_recognizer_client_impl_browsertest.cc
+++ b/chrome/browser/speech/speech_recognition_recognizer_client_impl_browsertest.cc
@@ -9,9 +9,11 @@
#include "ash/constants/ash_features.h"
#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/speech/cros_speech_recognition_service_factory.h"
#include "chrome/browser/speech/fake_speech_recognition_service.h"
+#include "chrome/browser/speech/fake_speech_recognizer.h"
#include "chrome/browser/speech/speech_recognizer_delegate.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
@@ -95,7 +97,9 @@
// Tests SpeechRecognitionRecognizerClientImpl plumbing with a fake
// SpeechRecognitionService. Does not do end-to-end audio fetching or test SODA
// on device.
-class SpeechRecognitionRecognizerClientImplTest : public InProcessBrowserTest {
+class SpeechRecognitionRecognizerClientImplTest
+ : public InProcessBrowserTest,
+ public speech::FakeSpeechRecognitionService::Observer {
public:
SpeechRecognitionRecognizerClientImplTest() = default;
~SpeechRecognitionRecognizerClientImplTest() override = default;
@@ -104,6 +108,15 @@
SpeechRecognitionRecognizerClientImplTest& operator=(
const SpeechRecognitionRecognizerClientImplTest&) = delete;
+ // FakeSpeechRecognitionService::Observer
+ void OnRecognizerBound(
+ speech::FakeSpeechRecognizer* bound_recognizer) override {
+ if (bound_recognizer->recognition_options()->recognizer_client_type ==
+ media::mojom::RecognizerClientType::kDictation) {
+ fake_speech_recognizer_ = bound_recognizer->GetWeakPtr();
+ }
+ }
+
void SetUpCommandLine(base::CommandLine* command_line) override {
scoped_feature_list_.InitAndEnableFeature(
ash::features::kOnDeviceSpeechRecognition);
@@ -135,6 +148,7 @@
std::unique_ptr<speech::FakeSpeechRecognitionService> fake_service =
std::make_unique<speech::FakeSpeechRecognitionService>();
fake_service_ = fake_service.get();
+ fake_service_->AddObserver(this);
return std::move(fake_service);
}
@@ -162,7 +176,7 @@
.Times(1)
.RetiresOnSaturation();
recognizer_->Start();
- fake_service_->WaitForRecognitionStarted();
+ fake_speech_recognizer_->WaitForRecognitionStarted();
base::RunLoop().RunUntilIdle();
}
@@ -183,6 +197,7 @@
// Unowned.
raw_ptr<speech::FakeSpeechRecognitionService, DanglingUntriaged>
fake_service_;
+ base::WeakPtr<speech::FakeSpeechRecognizer> fake_speech_recognizer_;
base::test::ScopedFeatureList scoped_feature_list_;
};
@@ -196,12 +211,12 @@
StartsCapturingAudio) {
testing::InSequence seq;
ConstructRecognizerAndWaitForReady();
- EXPECT_FALSE(fake_service_->is_capturing_audio());
+ EXPECT_FALSE(fake_speech_recognizer_->is_capturing_audio());
// Toggle a few times.
for (int i = 0; i < 2; i++) {
StartAndWaitForRecognizing();
- EXPECT_TRUE(fake_service_->is_capturing_audio());
+ EXPECT_TRUE(fake_speech_recognizer_->is_capturing_audio());
EXPECT_CALL(*mock_speech_delegate_,
OnSpeechRecognitionStateChanged(SPEECH_RECOGNITION_STOPPING))
@@ -219,7 +234,7 @@
recognizer_->Stop();
base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(fake_service_->is_capturing_audio());
+ EXPECT_FALSE(fake_speech_recognizer_->is_capturing_audio());
}
}
@@ -239,7 +254,7 @@
testing::_))
.Times(1)
.RetiresOnSaturation();
- fake_service_->SendSpeechRecognitionResult(
+ fake_speech_recognizer_->SendSpeechRecognitionResult(
media::SpeechRecognitionResult("All mammals have hair", false));
base::RunLoop().RunUntilIdle();
@@ -249,8 +264,9 @@
true, testing::_))
.Times(1)
.RetiresOnSaturation();
- fake_service_->SendSpeechRecognitionResult(media::SpeechRecognitionResult(
- "All mammals drink milk from their mothers", true));
+ fake_speech_recognizer_->SendSpeechRecognitionResult(
+ media::SpeechRecognitionResult(
+ "All mammals drink milk from their mothers", true));
base::RunLoop().RunUntilIdle();
}
@@ -263,7 +279,7 @@
OnSpeechRecognitionStateChanged(SPEECH_RECOGNIZER_ERROR))
.Times(1)
.RetiresOnSaturation();
- fake_service_->SendSpeechRecognitionError();
+ fake_speech_recognizer_->SendSpeechRecognitionError();
base::RunLoop().RunUntilIdle();
}
@@ -279,9 +295,9 @@
StartListeningWithAudioParams(params);
EXPECT_EQ(media::AudioDeviceDescription::kDefaultDeviceId,
- fake_service_->device_id());
- ASSERT_TRUE(fake_service_->audio_parameters());
- EXPECT_TRUE(fake_service_->audio_parameters()->Equals(params));
+ fake_speech_recognizer_->device_id());
+ ASSERT_TRUE(fake_speech_recognizer_->audio_parameters());
+ EXPECT_TRUE(fake_speech_recognizer_->audio_parameters()->Equals(params));
}
IN_PROC_BROWSER_TEST_F(SpeechRecognitionRecognizerClientImplTest,
@@ -296,14 +312,14 @@
StartListeningWithAudioParams(params);
EXPECT_EQ(media::AudioDeviceDescription::kDefaultDeviceId,
- fake_service_->device_id());
- ASSERT_TRUE(fake_service_->audio_parameters());
+ fake_speech_recognizer_->device_id());
+ ASSERT_TRUE(fake_speech_recognizer_->audio_parameters());
EXPECT_EQ(media::CHANNEL_LAYOUT_STEREO,
- fake_service_->audio_parameters()->channel_layout());
+ fake_speech_recognizer_->audio_parameters()->channel_layout());
// Picks a larger frames_per_buffer such that sample_rate/frames_per_buffer =
// kDefaultPollingTimesPerSecond.
EXPECT_EQ(sample_rate / kDefaultPollingTimesPerSecond,
- fake_service_->audio_parameters()->frames_per_buffer());
+ fake_speech_recognizer_->audio_parameters()->frames_per_buffer());
}
IN_PROC_BROWSER_TEST_F(SpeechRecognitionRecognizerClientImplTest,
@@ -318,11 +334,12 @@
StartListeningWithAudioParams(params);
EXPECT_EQ(media::AudioDeviceDescription::kDefaultDeviceId,
- fake_service_->device_id());
- ASSERT_TRUE(fake_service_->audio_parameters());
- EXPECT_EQ(sample_rate, fake_service_->audio_parameters()->sample_rate());
+ fake_speech_recognizer_->device_id());
+ ASSERT_TRUE(fake_speech_recognizer_->audio_parameters());
+ EXPECT_EQ(sample_rate,
+ fake_speech_recognizer_->audio_parameters()->sample_rate());
EXPECT_EQ(media::CHANNEL_LAYOUT_MONO,
- fake_service_->audio_parameters()->channel_layout());
+ fake_speech_recognizer_->audio_parameters()->channel_layout());
}
IN_PROC_BROWSER_TEST_F(SpeechRecognitionRecognizerClientImplTest,
@@ -332,13 +349,14 @@
StartListeningWithAudioParams(std::nullopt);
EXPECT_EQ(media::AudioDeviceDescription::kDefaultDeviceId,
- fake_service_->device_id());
- ASSERT_TRUE(fake_service_->audio_parameters());
+ fake_speech_recognizer_->device_id());
+ ASSERT_TRUE(fake_speech_recognizer_->audio_parameters());
EXPECT_EQ(kDefaultSampleRate,
- fake_service_->audio_parameters()->sample_rate());
- EXPECT_EQ(kDefaultPollingTimesPerSecond,
- fake_service_->audio_parameters()->sample_rate() /
- fake_service_->audio_parameters()->frames_per_buffer());
+ fake_speech_recognizer_->audio_parameters()->sample_rate());
+ EXPECT_EQ(
+ kDefaultPollingTimesPerSecond,
+ fake_speech_recognizer_->audio_parameters()->sample_rate() /
+ fake_speech_recognizer_->audio_parameters()->frames_per_buffer());
EXPECT_EQ(media::CHANNEL_LAYOUT_STEREO,
- fake_service_->audio_parameters()->channel_layout());
+ fake_speech_recognizer_->audio_parameters()->channel_layout());
}
diff --git a/chrome/browser/speech/speech_recognition_test_helper.cc b/chrome/browser/speech/speech_recognition_test_helper.cc
index 4e0268a..86118b4 100644
--- a/chrome/browser/speech/speech_recognition_test_helper.cc
+++ b/chrome/browser/speech/speech_recognition_test_helper.cc
@@ -10,6 +10,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/speech/cros_speech_recognition_service_factory.h"
#include "chrome/browser/speech/fake_speech_recognition_service.h"
+#include "chrome/browser/speech/fake_speech_recognizer.h"
#include "chrome/browser/speech/speech_recognition_constants.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/soda/soda_installer.h"
@@ -18,8 +19,9 @@
#include "media/mojo/mojom/speech_recognition.mojom.h"
SpeechRecognitionTestHelper::SpeechRecognitionTestHelper(
- speech::SpeechRecognitionType type)
- : type_(type) {}
+ speech::SpeechRecognitionType type,
+ media::mojom::RecognizerClientType client_type)
+ : feature_under_test_(client_type), type_(type) {}
SpeechRecognitionTestHelper::~SpeechRecognitionTestHelper() = default;
void SpeechRecognitionTestHelper::SetUp(Profile* profile) {
@@ -51,24 +53,41 @@
base::Unretained(this)));
}
+void SpeechRecognitionTestHelper::OnRecognizerBound(
+ speech::FakeSpeechRecognizer* bound_recognizer) {
+ if (bound_recognizer->recognition_options()->recognizer_client_type ==
+ feature_under_test_) {
+ fake_recognizer_ = bound_recognizer->GetWeakPtr();
+ }
+}
+
std::unique_ptr<KeyedService>
SpeechRecognitionTestHelper::CreateTestOnDeviceSpeechRecognitionService(
content::BrowserContext* context) {
std::unique_ptr<speech::FakeSpeechRecognitionService> fake_service =
std::make_unique<speech::FakeSpeechRecognitionService>();
fake_service_ = fake_service.get();
+ fake_service_->AddObserver(this);
return std::move(fake_service);
}
void SpeechRecognitionTestHelper::WaitForRecognitionStarted() {
+ // In the case of OnDevice recognition tests the fake_recognizer_ will not
+ // exist until events have propagated to the point that a recognition client
+ // impl is instantiated. So here we will wait for events to propagate in case
+ // the fake_recognizer_ needs to be instantiated.
+ base::RunLoop().RunUntilIdle();
+
// Only wait for recognition to start if it hasn't been started yet.
if (type_ == speech::SpeechRecognitionType::kNetwork &&
!fake_speech_recognition_manager_->is_recognizing()) {
fake_speech_recognition_manager_->WaitForRecognitionStarted();
} else if (type_ == speech::SpeechRecognitionType::kOnDevice &&
- !fake_service_->is_capturing_audio()) {
- fake_service_->WaitForRecognitionStarted();
+ fake_recognizer_ && !fake_recognizer_->is_capturing_audio()) {
+ fake_recognizer_->WaitForRecognitionStarted();
}
+
+ // Wait for any subsequent events to propagate.
base::RunLoop().RunUntilIdle();
}
@@ -101,8 +120,8 @@
false /* end recognition */, loop.QuitClosure());
loop.Run();
} else {
- DCHECK(fake_service_->is_capturing_audio());
- fake_service_->SendSpeechRecognitionResult(
+ CHECK(fake_recognizer_->is_capturing_audio());
+ fake_recognizer_->SendSpeechRecognitionResult(
media::SpeechRecognitionResult(transcript, is_final));
loop.RunUntilIdle();
}
@@ -114,7 +133,8 @@
fake_speech_recognition_manager_->SendFakeError(loop.QuitClosure());
loop.Run();
} else {
- fake_service_->SendSpeechRecognitionError();
+ CHECK(fake_recognizer_);
+ fake_recognizer_->SendSpeechRecognitionError();
loop.RunUntilIdle();
}
}
diff --git a/chrome/browser/speech/speech_recognition_test_helper.h b/chrome/browser/speech/speech_recognition_test_helper.h
index 9a675099..29da057 100644
--- a/chrome/browser/speech/speech_recognition_test_helper.h
+++ b/chrome/browser/speech/speech_recognition_test_helper.h
@@ -11,6 +11,7 @@
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/speech/fake_speech_recognition_service.h"
class KeyedService;
class Profile;
@@ -21,7 +22,7 @@
} // namespace content
namespace speech {
-class FakeSpeechRecognitionService;
+class FakeSpeechRecognizer;
enum class SpeechRecognitionType;
} // namespace speech
@@ -36,10 +37,13 @@
//
// For examples, please see SpeechRecognitionPrivateBaseTest or
// DictationBaseTest.
-class SpeechRecognitionTestHelper {
+class SpeechRecognitionTestHelper
+ : public speech::FakeSpeechRecognitionService::Observer {
public:
- explicit SpeechRecognitionTestHelper(speech::SpeechRecognitionType type);
- ~SpeechRecognitionTestHelper();
+ explicit SpeechRecognitionTestHelper(
+ speech::SpeechRecognitionType type,
+ media::mojom::RecognizerClientType client_type);
+ ~SpeechRecognitionTestHelper() override;
SpeechRecognitionTestHelper(const SpeechRecognitionTestHelper&) = delete;
SpeechRecognitionTestHelper& operator=(const SpeechRecognitionTestHelper&) =
delete;
@@ -62,6 +66,10 @@
// Returns a list of features that should be disabled.
std::vector<base::test::FeatureRef> GetDisabledFeatures();
+ // FakeSpeechRecognitionService::Observer
+ void OnRecognizerBound(
+ speech::FakeSpeechRecognizer* bound_recognizer) override;
+
private:
// Methods for setup.
void SetUpNetworkRecognition();
@@ -73,6 +81,10 @@
void SendFakeSpeechResultAndWait(const std::string& transcript,
bool is_final);
+ // Represents the feature under test, we use this to identify the correct
+ // FakeSpeechRecognizer when it becomes bound.
+ media::mojom::RecognizerClientType feature_under_test_;
+
speech::SpeechRecognitionType type_;
// For network recognition.
std::unique_ptr<content::FakeSpeechRecognitionManager>
@@ -80,6 +92,9 @@
// For on-device recognition. KeyedService owned by the test profile.
raw_ptr<speech::FakeSpeechRecognitionService, DanglingUntriaged>
fake_service_;
+ // For on-device recognition, this is the fakeSpeechRecognizer passed to the
+ // fake service, used for checking session status and assertions.
+ base::WeakPtr<speech::FakeSpeechRecognizer> fake_recognizer_;
};
#endif // CHROME_BROWSER_SPEECH_SPEECH_RECOGNITION_TEST_HELPER_H_
diff --git a/chrome/browser/ui/ash/DEPS b/chrome/browser/ui/ash/DEPS
index ee6a17c..21497b8 100644
--- a/chrome/browser/ui/ash/DEPS
+++ b/chrome/browser/ui/ash/DEPS
@@ -120,6 +120,7 @@
"+chrome/browser/signin/identity_test_environment_profile_adaptor.h",
"+chrome/browser/speech/cros_speech_recognition_service_factory.h",
"+chrome/browser/speech/fake_speech_recognition_service.h",
+ "+chrome/browser/speech/fake_speech_recognizer.h",
"+chrome/browser/speech/speech_recognition_recognizer_client_impl.h",
"+chrome/browser/speech/speech_recognizer_delegate.h",
"+chrome/browser/supervised_user/supervised_user_service_factory.h",
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc b/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc
index 5c520c13..bcdaab63 100644
--- a/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc
+++ b/chrome/browser/ui/ash/projector/projector_client_impl_unittest.cc
@@ -16,6 +16,7 @@
#include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
#include "ash/webui/projector_app/test/mock_app_client.h"
#include "base/memory/raw_ptr.h"
+#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/branding_buildflags.h"
@@ -23,6 +24,7 @@
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/speech/cros_speech_recognition_service_factory.h"
#include "chrome/browser/speech/fake_speech_recognition_service.h"
+#include "chrome/browser/speech/fake_speech_recognizer.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
@@ -113,7 +115,8 @@
} // namespace
class ProjectorClientImplUnitTest
- : public testing::TestWithParam<ProjectorClientTestScenario> {
+ : public testing::TestWithParam<ProjectorClientTestScenario>,
+ public speech::FakeSpeechRecognitionService::Observer {
public:
ProjectorClientImplUnitTest() = default;
@@ -138,13 +141,6 @@
ASSERT_TRUE(testing_profile_manager_.SetUp());
testing_profile_ = ProfileManager::GetPrimaryUserProfile();
ASSERT_TRUE(testing_profile_);
-
- CrosSpeechRecognitionServiceFactory::GetInstanceForTest()
- ->SetTestingFactoryAndUse(
- profile(),
- base::BindRepeating(&ProjectorClientImplUnitTest::
- CreateTestSpeechRecognitionService,
- base::Unretained(this)));
SetLocale(kEnglishUS);
soda_installer_ = std::make_unique<MockSodaInstaller>();
ON_CALL(*soda_installer_, GetAvailableLanguages)
@@ -155,6 +151,11 @@
mock_locale_controller_ = std::make_unique<MockLocaleUpdateController>();
projector_client_ =
std::make_unique<ProjectorClientImpl>(&projector_controller_);
+ CrosSpeechRecognitionServiceFactory::GetInstanceForTest()
+ ->SetTestingFactoryAndUse(
+ profile(), base::BindOnce(&ProjectorClientImplUnitTest::
+ CreateTestSpeechRecognitionService,
+ base::Unretained(this)));
}
void TearDown() override {
@@ -170,24 +171,33 @@
std::unique_ptr<speech::FakeSpeechRecognitionService> fake_service =
std::make_unique<speech::FakeSpeechRecognitionService>();
fake_service_ = fake_service.get();
- return std::move(fake_service);
+ fake_service_->AddObserver(this);
+ return fake_service;
}
void SendSpeechResult(const char* result, bool is_final) {
- EXPECT_TRUE(fake_service_->is_capturing_audio());
+ EXPECT_TRUE(fake_recognizer_->is_capturing_audio());
base::RunLoop loop;
- fake_service_->SendSpeechRecognitionResult(
+ fake_recognizer_->SendSpeechRecognitionResult(
media::SpeechRecognitionResult(result, is_final));
loop.RunUntilIdle();
}
void SendTranscriptionError() {
- EXPECT_TRUE(fake_service_->is_capturing_audio());
+ EXPECT_TRUE(fake_recognizer_->is_capturing_audio());
base::RunLoop loop;
- fake_service_->SendSpeechRecognitionError();
+ fake_recognizer_->SendSpeechRecognitionError();
loop.RunUntilIdle();
}
+ void OnRecognizerBound(
+ speech::FakeSpeechRecognizer* bound_recognizer) override {
+ if (bound_recognizer->recognition_options()->recognizer_client_type ==
+ media::mojom::RecognizerClientType::kProjector) {
+ fake_recognizer_ = bound_recognizer->GetWeakPtr();
+ }
+ }
+
protected:
// Start speech recognition and verify on device to server based speech
// recognition fallback reason. If `expected_reason` is null, there will be no
@@ -224,13 +234,17 @@
std::unique_ptr<MockAppClient> mock_app_client_;
std::unique_ptr<MockLocaleUpdateController> mock_locale_controller_;
raw_ptr<speech::FakeSpeechRecognitionService> fake_service_;
+ base::WeakPtr<speech::FakeSpeechRecognizer> fake_recognizer_;
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_P(ProjectorClientImplUnitTest, SpeechRecognitionResults) {
- client()->StartSpeechRecognition();
- fake_service_->WaitForRecognitionStarted();
+ ProjectorClient* got_client = client();
+ ASSERT_TRUE(got_client);
+
+ got_client->StartSpeechRecognition();
+ base::RunLoop().RunUntilIdle();
EXPECT_CALL(projector_controller(),
OnTranscription(