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 "content/browser/speech/tts_controller_impl.h"
8
9 #include "base/memory/ptr_util.h"
10 #include "base/values.h"
11 #include "content/browser/speech/tts_utterance_impl.h"
12 #include "content/public/browser/tts_platform.h"
13 #include "content/public/browser/visibility.h"
14 #include "content/public/test/browser_task_environment.h"
15 #include "content/public/test/test_browser_context.h"
16 #include "content/public/test/test_renderer_host.h"
17 #include "content/test/test_content_browser_client.h"
18 #include "content/test/test_web_contents.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 #include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h"
21
22 #if defined(OS_CHROMEOS)
23 #include "content/public/browser/tts_controller_delegate.h"
24 #endif
25
26 namespace content {
27
28 // Platform Tts implementation that does nothing.
29 class MockTtsPlatformImpl : public TtsPlatform {
30 public:
MockTtsPlatformImpl(TtsController * controller)31 explicit MockTtsPlatformImpl(TtsController* controller)
32 : controller_(controller) {}
33 virtual ~MockTtsPlatformImpl() = default;
34
35 // Override the Mock API results.
set_voices(const std::vector<VoiceData> & voices)36 void set_voices(const std::vector<VoiceData>& voices) { voices_ = voices; }
set_is_speaking(bool value)37 void set_is_speaking(bool value) { is_speaking_ = value; }
38
39 // TtsPlatform:
PlatformImplSupported()40 bool PlatformImplSupported() override { return platform_supported_; }
PlatformImplInitialized()41 bool PlatformImplInitialized() override { return platform_initialized_; }
42
Speak(int utterance_id,const std::string & utterance,const std::string & lang,const VoiceData & voice,const UtteranceContinuousParameters & params,base::OnceCallback<void (bool)> did_start_speaking_callback)43 void Speak(
44 int utterance_id,
45 const std::string& utterance,
46 const std::string& lang,
47 const VoiceData& voice,
48 const UtteranceContinuousParameters& params,
49 base::OnceCallback<void(bool)> did_start_speaking_callback) override {
50 utterance_id_ = utterance_id;
51 did_start_speaking_callback_ = std::move(did_start_speaking_callback);
52 }
IsSpeaking()53 bool IsSpeaking() override { return is_speaking_; }
StopSpeaking()54 bool StopSpeaking() override {
55 ++stop_speaking_called_;
56 return true;
57 }
Pause()58 void Pause() override { ++pause_called_; }
Resume()59 void Resume() override { ++resume_called_; }
GetVoices(std::vector<VoiceData> * out_voices)60 void GetVoices(std::vector<VoiceData>* out_voices) override {
61 *out_voices = voices_;
62 }
LoadBuiltInTtsEngine(BrowserContext * browser_context)63 bool LoadBuiltInTtsEngine(BrowserContext* browser_context) override {
64 return false;
65 }
WillSpeakUtteranceWithVoice(TtsUtterance * utterance,const VoiceData & voice_data)66 void WillSpeakUtteranceWithVoice(TtsUtterance* utterance,
67 const VoiceData& voice_data) override {}
SetError(const std::string & error)68 void SetError(const std::string& error) override { error_ = error; }
GetError()69 std::string GetError() override { return error_; }
ClearError()70 void ClearError() override { error_.clear(); }
Shutdown()71 void Shutdown() override {}
72
SetPlatformImplSupported(bool state)73 void SetPlatformImplSupported(bool state) { platform_supported_ = state; }
SetPlatformImplInitialized(bool state)74 void SetPlatformImplInitialized(bool state) { platform_initialized_ = state; }
75
76 // Returns the amount of calls to Mock API.
pause_called() const77 int pause_called() const { return pause_called_; }
resume_called() const78 int resume_called() const { return resume_called_; }
stop_speaking_called() const79 int stop_speaking_called() const { return stop_speaking_called_; }
80
81 // Simulate the TTS platform calling back the closure
82 // |did_start_speaking_callback| passed to Speak(...). This closure can be
83 // called synchronously or asynchronously.
StartSpeaking(bool result)84 void StartSpeaking(bool result) {
85 is_speaking_ = true;
86 std::move(did_start_speaking_callback_).Run(result);
87 }
88
FinishSpeaking()89 void FinishSpeaking() {
90 is_speaking_ = false;
91 controller_->OnTtsEvent(utterance_id_, TTS_EVENT_END, 0, 0, {});
92 utterance_id_ = -1;
93 }
94
95 private:
96 TtsController* const controller_;
97 bool platform_supported_ = true;
98 bool platform_initialized_ = true;
99 std::vector<VoiceData> voices_;
100 int utterance_id_ = -1;
101 bool is_speaking_ = false;
102 int pause_called_ = 0;
103 int resume_called_ = 0;
104 int stop_speaking_called_ = 0;
105 std::string error_;
106 base::OnceCallback<void(bool)> did_start_speaking_callback_;
107 };
108
109 #if defined(OS_CHROMEOS)
110 class MockTtsControllerDelegate : public TtsControllerDelegate {
111 public:
112 MockTtsControllerDelegate() = default;
113 ~MockTtsControllerDelegate() override = default;
114
SetPreferredVoiceIds(const PreferredVoiceIds & ids)115 void SetPreferredVoiceIds(const PreferredVoiceIds& ids) { ids_ = ids; }
116
GetLastBrowserContext()117 BrowserContext* GetLastBrowserContext() {
118 BrowserContext* result = last_browser_context_;
119 last_browser_context_ = nullptr;
120 return result;
121 }
122
123 // TtsControllerDelegate:
GetPreferredVoiceIdsForUtterance(TtsUtterance * utterance)124 std::unique_ptr<PreferredVoiceIds> GetPreferredVoiceIdsForUtterance(
125 TtsUtterance* utterance) override {
126 last_browser_context_ = utterance->GetBrowserContext();
127 auto ids = std::make_unique<PreferredVoiceIds>(ids_);
128 return ids;
129 }
130
UpdateUtteranceDefaultsFromPrefs(content::TtsUtterance * utterance,double * rate,double * pitch,double * volume)131 void UpdateUtteranceDefaultsFromPrefs(content::TtsUtterance* utterance,
132 double* rate,
133 double* pitch,
134 double* volume) override {}
135
136 private:
137 BrowserContext* last_browser_context_ = nullptr;
138 PreferredVoiceIds ids_;
139 };
140 #endif
141
142 class MockVoicesChangedDelegate : public VoicesChangedDelegate {
143 public:
OnVoicesChanged()144 void OnVoicesChanged() override {}
145 };
146
147 class TestTtsControllerImpl : public TtsControllerImpl {
148 public:
149 TestTtsControllerImpl() = default;
150 ~TestTtsControllerImpl() override = default;
151
152 // Exposed API for testing.
153 using TtsControllerImpl::FinishCurrentUtterance;
154 using TtsControllerImpl::GetMatchingVoice;
155 using TtsControllerImpl::SpeakNextUtterance;
156 using TtsControllerImpl::UpdateUtteranceDefaults;
157 #if defined(OS_CHROMEOS)
158 using TtsControllerImpl::SetTtsControllerDelegateForTesting;
159 #endif
160 using TtsControllerImpl::IsPausedForTesting;
161
current_utterance()162 TtsUtterance* current_utterance() { return current_utterance_.get(); }
163 };
164
165 class TtsControllerTest : public testing::Test {
166 public:
167 TtsControllerTest() = default;
168 ~TtsControllerTest() override = default;
169
SetUp()170 void SetUp() override {
171 controller_ = std::make_unique<TestTtsControllerImpl>();
172 platform_impl_ = std::make_unique<MockTtsPlatformImpl>(controller_.get());
173 browser_context_ = std::make_unique<TestBrowserContext>();
174 controller()->SetTtsPlatform(platform_impl_.get());
175 #if defined(OS_CHROMEOS)
176 controller()->SetTtsControllerDelegateForTesting(&delegate_);
177 #endif
178 controller()->AddVoicesChangedDelegate(&voices_changed_);
179 }
180
TearDown()181 void TearDown() override {
182 if (controller())
183 controller()->RemoveVoicesChangedDelegate(&voices_changed_);
184 }
185
platform_impl()186 MockTtsPlatformImpl* platform_impl() { return platform_impl_.get(); }
controller()187 TestTtsControllerImpl* controller() { return controller_.get(); }
browser_context()188 TestBrowserContext* browser_context() { return browser_context_.get(); }
189
190 #if defined(OS_CHROMEOS)
delegate()191 MockTtsControllerDelegate* delegate() { return &delegate_; }
192 #endif
193
ReleaseTtsController()194 void ReleaseTtsController() { controller_.reset(); }
ReleaseBrowserContext()195 void ReleaseBrowserContext() {
196 // BrowserContext::~BrowserContext(...) is calling OnBrowserContextDestroyed
197 // on the tts controller singleton. That call is simulated here to ensures
198 // it is called on our test controller instances.
199 controller()->OnBrowserContextDestroyed(browser_context_.get());
200 browser_context_.reset();
201 }
202
CreateWebContents()203 std::unique_ptr<TestWebContents> CreateWebContents() {
204 return std::unique_ptr<TestWebContents>(
205 TestWebContents::Create(browser_context_.get(), nullptr));
206 }
207
CreateUtteranceImpl(WebContents * web_contents=nullptr)208 std::unique_ptr<TtsUtteranceImpl> CreateUtteranceImpl(
209 WebContents* web_contents = nullptr) {
210 return std::make_unique<TtsUtteranceImpl>(browser_context_.get(),
211 web_contents);
212 }
213
TtsControllerCurrentUtterance()214 TtsUtterance* TtsControllerCurrentUtterance() {
215 return controller()->current_utterance();
216 }
217
IsUtteranceListEmpty()218 bool IsUtteranceListEmpty() { return controller()->QueueSize() == 0; }
219
220 private:
221 content::BrowserTaskEnvironment task_environment_;
222 RenderViewHostTestEnabler rvh_enabler_;
223
224 std::unique_ptr<TestTtsControllerImpl> controller_;
225 std::unique_ptr<MockTtsPlatformImpl> platform_impl_;
226 std::unique_ptr<TestBrowserContext> browser_context_;
227 #if defined(OS_CHROMEOS)
228 MockTtsControllerDelegate delegate_;
229 #endif
230 MockVoicesChangedDelegate voices_changed_;
231 };
232
TEST_F(TtsControllerTest,TestTtsControllerShutdown)233 TEST_F(TtsControllerTest, TestTtsControllerShutdown) {
234 std::unique_ptr<TtsUtterance> utterance1 = TtsUtterance::Create(nullptr);
235 utterance1->SetCanEnqueue(true);
236 utterance1->SetSrcId(1);
237 controller()->SpeakOrEnqueue(std::move(utterance1));
238
239 std::unique_ptr<TtsUtterance> utterance2 = TtsUtterance::Create(nullptr);
240 utterance2->SetCanEnqueue(true);
241 utterance2->SetSrcId(2);
242 controller()->SpeakOrEnqueue(std::move(utterance2));
243
244 // Make sure that deleting the controller when there are pending
245 // utterances doesn't cause a crash.
246 ReleaseTtsController();
247 }
248
249 #if defined(OS_CHROMEOS)
TEST_F(TtsControllerTest,TestBrowserContextRemoved)250 TEST_F(TtsControllerTest, TestBrowserContextRemoved) {
251 std::vector<VoiceData> voices;
252 VoiceData voice_data;
253 voice_data.engine_id = "x";
254 voice_data.events.insert(TTS_EVENT_END);
255 voices.push_back(voice_data);
256 platform_impl()->set_voices(voices);
257
258 // Speak an utterances associated with this test browser context.
259 std::unique_ptr<TtsUtterance> utterance1 =
260 TtsUtterance::Create(browser_context());
261 utterance1->SetEngineId("x");
262 utterance1->SetCanEnqueue(true);
263 utterance1->SetSrcId(1);
264 controller()->SpeakOrEnqueue(std::move(utterance1));
265
266 // Assert that the delegate was called and it got our browser context.
267 ASSERT_EQ(browser_context(), delegate()->GetLastBrowserContext());
268
269 // Now queue up a second utterance to be spoken, also associated with
270 // this browser context.
271 std::unique_ptr<TtsUtterance> utterance2 =
272 TtsUtterance::Create(browser_context());
273 utterance2->SetEngineId("x");
274 utterance2->SetCanEnqueue(true);
275 utterance2->SetSrcId(2);
276 controller()->SpeakOrEnqueue(std::move(utterance2));
277
278 // Destroy the browser context before the utterance is spoken.
279 ReleaseBrowserContext();
280
281 // Now speak the next utterance, and ensure that we don't get the
282 // destroyed browser context.
283 controller()->FinishCurrentUtterance();
284 controller()->SpeakNextUtterance();
285 ASSERT_EQ(nullptr, delegate()->GetLastBrowserContext());
286 }
287 #else
TEST_F(TtsControllerTest,TestTtsControllerUtteranceDefaults)288 TEST_F(TtsControllerTest, TestTtsControllerUtteranceDefaults) {
289 std::unique_ptr<TtsUtterance> utterance1 =
290 content::TtsUtterance::Create(nullptr);
291 // Initialized to default (unset constant) values.
292 EXPECT_EQ(blink::mojom::kSpeechSynthesisDoublePrefNotSet,
293 utterance1->GetContinuousParameters().rate);
294 EXPECT_EQ(blink::mojom::kSpeechSynthesisDoublePrefNotSet,
295 utterance1->GetContinuousParameters().pitch);
296 EXPECT_EQ(blink::mojom::kSpeechSynthesisDoublePrefNotSet,
297 utterance1->GetContinuousParameters().volume);
298
299 controller()->UpdateUtteranceDefaults(utterance1.get());
300 // Updated to global defaults.
301 EXPECT_EQ(blink::mojom::kSpeechSynthesisDefaultRate,
302 utterance1->GetContinuousParameters().rate);
303 EXPECT_EQ(blink::mojom::kSpeechSynthesisDefaultPitch,
304 utterance1->GetContinuousParameters().pitch);
305 EXPECT_EQ(blink::mojom::kSpeechSynthesisDefaultVolume,
306 utterance1->GetContinuousParameters().volume);
307 }
308 #endif
309
TEST_F(TtsControllerTest,TestGetMatchingVoice)310 TEST_F(TtsControllerTest, TestGetMatchingVoice) {
311 TestContentBrowserClient::GetInstance()->set_application_locale("en");
312
313 {
314 // Calling GetMatchingVoice with no voices returns -1.
315 std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create(nullptr));
316 std::vector<VoiceData> voices;
317 EXPECT_EQ(-1, controller()->GetMatchingVoice(utterance.get(), voices));
318 }
319
320 {
321 // Calling GetMatchingVoice with any voices returns the first one
322 // even if there are no criteria that match.
323 std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create(nullptr));
324 std::vector<VoiceData> voices(2);
325 EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices));
326 }
327
328 {
329 // If nothing else matches, the English voice is returned.
330 // (In tests the language will always be English.)
331 std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create(nullptr));
332 std::vector<VoiceData> voices;
333 VoiceData fr_voice;
334 fr_voice.lang = "fr";
335 voices.push_back(fr_voice);
336 VoiceData en_voice;
337 en_voice.lang = "en";
338 voices.push_back(en_voice);
339 VoiceData de_voice;
340 de_voice.lang = "de";
341 voices.push_back(de_voice);
342 EXPECT_EQ(1, controller()->GetMatchingVoice(utterance.get(), voices));
343 }
344
345 {
346 // Check precedence of various matching criteria.
347 std::vector<VoiceData> voices;
348 VoiceData voice0;
349 voices.push_back(voice0);
350 VoiceData voice1;
351 voice1.events.insert(TTS_EVENT_WORD);
352 voices.push_back(voice1);
353 VoiceData voice2;
354 voice2.lang = "de-DE";
355 voices.push_back(voice2);
356 VoiceData voice3;
357 voice3.lang = "fr-CA";
358 voices.push_back(voice3);
359 VoiceData voice4;
360 voice4.name = "Voice4";
361 voices.push_back(voice4);
362 VoiceData voice5;
363 voice5.engine_id = "id5";
364 voices.push_back(voice5);
365 VoiceData voice6;
366 voice6.engine_id = "id7";
367 voice6.name = "Voice6";
368 voice6.lang = "es-es";
369 voices.push_back(voice6);
370 VoiceData voice7;
371 voice7.engine_id = "id7";
372 voice7.name = "Voice7";
373 voice7.lang = "es-mx";
374 voices.push_back(voice7);
375 VoiceData voice8;
376 voice8.engine_id = "";
377 voice8.name = "Android";
378 voice8.lang = "";
379 voice8.native = true;
380 voices.push_back(voice8);
381
382 std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create(nullptr));
383 EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices));
384
385 std::set<TtsEventType> types;
386 types.insert(TTS_EVENT_WORD);
387 utterance->SetRequiredEventTypes(types);
388 EXPECT_EQ(1, controller()->GetMatchingVoice(utterance.get(), voices));
389
390 utterance->SetLang("de-DE");
391 EXPECT_EQ(2, controller()->GetMatchingVoice(utterance.get(), voices));
392
393 utterance->SetLang("fr-FR");
394 EXPECT_EQ(3, controller()->GetMatchingVoice(utterance.get(), voices));
395
396 utterance->SetVoiceName("Voice4");
397 EXPECT_EQ(4, controller()->GetMatchingVoice(utterance.get(), voices));
398
399 utterance->SetVoiceName("");
400 utterance->SetEngineId("id5");
401 EXPECT_EQ(5, controller()->GetMatchingVoice(utterance.get(), voices));
402
403 #if defined(OS_CHROMEOS)
404 TtsControllerDelegate::PreferredVoiceIds preferred_voice_ids;
405 preferred_voice_ids.locale_voice_id.emplace("Voice7", "id7");
406 preferred_voice_ids.any_locale_voice_id.emplace("Android", "");
407 delegate()->SetPreferredVoiceIds(preferred_voice_ids);
408
409 // Voice6 is matched when the utterance locale exactly matches its locale.
410 utterance->SetEngineId("");
411 utterance->SetLang("es-es");
412 EXPECT_EQ(6, controller()->GetMatchingVoice(utterance.get(), voices));
413
414 // The 7th voice is the default for "es", even though the utterance is
415 // "es-ar". |voice6| is not matched because it is not the default.
416 utterance->SetEngineId("");
417 utterance->SetLang("es-ar");
418 EXPECT_EQ(7, controller()->GetMatchingVoice(utterance.get(), voices));
419
420 // The 8th voice is like the built-in "Android" voice, it has no lang
421 // and no extension ID. Make sure it can still be matched.
422 preferred_voice_ids.locale_voice_id.reset();
423 delegate()->SetPreferredVoiceIds(preferred_voice_ids);
424 utterance->SetVoiceName("Android");
425 utterance->SetEngineId("");
426 utterance->SetLang("");
427 EXPECT_EQ(8, controller()->GetMatchingVoice(utterance.get(), voices));
428
429 delegate()->SetPreferredVoiceIds({});
430 #endif
431 }
432
433 {
434 // Check voices against system language.
435 std::vector<VoiceData> voices;
436 VoiceData voice0;
437 voice0.engine_id = "id0";
438 voice0.name = "voice0";
439 voice0.lang = "en-GB";
440 voices.push_back(voice0);
441 VoiceData voice1;
442 voice1.engine_id = "id1";
443 voice1.name = "voice1";
444 voice1.lang = "en-US";
445 voices.push_back(voice1);
446 std::unique_ptr<TtsUtterance> utterance(TtsUtterance::Create(nullptr));
447
448 // voice1 is matched against the exact default system language.
449 TestContentBrowserClient::GetInstance()->set_application_locale("en-US");
450 utterance->SetLang("");
451 EXPECT_EQ(1, controller()->GetMatchingVoice(utterance.get(), voices));
452
453 #if defined(OS_CHROMEOS)
454 // voice0 is matched against the system language which has no region piece.
455 TestContentBrowserClient::GetInstance()->set_application_locale("en");
456 EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices));
457
458 TtsControllerDelegate::PreferredVoiceIds preferred_voice_ids2;
459 preferred_voice_ids2.locale_voice_id.emplace("voice0", "id0");
460 delegate()->SetPreferredVoiceIds(preferred_voice_ids2);
461 // voice0 is matched against the pref over the system language.
462 TestContentBrowserClient::GetInstance()->set_application_locale("en-US");
463 EXPECT_EQ(0, controller()->GetMatchingVoice(utterance.get(), voices));
464 #endif
465 }
466 }
467
TEST_F(TtsControllerTest,StopsWhenWebContentsDestroyed)468 TEST_F(TtsControllerTest, StopsWhenWebContentsDestroyed) {
469 std::unique_ptr<WebContents> web_contents = CreateWebContents();
470 std::unique_ptr<TtsUtteranceImpl> utterance =
471 CreateUtteranceImpl(web_contents.get());
472
473 controller()->SpeakOrEnqueue(std::move(utterance));
474 EXPECT_TRUE(controller()->IsSpeaking());
475 EXPECT_TRUE(TtsControllerCurrentUtterance());
476
477 web_contents.reset();
478 // Destroying the WebContents should reset
479 // |TtsController::current_utterance_|.
480 EXPECT_FALSE(TtsControllerCurrentUtterance());
481 }
482
TEST_F(TtsControllerTest,StartsQueuedUtteranceWhenWebContentsDestroyed)483 TEST_F(TtsControllerTest, StartsQueuedUtteranceWhenWebContentsDestroyed) {
484 std::unique_ptr<WebContents> web_contents1 = CreateWebContents();
485 std::unique_ptr<WebContents> web_contents2 = CreateWebContents();
486 std::unique_ptr<TtsUtteranceImpl> utterance1 =
487 CreateUtteranceImpl(web_contents1.get());
488 void* raw_utterance1 = utterance1.get();
489 std::unique_ptr<TtsUtteranceImpl> utterance2 =
490 CreateUtteranceImpl(web_contents2.get());
491 utterance2->SetCanEnqueue(true);
492 void* raw_utterance2 = utterance2.get();
493
494 controller()->SpeakOrEnqueue(std::move(utterance1));
495 EXPECT_TRUE(controller()->IsSpeaking());
496 EXPECT_TRUE(TtsControllerCurrentUtterance());
497 controller()->SpeakOrEnqueue(std::move(utterance2));
498 EXPECT_EQ(raw_utterance1, TtsControllerCurrentUtterance());
499
500 web_contents1.reset();
501 // Destroying |web_contents1| should delete |utterance1| and start
502 // |utterance2|.
503 EXPECT_TRUE(TtsControllerCurrentUtterance());
504 EXPECT_EQ(raw_utterance2, TtsControllerCurrentUtterance());
505 }
506
TEST_F(TtsControllerTest,StartsQueuedUtteranceWhenWebContentsDestroyed2)507 TEST_F(TtsControllerTest, StartsQueuedUtteranceWhenWebContentsDestroyed2) {
508 std::unique_ptr<WebContents> web_contents1 = CreateWebContents();
509 std::unique_ptr<WebContents> web_contents2 = CreateWebContents();
510 std::unique_ptr<TtsUtteranceImpl> utterance1 =
511 CreateUtteranceImpl(web_contents1.get());
512 void* raw_utterance1 = utterance1.get();
513 std::unique_ptr<TtsUtteranceImpl> utterance2 =
514 CreateUtteranceImpl(web_contents1.get());
515 std::unique_ptr<TtsUtteranceImpl> utterance3 =
516 CreateUtteranceImpl(web_contents2.get());
517 void* raw_utterance3 = utterance3.get();
518 utterance2->SetCanEnqueue(true);
519 utterance3->SetCanEnqueue(true);
520
521 controller()->SpeakOrEnqueue(std::move(utterance1));
522 controller()->SpeakOrEnqueue(std::move(utterance2));
523 controller()->SpeakOrEnqueue(std::move(utterance3));
524 EXPECT_TRUE(controller()->IsSpeaking());
525 EXPECT_EQ(raw_utterance1, TtsControllerCurrentUtterance());
526
527 web_contents1.reset();
528 // Deleting |web_contents1| should delete |utterance1| and |utterance2| as
529 // they are both from |web_contents1|. |raw_utterance3| should be made the
530 // current as it's from a different WebContents.
531 EXPECT_EQ(raw_utterance3, TtsControllerCurrentUtterance());
532 EXPECT_TRUE(IsUtteranceListEmpty());
533
534 web_contents2.reset();
535 // Deleting |web_contents2| should delete |utterance3| as it's from a
536 // different WebContents.
537 EXPECT_EQ(nullptr, TtsControllerCurrentUtterance());
538 }
539
TEST_F(TtsControllerTest,StartsUtteranceWhenWebContentsHidden)540 TEST_F(TtsControllerTest, StartsUtteranceWhenWebContentsHidden) {
541 std::unique_ptr<TestWebContents> web_contents = CreateWebContents();
542 web_contents->SetVisibilityAndNotifyObservers(Visibility::HIDDEN);
543 std::unique_ptr<TtsUtteranceImpl> utterance =
544 CreateUtteranceImpl(web_contents.get());
545 controller()->SpeakOrEnqueue(std::move(utterance));
546 EXPECT_TRUE(controller()->IsSpeaking());
547 }
548
TEST_F(TtsControllerTest,DoesNotStartUtteranceWhenWebContentsHiddenAndStopSpeakingWhenHiddenSet)549 TEST_F(TtsControllerTest,
550 DoesNotStartUtteranceWhenWebContentsHiddenAndStopSpeakingWhenHiddenSet) {
551 std::unique_ptr<TestWebContents> web_contents = CreateWebContents();
552 web_contents->SetVisibilityAndNotifyObservers(Visibility::HIDDEN);
553 std::unique_ptr<TtsUtteranceImpl> utterance =
554 CreateUtteranceImpl(web_contents.get());
555 controller()->SetStopSpeakingWhenHidden(true);
556 controller()->SpeakOrEnqueue(std::move(utterance));
557 EXPECT_EQ(nullptr, TtsControllerCurrentUtterance());
558 EXPECT_TRUE(IsUtteranceListEmpty());
559 }
560
TEST_F(TtsControllerTest,SkipsQueuedUtteranceFromHiddenWebContents)561 TEST_F(TtsControllerTest, SkipsQueuedUtteranceFromHiddenWebContents) {
562 controller()->SetStopSpeakingWhenHidden(true);
563 std::unique_ptr<WebContents> web_contents1 = CreateWebContents();
564 std::unique_ptr<TestWebContents> web_contents2 = CreateWebContents();
565 std::unique_ptr<TtsUtteranceImpl> utterance1 =
566 CreateUtteranceImpl(web_contents1.get());
567 const int utterance1_id = utterance1->GetId();
568 std::unique_ptr<TtsUtteranceImpl> utterance2 =
569 CreateUtteranceImpl(web_contents2.get());
570 utterance2->SetCanEnqueue(true);
571
572 controller()->SpeakOrEnqueue(std::move(utterance1));
573 EXPECT_TRUE(TtsControllerCurrentUtterance());
574 EXPECT_TRUE(IsUtteranceListEmpty());
575
576 // Speak |utterance2|, which should get queued.
577 controller()->SpeakOrEnqueue(std::move(utterance2));
578 EXPECT_FALSE(IsUtteranceListEmpty());
579
580 // Make the second WebContents hidden, this shouldn't change anything in
581 // TtsController.
582 web_contents2->SetVisibilityAndNotifyObservers(Visibility::HIDDEN);
583 EXPECT_FALSE(IsUtteranceListEmpty());
584
585 // Finish |utterance1|, which should skip |utterance2| because |web_contents2|
586 // is hidden.
587 controller()->OnTtsEvent(utterance1_id, TTS_EVENT_END, 0, 0, {});
588 EXPECT_EQ(nullptr, TtsControllerCurrentUtterance());
589 EXPECT_TRUE(IsUtteranceListEmpty());
590 }
591
TEST_F(TtsControllerTest,PauseResumeNoUtterance)592 TEST_F(TtsControllerTest, PauseResumeNoUtterance) {
593 // Pause should not call the platform API when there is no utterance.
594 controller()->Pause();
595 controller()->Resume();
596 EXPECT_EQ(0, platform_impl()->pause_called());
597 EXPECT_EQ(0, platform_impl()->resume_called());
598 }
599
TEST_F(TtsControllerTest,SpeakPauseResume)600 TEST_F(TtsControllerTest, SpeakPauseResume) {
601 std::unique_ptr<WebContents> web_contents = CreateWebContents();
602 std::unique_ptr<TtsUtteranceImpl> utterance =
603 CreateUtteranceImpl(web_contents.get());
604 utterance->SetCanEnqueue(true);
605
606 // Start speaking an utterance.
607 controller()->SpeakOrEnqueue(std::move(utterance));
608 platform_impl()->StartSpeaking(true);
609
610 // Pause the currently playing utterance should call the platform API pause.
611 controller()->Pause();
612 EXPECT_TRUE(controller()->IsPausedForTesting());
613 EXPECT_EQ(1, platform_impl()->pause_called());
614
615 // Double pause should not call again the platform API pause.
616 controller()->Pause();
617 EXPECT_EQ(1, platform_impl()->pause_called());
618
619 EXPECT_TRUE(IsUtteranceListEmpty());
620 EXPECT_TRUE(TtsControllerCurrentUtterance());
621
622 // Resuming the playing utterance should call the platform API resume.
623 controller()->Resume();
624 EXPECT_FALSE(controller()->IsPausedForTesting());
625 EXPECT_EQ(1, platform_impl()->resume_called());
626
627 // Double resume should not call again the platform API resume.
628 controller()->Resume();
629 EXPECT_EQ(1, platform_impl()->resume_called());
630 EXPECT_TRUE(controller()->IsSpeaking());
631
632 // Complete the playing utterance.
633 platform_impl()->FinishSpeaking();
634
635 EXPECT_TRUE(IsUtteranceListEmpty());
636 EXPECT_FALSE(TtsControllerCurrentUtterance());
637 EXPECT_FALSE(controller()->IsSpeaking());
638 }
639
TEST_F(TtsControllerTest,SpeakWhenPaused)640 TEST_F(TtsControllerTest, SpeakWhenPaused) {
641 std::unique_ptr<WebContents> web_contents = CreateWebContents();
642 std::unique_ptr<TtsUtteranceImpl> utterance =
643 CreateUtteranceImpl(web_contents.get());
644 utterance->SetCanEnqueue(true);
645
646 // Pause the controller.
647 controller()->Pause();
648 EXPECT_TRUE(controller()->IsPausedForTesting());
649
650 // Speak an utterance while controller is paused, the utterance should be
651 // queued.
652 controller()->SpeakOrEnqueue(std::move(utterance));
653 EXPECT_FALSE(IsUtteranceListEmpty());
654 EXPECT_FALSE(TtsControllerCurrentUtterance());
655
656 // Resume speaking, the utterance should start playing.
657 controller()->Resume();
658 EXPECT_FALSE(controller()->IsPausedForTesting());
659 EXPECT_TRUE(IsUtteranceListEmpty());
660 EXPECT_TRUE(TtsControllerCurrentUtterance());
661
662 // Simulate platform starting to play the utterance.
663 platform_impl()->StartSpeaking(true);
664 EXPECT_TRUE(IsUtteranceListEmpty());
665 EXPECT_TRUE(TtsControllerCurrentUtterance());
666
667 EXPECT_TRUE(controller()->IsSpeaking());
668
669 // Complete the playing utterance.
670 platform_impl()->FinishSpeaking();
671 EXPECT_TRUE(IsUtteranceListEmpty());
672 EXPECT_FALSE(TtsControllerCurrentUtterance());
673 EXPECT_FALSE(controller()->IsSpeaking());
674 }
675
TEST_F(TtsControllerTest,SpeakWhenPausedAndCannotEnqueueUtterance)676 TEST_F(TtsControllerTest, SpeakWhenPausedAndCannotEnqueueUtterance) {
677 std::unique_ptr<WebContents> web_contents = CreateWebContents();
678 std::unique_ptr<TtsUtteranceImpl> utterance1 =
679 CreateUtteranceImpl(web_contents.get());
680 utterance1->SetCanEnqueue(false);
681
682 // Pause the controller.
683 controller()->Pause();
684 EXPECT_TRUE(controller()->IsPausedForTesting());
685
686 // Speak an utterance while controller is paused. The utterance cannot be
687 // queued and should be dropped.
688 controller()->SpeakOrEnqueue(std::move(utterance1));
689 EXPECT_TRUE(IsUtteranceListEmpty());
690 EXPECT_FALSE(TtsControllerCurrentUtterance());
691
692 // Speak an utterance that can be queued. The controller should stay paused
693 // and the second utterance must be queued.
694 std::unique_ptr<TtsUtteranceImpl> utterance2 =
695 CreateUtteranceImpl(web_contents.get());
696 utterance2->SetCanEnqueue(true);
697
698 controller()->SpeakOrEnqueue(std::move(utterance2));
699 EXPECT_TRUE(controller()->IsPausedForTesting());
700 EXPECT_FALSE(IsUtteranceListEmpty());
701 EXPECT_FALSE(TtsControllerCurrentUtterance());
702
703 // Speak an utterance that cannot be queued should clear the queue.
704 std::unique_ptr<TtsUtteranceImpl> utterance3 =
705 CreateUtteranceImpl(web_contents.get());
706 utterance3->SetCanEnqueue(false);
707
708 controller()->SpeakOrEnqueue(std::move(utterance3));
709 EXPECT_TRUE(controller()->IsPausedForTesting());
710 EXPECT_TRUE(IsUtteranceListEmpty());
711 EXPECT_FALSE(TtsControllerCurrentUtterance());
712
713 // Resume the controller.
714 controller()->Resume();
715 EXPECT_FALSE(controller()->IsPausedForTesting());
716 }
717
TEST_F(TtsControllerTest,StopMustResumeController)718 TEST_F(TtsControllerTest, StopMustResumeController) {
719 std::unique_ptr<WebContents> web_contents = CreateWebContents();
720 std::unique_ptr<TtsUtteranceImpl> utterance =
721 CreateUtteranceImpl(web_contents.get());
722 utterance->SetCanEnqueue(true);
723
724 // Speak an utterance while controller is paused. The utterance is queued.
725 controller()->SpeakOrEnqueue(std::move(utterance));
726 platform_impl()->StartSpeaking(true);
727 EXPECT_TRUE(IsUtteranceListEmpty());
728 EXPECT_TRUE(TtsControllerCurrentUtterance());
729
730 platform_impl()->SetError("dummy");
731
732 // Stop must resume the controller and clear the current utterance.
733 controller()->Stop();
734 platform_impl()->FinishSpeaking();
735
736 EXPECT_EQ(2, platform_impl()->stop_speaking_called());
737 EXPECT_TRUE(IsUtteranceListEmpty());
738 EXPECT_FALSE(TtsControllerCurrentUtterance());
739 EXPECT_TRUE(platform_impl()->GetError().empty());
740 EXPECT_FALSE(controller()->IsSpeaking());
741 }
742
TEST_F(TtsControllerTest,PauseAndStopMustResumeController)743 TEST_F(TtsControllerTest, PauseAndStopMustResumeController) {
744 std::unique_ptr<WebContents> web_contents = CreateWebContents();
745 std::unique_ptr<TtsUtteranceImpl> utterance =
746 CreateUtteranceImpl(web_contents.get());
747 utterance->SetCanEnqueue(true);
748
749 // Pause the controller.
750 controller()->Pause();
751 EXPECT_TRUE(controller()->IsPausedForTesting());
752
753 // Speak an utterance while controller is paused. The utterance is queued.
754 controller()->SpeakOrEnqueue(std::move(utterance));
755 EXPECT_FALSE(IsUtteranceListEmpty());
756 EXPECT_FALSE(TtsControllerCurrentUtterance());
757
758 platform_impl()->SetError("dummy");
759
760 // Stop must resume the controller and clear the queue.
761 controller()->Stop();
762 EXPECT_FALSE(controller()->IsPausedForTesting());
763 EXPECT_EQ(1, platform_impl()->stop_speaking_called());
764 EXPECT_TRUE(IsUtteranceListEmpty());
765 EXPECT_FALSE(TtsControllerCurrentUtterance());
766 EXPECT_TRUE(platform_impl()->GetError().empty());
767 }
768
TEST_F(TtsControllerTest,PlatformNotSupported)769 TEST_F(TtsControllerTest, PlatformNotSupported) {
770 std::unique_ptr<WebContents> web_contents = CreateWebContents();
771 std::unique_ptr<TtsUtteranceImpl> utterance =
772 CreateUtteranceImpl(web_contents.get());
773
774 // The utterance is dropped when the platform is not available.
775 platform_impl()->SetPlatformImplSupported(false);
776 controller()->SpeakOrEnqueue(std::move(utterance));
777 EXPECT_FALSE(TtsControllerCurrentUtterance());
778 EXPECT_TRUE(IsUtteranceListEmpty());
779
780 // No methods are called on the platform when not available.
781 controller()->Pause();
782 controller()->Resume();
783 controller()->Stop();
784
785 EXPECT_EQ(0, platform_impl()->pause_called());
786 EXPECT_EQ(0, platform_impl()->resume_called());
787 EXPECT_EQ(0, platform_impl()->stop_speaking_called());
788 }
789
TEST_F(TtsControllerTest,SpeakWhenLoading)790 TEST_F(TtsControllerTest, SpeakWhenLoading) {
791 platform_impl()->SetPlatformImplInitialized(false);
792
793 std::unique_ptr<WebContents> web_contents = CreateWebContents();
794 std::unique_ptr<TtsUtteranceImpl> utterance =
795 CreateUtteranceImpl(web_contents.get());
796 utterance->SetCanEnqueue(true);
797
798 // Speak an utterance while platform is loading, the utterance should be
799 // queued.
800 controller()->SpeakOrEnqueue(std::move(utterance));
801 EXPECT_FALSE(IsUtteranceListEmpty());
802 EXPECT_FALSE(TtsControllerCurrentUtterance());
803
804 // Simulate the completion of the initialisation.
805 platform_impl()->SetPlatformImplInitialized(true);
806 controller()->VoicesChanged();
807
808 platform_impl()->StartSpeaking(true);
809 EXPECT_TRUE(IsUtteranceListEmpty());
810 EXPECT_TRUE(TtsControllerCurrentUtterance());
811 EXPECT_TRUE(controller()->IsSpeaking());
812
813 // Complete the playing utterance.
814 platform_impl()->FinishSpeaking();
815 EXPECT_TRUE(IsUtteranceListEmpty());
816 EXPECT_FALSE(TtsControllerCurrentUtterance());
817 EXPECT_FALSE(controller()->IsSpeaking());
818 }
819
820 } // namespace content
821