1 // Copyright (c) 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Unit tests for the TTS Controller.
6
7 #include "base/memory/ptr_util.h"
8 #include "base/values.h"
9 #include "content/browser/speech/tts_controller_impl.h"
10 #include "content/public/browser/tts_controller_delegate.h"
11 #include "content/public/browser/tts_platform.h"
12 #include "content/public/test/browser_task_environment.h"
13 #include "content/public/test/test_browser_context.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h"
16
17 namespace content {
18
19 class TtsControllerTest : public testing::Test {};
20
21 // Platform Tts implementation that does nothing.
22 class MockTtsPlatformImpl : public TtsPlatform {
23 public:
MockTtsPlatformImpl()24 MockTtsPlatformImpl() {}
~MockTtsPlatformImpl()25 virtual ~MockTtsPlatformImpl() {}
PlatformImplAvailable()26 bool PlatformImplAvailable() override { return true; }
Speak(int utterance_id,const std::string & utterance,const std::string & lang,const VoiceData & voice,const UtteranceContinuousParameters & params,base::OnceCallback<void (bool)> on_speak_finished)27 void Speak(int utterance_id,
28 const std::string& utterance,
29 const std::string& lang,
30 const VoiceData& voice,
31 const UtteranceContinuousParameters& params,
32 base::OnceCallback<void(bool)> on_speak_finished) override {
33 std::move(on_speak_finished).Run(true);
34 }
IsSpeaking()35 bool IsSpeaking() override { return false; }
StopSpeaking()36 bool StopSpeaking() override { return true; }
Pause()37 void Pause() override {}
Resume()38 void Resume() override {}
GetVoices(std::vector<VoiceData> * out_voices)39 void GetVoices(std::vector<VoiceData>* out_voices) override {}
LoadBuiltInTtsEngine(BrowserContext * browser_context)40 bool LoadBuiltInTtsEngine(BrowserContext* browser_context) override {
41 return false;
42 }
WillSpeakUtteranceWithVoice(TtsUtterance * utterance,const VoiceData & voice_data)43 void WillSpeakUtteranceWithVoice(TtsUtterance* utterance,
44 const VoiceData& voice_data) override {}
SetError(const std::string & error)45 void SetError(const std::string& error) override {}
GetError()46 std::string GetError() override { return std::string(); }
ClearError()47 void ClearError() override {}
48 };
49
50 class MockTtsControllerDelegate : public TtsControllerDelegate {
51 public:
MockTtsControllerDelegate()52 MockTtsControllerDelegate() {}
~MockTtsControllerDelegate()53 ~MockTtsControllerDelegate() override {}
54
GetLastBrowserContext()55 BrowserContext* GetLastBrowserContext() {
56 BrowserContext* result = last_browser_context_;
57 last_browser_context_ = nullptr;
58 return result;
59 }
60
GetMatchingVoice(content::TtsUtterance * utterance,std::vector<content::VoiceData> & voices)61 int GetMatchingVoice(content::TtsUtterance* utterance,
62 std::vector<content::VoiceData>& voices) override {
63 last_browser_context_ = utterance->GetBrowserContext();
64 return -1;
65 }
66
UpdateUtteranceDefaultsFromPrefs(content::TtsUtterance * utterance,double * rate,double * pitch,double * volume)67 void UpdateUtteranceDefaultsFromPrefs(content::TtsUtterance* utterance,
68 double* rate,
69 double* pitch,
70 double* volume) override {}
71
SetTtsEngineDelegate(content::TtsEngineDelegate * delegate)72 void SetTtsEngineDelegate(content::TtsEngineDelegate* delegate) override {}
73
GetTtsEngineDelegate()74 content::TtsEngineDelegate* GetTtsEngineDelegate() override {
75 return nullptr;
76 }
77
78 private:
79 BrowserContext* last_browser_context_ = nullptr;
80 };
81
82 // Subclass of TtsController with a public ctor and dtor.
83 class TtsControllerForTesting : public TtsControllerImpl {
84 public:
TtsControllerForTesting()85 TtsControllerForTesting() {}
~TtsControllerForTesting()86 ~TtsControllerForTesting() override {}
87 };
88
TEST_F(TtsControllerTest,TestTtsControllerShutdown)89 TEST_F(TtsControllerTest, TestTtsControllerShutdown) {
90 MockTtsPlatformImpl platform_impl;
91 TtsControllerForTesting* controller = new TtsControllerForTesting();
92 MockTtsControllerDelegate* delegate = new MockTtsControllerDelegate();
93 controller->delegate_ = delegate;
94
95 controller->SetTtsPlatform(&platform_impl);
96
97 std::unique_ptr<TtsUtterance> utterance1 = TtsUtterance::Create(nullptr);
98 utterance1->SetCanEnqueue(true);
99 utterance1->SetSrcId(1);
100 controller->SpeakOrEnqueue(std::move(utterance1));
101
102 std::unique_ptr<TtsUtterance> utterance2 = TtsUtterance::Create(nullptr);
103 utterance2->SetCanEnqueue(true);
104 utterance2->SetSrcId(2);
105 controller->SpeakOrEnqueue(std::move(utterance2));
106
107 // Make sure that deleting the controller when there are pending
108 // utterances doesn't cause a crash.
109 delete controller;
110
111 // Clean up.
112 delete delegate;
113 }
114
TEST_F(TtsControllerTest,TestBrowserContextRemoved)115 TEST_F(TtsControllerTest, TestBrowserContextRemoved) {
116 // Create a controller, mock other stuff, and create a test
117 // browser context.
118 TtsControllerImpl* controller = TtsControllerImpl::GetInstance();
119 MockTtsPlatformImpl platform_impl;
120 MockTtsControllerDelegate delegate;
121 controller->delegate_ = &delegate;
122 controller->SetTtsPlatform(&platform_impl);
123 content::BrowserTaskEnvironment task_environment;
124 auto browser_context = std::make_unique<TestBrowserContext>();
125
126 // Speak an utterances associated with this test browser context.
127 std::unique_ptr<TtsUtterance> utterance1 =
128 TtsUtterance::Create(browser_context.get());
129 utterance1->SetCanEnqueue(true);
130 utterance1->SetSrcId(1);
131 controller->SpeakOrEnqueue(std::move(utterance1));
132
133 // Assert that the delegate was called and it got our browser context.
134 ASSERT_EQ(browser_context.get(), delegate.GetLastBrowserContext());
135
136 // Now queue up a second utterance to be spoken, also associated with
137 // this browser context.
138 std::unique_ptr<TtsUtterance> utterance2 =
139 TtsUtterance::Create(browser_context.get());
140 utterance2->SetCanEnqueue(true);
141 utterance2->SetSrcId(2);
142 controller->SpeakOrEnqueue(std::move(utterance2));
143
144 // Destroy the browser context before the utterance is spoken.
145 browser_context.reset();
146
147 // Now speak the next utterance, and ensure that we don't get the
148 // destroyed browser context.
149 controller->FinishCurrentUtterance();
150 controller->SpeakNextUtterance();
151 ASSERT_EQ(nullptr, delegate.GetLastBrowserContext());
152 }
153
154 #if !defined(OS_CHROMEOS)
TEST_F(TtsControllerTest,TestTtsControllerUtteranceDefaults)155 TEST_F(TtsControllerTest, TestTtsControllerUtteranceDefaults) {
156 std::unique_ptr<TtsControllerForTesting> controller =
157 std::make_unique<TtsControllerForTesting>();
158
159 std::unique_ptr<TtsUtterance> utterance1 =
160 content::TtsUtterance::Create(nullptr);
161 // Initialized to default (unset constant) values.
162 EXPECT_EQ(blink::mojom::kSpeechSynthesisDoublePrefNotSet,
163 utterance1->GetContinuousParameters().rate);
164 EXPECT_EQ(blink::mojom::kSpeechSynthesisDoublePrefNotSet,
165 utterance1->GetContinuousParameters().pitch);
166 EXPECT_EQ(blink::mojom::kSpeechSynthesisDoublePrefNotSet,
167 utterance1->GetContinuousParameters().volume);
168
169 controller->UpdateUtteranceDefaults(utterance1.get());
170 // Updated to global defaults.
171 EXPECT_EQ(blink::mojom::kSpeechSynthesisDefaultRate,
172 utterance1->GetContinuousParameters().rate);
173 EXPECT_EQ(blink::mojom::kSpeechSynthesisDefaultPitch,
174 utterance1->GetContinuousParameters().pitch);
175 EXPECT_EQ(blink::mojom::kSpeechSynthesisDefaultVolume,
176 utterance1->GetContinuousParameters().volume);
177 }
178 #endif // !defined(OS_CHROMEOS)
179
180 } // namespace content
181