| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <set> |
| |
| #include "base/unguessable_token.h" |
| #include "base/values.h" |
| #include "chrome/browser/speech/extension_api/tts_extension_api_constants.h" |
| #include "chrome/browser/speech/tts_crosapi_util.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chromeos/crosapi/mojom/tts.mojom.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/tts_controller.h" |
| #include "content/public/browser/tts_utterance.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/test_renderer_host.h" |
| #include "content/public/test/web_contents_tester.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| const char kText[] = "hello, world"; |
| const char kVoiceName[] = "Alice"; |
| const char kLang[] = "en-GB"; |
| const int kSrcId = 12345; |
| const char kSrcUrl[] = "http://test.com"; |
| const char kEngineId[] = "test_engine_id"; |
| const double kRate = 1.0f; |
| const double kPitch = 0.5f; |
| const double kVolume = 0.9f; |
| const base::UnguessableToken kBrowerContextId = |
| base::UnguessableToken::Create(); |
| |
| bool EventTypesMatches( |
| const std::set<content::TtsEventType>& tts_event_types_in, |
| const std::set<content::TtsEventType>& tts_event_types_out) { |
| if (tts_event_types_in.size() != tts_event_types_out.size()) |
| return false; |
| |
| for (const auto& event_type : tts_event_types_in) { |
| if (tts_event_types_out.find(event_type) == tts_event_types_out.end()) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| class TtsUtteranceMojomTest : public testing::Test { |
| public: |
| TtsUtteranceMojomTest() |
| : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP), |
| rvh_test_enabler_(new content::RenderViewHostTestEnabler()) {} |
| |
| TtsUtteranceMojomTest(const TtsUtteranceMojomTest&) = delete; |
| TtsUtteranceMojomTest& operator=(const TtsUtteranceMojomTest&) = delete; |
| |
| void SetUp() override { |
| testing::Test::SetUp(); |
| |
| testing_profile_ = TestingProfile::Builder().Build(); |
| web_contents_ = CreateTestWebContents(); |
| } |
| |
| protected: |
| content::BrowserContext* browser_context() { return testing_profile_.get(); } |
| content::WebContents* GetWebContents() { return web_contents_.get(); } |
| |
| private: |
| std::unique_ptr<content::WebContents> CreateTestWebContents() { |
| auto site_instance = content::SiteInstance::Create(browser_context()); |
| return content::WebContentsTester::CreateTestWebContents( |
| browser_context(), std::move(site_instance)); |
| } |
| |
| content::BrowserTaskEnvironment task_environment_; |
| std::unique_ptr<content::RenderViewHostTestEnabler> rvh_test_enabler_; |
| std::unique_ptr<TestingProfile> testing_profile_; |
| std::unique_ptr<content::WebContents> web_contents_; |
| }; |
| |
| // Test that every field in content::TtsUtterance in correctly converted |
| // with a round trip conversion to and from crosapi::mojom::TtsUtterance. |
| TEST_F(TtsUtteranceMojomTest, RoundTripWithoutOptions) { |
| std::unique_ptr<content::TtsUtterance> in_utterance = |
| content::TtsUtterance::Create(); |
| in_utterance->SetText(kText); |
| in_utterance->SetVoiceName(kVoiceName); |
| in_utterance->SetLang(kLang); |
| in_utterance->SetSrcId(kSrcId); |
| in_utterance->SetSrcUrl(GURL(kSrcUrl)); |
| in_utterance->SetEngineId(kEngineId); |
| in_utterance->SetContinuousParameters(kRate, kPitch, kVolume); |
| in_utterance->SetShouldClearQueue(true); |
| |
| std::set<content::TtsEventType> required_event_types; |
| required_event_types.insert(content::TTS_EVENT_START); |
| required_event_types.insert(content::TTS_EVENT_WORD); |
| required_event_types.insert(content::TTS_EVENT_END); |
| in_utterance->SetRequiredEventTypes(required_event_types); |
| |
| std::set<content::TtsEventType> desired_event_types; |
| desired_event_types.insert(content::TTS_EVENT_CANCELLED); |
| desired_event_types.insert(content::TTS_EVENT_RESUME); |
| desired_event_types.insert(content::TTS_EVENT_PAUSE); |
| in_utterance->SetDesiredEventTypes(desired_event_types); |
| |
| // Round trip conversion to and from mojom utterance. |
| auto mojo_utterance = tts_crosapi_util::ToMojo(in_utterance.get()); |
| mojo_utterance->browser_context_id = kBrowerContextId; |
| |
| { |
| // Create TtsUtterance for a Lacros Utterance. |
| std::unique_ptr<content::TtsUtterance> out_utterance = |
| tts_crosapi_util::CreateUtteranceFromMojo( |
| mojo_utterance, /*should_always_be_spoken=*/true); |
| |
| ASSERT_EQ(out_utterance->GetText(), kText); |
| ASSERT_EQ(out_utterance->GetVoiceName(), kVoiceName); |
| ASSERT_EQ(out_utterance->GetLang(), kLang); |
| ASSERT_EQ(out_utterance->GetSrcId(), kSrcId); |
| ASSERT_EQ(out_utterance->GetSrcUrl(), GURL(kSrcUrl)); |
| ASSERT_EQ(out_utterance->GetEngineId(), kEngineId); |
| auto continuouse_params = out_utterance->GetContinuousParameters(); |
| ASSERT_EQ(continuouse_params.rate, kRate); |
| ASSERT_EQ(continuouse_params.pitch, kPitch); |
| ASSERT_EQ(continuouse_params.volume, kVolume); |
| ASSERT_TRUE(out_utterance->GetShouldClearQueue()); |
| |
| ASSERT_TRUE(EventTypesMatches(in_utterance->GetRequiredEventTypes(), |
| out_utterance->GetRequiredEventTypes())); |
| ASSERT_TRUE(EventTypesMatches(in_utterance->GetDesiredEventTypes(), |
| out_utterance->GetDesiredEventTypes())); |
| |
| ASSERT_TRUE(out_utterance->ShouldAlwaysBeSpoken()); |
| } |
| |
| { |
| // Create TtsUtterance for an Ash Utterance. |
| std::unique_ptr<content::TtsUtterance> out_utterance = |
| tts_crosapi_util::CreateUtteranceFromMojo( |
| mojo_utterance, /*should_always_be_spoken=*/false); |
| |
| ASSERT_EQ(out_utterance->GetText(), kText); |
| ASSERT_EQ(out_utterance->GetVoiceName(), kVoiceName); |
| ASSERT_EQ(out_utterance->GetLang(), kLang); |
| ASSERT_EQ(out_utterance->GetSrcId(), kSrcId); |
| ASSERT_EQ(out_utterance->GetSrcUrl(), GURL(kSrcUrl)); |
| ASSERT_EQ(out_utterance->GetEngineId(), kEngineId); |
| auto continuouse_params = out_utterance->GetContinuousParameters(); |
| ASSERT_EQ(continuouse_params.rate, kRate); |
| ASSERT_EQ(continuouse_params.pitch, kPitch); |
| ASSERT_EQ(continuouse_params.volume, kVolume); |
| ASSERT_TRUE(out_utterance->GetShouldClearQueue()); |
| |
| ASSERT_TRUE(EventTypesMatches(in_utterance->GetRequiredEventTypes(), |
| out_utterance->GetRequiredEventTypes())); |
| ASSERT_TRUE(EventTypesMatches(in_utterance->GetDesiredEventTypes(), |
| out_utterance->GetDesiredEventTypes())); |
| |
| ASSERT_FALSE(out_utterance->ShouldAlwaysBeSpoken()); |
| } |
| } |
| |
| TEST_F(TtsUtteranceMojomTest, RoundTripWithOptions) { |
| std::unique_ptr<content::TtsUtterance> in_utterance = |
| content::TtsUtterance::Create(); |
| base::Value::Dict in_options; |
| in_options.Set(tts_extension_api_constants::kVoiceNameKey, kVoiceName); |
| in_options.Set(tts_extension_api_constants::kRateKey, kRate); |
| in_options.Set(tts_extension_api_constants::kEnqueueKey, true); |
| in_utterance->SetOptions(std::move(in_options)); |
| |
| auto mojo_utterance = tts_crosapi_util::ToMojo(in_utterance.get()); |
| std::unique_ptr<content::TtsUtterance> out_utterance = |
| tts_crosapi_util::CreateUtteranceFromMojo( |
| mojo_utterance, /*should_always_be_spoken=*/true); |
| |
| auto* out_options = out_utterance->GetOptions(); |
| |
| const base::Value* voice_value = |
| out_options->Find(tts_extension_api_constants::kVoiceNameKey); |
| ASSERT_TRUE(voice_value); |
| ASSERT_TRUE(voice_value->is_string()); |
| ASSERT_EQ(voice_value->GetString(), kVoiceName); |
| |
| const base::Value* rate_value = |
| out_options->Find(tts_extension_api_constants::kRateKey); |
| ASSERT_TRUE(rate_value); |
| ASSERT_TRUE(rate_value->is_double()); |
| ASSERT_EQ(rate_value->GetDouble(), kRate); |
| |
| const base::Value* enqueue_value = |
| out_options->Find(tts_extension_api_constants::kEnqueueKey); |
| ASSERT_TRUE(enqueue_value); |
| ASSERT_TRUE(enqueue_value->is_bool()); |
| ASSERT_TRUE(enqueue_value->GetBool()); |
| } |
| |
| TEST_F(TtsUtteranceMojomTest, WasCreatedWithNoWebContents) { |
| std::unique_ptr<content::TtsUtterance> in_utterance = |
| content::TtsUtterance::Create(); |
| ASSERT_FALSE(in_utterance->GetWebContents()); |
| auto mojo_utterance = tts_crosapi_util::ToMojo(in_utterance.get()); |
| ASSERT_FALSE(mojo_utterance->was_created_with_web_contents); |
| } |
| |
| TEST_F(TtsUtteranceMojomTest, WasCreatedWithWebContents) { |
| content::WebContents* web_contents = GetWebContents(); |
| std::unique_ptr<content::TtsUtterance> in_utterance = |
| content::TtsUtterance::Create(web_contents); |
| auto mojo_utterance = tts_crosapi_util::ToMojo(in_utterance.get()); |
| ASSERT_TRUE(mojo_utterance->was_created_with_web_contents); |
| |
| // Finish |in_utterance| so that it can be destructed without DCHECK |
| // failure. |
| in_utterance->Finish(); |
| } |