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